1 /* Portions Copyright 2006 Stephen P. Potter */ 2 3 /* 4 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 5 * Use is subject to license terms. 6 */ 7 8 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 9 /* All Rights Reserved */ 10 11 /* 12 * Copyright (c) 1980 Regents of the University of California. 13 * All rights reserved. The Berkeley software License Agreement 14 * specifies the terms and conditions for redistribution. 15 */ 16 17 /* 18 * ls 19 * 20 * 4.2bsd version for symbolic links, variable length 21 * directory entries, block size in the inode, etc. 22 */ 23 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <unistd.h> 27 #include <string.h> 28 #include <stddef.h> 29 #include <dirent.h> 30 #include <ctype.h> 31 #include <time.h> 32 #include <limits.h> 33 #include <locale.h> 34 #include <errno.h> 35 #include <sys/types.h> 36 #include <sys/param.h> 37 #include <sys/stat.h> 38 #include <sys/termios.h> 39 #include <sys/mkdev.h> 40 #include <sys/acl.h> 41 42 #define dbtokb(nb) ((nb) / (1024 / DEV_BSIZE)) 43 44 struct afile { 45 char ftype; /* file type, e.g. 'd', 'c', 'f' */ 46 ino_t fnum; /* inode number of file */ 47 short fflags; /* mode&~S_IFMT, perhaps ISARG */ 48 nlink_t fnl; /* number of links */ 49 uid_t fuid; /* owner id */ 50 gid_t fgid; /* group id */ 51 off_t fsize; /* file size */ 52 blkcnt_t fblks; /* number of blocks used */ 53 time_t fmtime; /* time (modify or access or create) */ 54 char *fname; /* file name */ 55 char *flinkto; /* symbolic link value */ 56 char acl; /* acl access flag */ 57 }; 58 59 #define ISARG 0x8000 /* extra ``mode'' */ 60 61 static struct subdirs { 62 char *sd_name; 63 struct subdirs *sd_next; 64 } *subdirs; 65 66 static int aflg, dflg, gflg, lflg, sflg, tflg, uflg, iflg, fflg, cflg; 67 static int rflg = 1; 68 static int qflg, Aflg, Cflg, Fflg, Lflg, Rflg; 69 70 static int usetabs; 71 72 static time_t now, sixmonthsago, onehourfromnow; 73 74 static char *dotp = "."; 75 76 static struct winsize win; 77 static int twidth; 78 79 static struct afile *gstat(struct afile *, char *, int, off_t *); 80 static int fcmp(const void *, const void *); 81 static char *cat(char *, char *); 82 static char *savestr(char *); 83 static char *fmtentry(struct afile *); 84 static char *getname(), *getgroup(); 85 static void formatd(char *, int); 86 static void formatf(struct afile *, struct afile *); 87 static off_t getdir(char *, struct afile **, struct afile **); 88 89 int 90 main(int argc, char **argv) 91 { 92 int i; 93 struct afile *fp0, *fplast; 94 register struct afile *fp; 95 struct termios trbuf; 96 97 argc--, argv++; 98 if (getuid() == 0) 99 Aflg++; 100 (void) time(&now); 101 sixmonthsago = now - 6L*30L*24L*60L*60L; 102 onehourfromnow = now + 60L*60L; 103 now += 60; 104 twidth = 80; 105 if (isatty(1)) { 106 qflg = Cflg = 1; 107 (void) ioctl(1, TCGETS, &trbuf); 108 if (ioctl(1, TIOCGWINSZ, &win) != -1) 109 twidth = (win.ws_col == 0 ? 80 : win.ws_col); 110 if ((trbuf.c_oflag & TABDLY) != TAB3) 111 usetabs = 1; 112 } else 113 usetabs = 1; 114 115 (void) setlocale(LC_ALL, ""); /* set local environment */ 116 117 while (argc > 0 && **argv == '-') { 118 (*argv)++; 119 while (**argv) { 120 switch (*(*argv)++) { 121 case 'C': 122 Cflg = 1; break; 123 case 'q': 124 qflg = 1; break; 125 case '1': 126 Cflg = 0; break; 127 case 'a': 128 aflg++; break; 129 case 'A': 130 Aflg++; break; 131 case 'c': 132 cflg++; break; 133 case 's': 134 sflg++; break; 135 case 'd': 136 dflg++; break; 137 case 'g': 138 gflg++; break; 139 case 'l': 140 lflg++; break; 141 case 'r': 142 rflg = -1; break; 143 case 't': 144 tflg++; break; 145 case 'u': 146 uflg++; break; 147 case 'i': 148 iflg++; break; 149 case 'f': 150 fflg++; break; 151 case 'L': 152 Lflg++; break; 153 case 'F': 154 Fflg++; break; 155 case 'R': 156 Rflg++; break; 157 } 158 } 159 argc--, argv++; 160 } 161 if (fflg) { 162 aflg++; lflg = 0; sflg = 0; tflg = 0; 163 } 164 if (lflg) 165 Cflg = 0; 166 if (argc == 0) { 167 argc++; 168 argv = &dotp; 169 } 170 fp = (struct afile *)calloc(argc, sizeof (struct afile)); 171 if (fp == 0) { 172 (void) fprintf(stderr, "ls: out of memory\n"); 173 exit(1); 174 } 175 fp0 = fp; 176 for (i = 0; i < argc; i++) { 177 if (gstat(fp, *argv, 1, (off_t *)0)) { 178 fp->fname = *argv; 179 fp->fflags |= ISARG; 180 fp++; 181 } 182 argv++; 183 } 184 fplast = fp; 185 qsort(fp0, fplast - fp0, sizeof (struct afile), fcmp); 186 if (dflg) { 187 formatf(fp0, fplast); 188 exit(0); 189 } 190 if (fflg) 191 fp = fp0; 192 else { 193 for (fp = fp0; fp < fplast && fp->ftype != 'd'; fp++) 194 continue; 195 formatf(fp0, fp); 196 } 197 if (fp < fplast) { 198 if (fp > fp0) 199 (void) printf("\n"); 200 for (;;) { 201 formatd(fp->fname, argc > 1); 202 while (subdirs) { 203 struct subdirs *t; 204 205 t = subdirs; subdirs = t->sd_next; 206 (void) printf("\n"); 207 formatd(t->sd_name, 1); 208 free(t->sd_name); 209 free(t); 210 } 211 if (++fp == fplast) 212 break; 213 (void) printf("\n"); 214 } 215 } 216 return (0); 217 } 218 219 static void 220 formatd(char *name, int title) 221 { 222 register struct afile *fp; 223 register struct subdirs *dp; 224 struct afile *dfp0, *dfplast; 225 off_t nkb; 226 227 nkb = getdir(name, &dfp0, &dfplast); 228 if (dfp0 == 0) 229 return; 230 if (fflg == 0) 231 qsort(dfp0, dfplast - dfp0, sizeof (struct afile), fcmp); 232 if (title) 233 (void) printf("%s:\n", name); 234 if (lflg || sflg) 235 (void) printf("total %lld\n", nkb); 236 formatf(dfp0, dfplast); 237 if (Rflg) 238 for (fp = dfplast - 1; fp >= dfp0; fp--) { 239 if (fp->ftype != 'd' || 240 strcmp(fp->fname, ".") == 0 || 241 strcmp(fp->fname, "..") == 0) 242 continue; 243 dp = (struct subdirs *)malloc(sizeof (struct subdirs)); 244 dp->sd_name = savestr(cat(name, fp->fname)); 245 dp->sd_next = subdirs; subdirs = dp; 246 } 247 for (fp = dfp0; fp < dfplast; fp++) { 248 if ((fp->fflags&ISARG) == 0 && fp->fname) 249 free(fp->fname); 250 if (fp->flinkto) 251 free(fp->flinkto); 252 } 253 free(dfp0); 254 } 255 256 static off_t 257 getdir(char *dir, struct afile **pfp0, struct afile **pfplast) 258 { 259 register struct afile *fp; 260 DIR *dirp; 261 register struct dirent *dp; 262 off_t nb; 263 size_t nent = 20; 264 265 /* 266 * This code (opendir, readdir, and the "for" loop) is arranged in 267 * this strange manner to handle the case where UNIX lets root open 268 * any directory for reading, but NFS does not let root read the 269 * openned directory. 270 */ 271 *pfp0 = *pfplast = NULL; 272 if ((dirp = opendir(dir)) == NULL) { 273 (void) printf("%s unreadable\n", dir); /* not stderr! */ 274 return (0); 275 } 276 errno = 0; 277 if (((dp = readdir(dirp)) == NULL) && (errno != 0)) { 278 /* root reading across NFS can get to this error case */ 279 (void) printf("%s unreadable\n", dir); /* not stderr! */ 280 (void) closedir(dirp); 281 return (0); 282 } 283 fp = *pfp0 = (struct afile *)calloc(nent, sizeof (struct afile)); 284 *pfplast = *pfp0 + nent; 285 for (nb = 0; dp != NULL; dp = readdir(dirp)) { 286 if (dp->d_ino == 0) 287 continue; 288 if (aflg == 0 && dp->d_name[0] == '.' && 289 (Aflg == 0 || dp->d_name[1] == 0 || 290 dp->d_name[1] == '.' && dp->d_name[2] == 0)) 291 continue; 292 if (gstat(fp, cat(dir, dp->d_name), Fflg+Rflg, &nb) == 0) 293 continue; 294 fp->fnum = dp->d_ino; 295 fp->fname = savestr(dp->d_name); 296 fp++; 297 if (fp == *pfplast) { 298 *pfp0 = (struct afile *)realloc((char *)*pfp0, 299 2 * nent * sizeof (struct afile)); 300 if (*pfp0 == 0) { 301 (void) fprintf(stderr, "ls: out of memory\n"); 302 exit(1); 303 } 304 fp = *pfp0 + nent; 305 *pfplast = fp + nent; 306 nent *= 2; 307 } 308 } 309 (void) closedir(dirp); 310 *pfplast = fp; 311 return (dbtokb(nb)); 312 } 313 314 315 static struct afile * 316 gstat(struct afile *fp, char *file, int statarg, off_t *pnb) 317 { 318 static struct afile azerofile; 319 int (*statf)() = Lflg ? stat : lstat; 320 int cc; 321 char buf[PATH_MAX]; 322 int aclcnt; 323 aclent_t *aclp; 324 aclent_t *tp; 325 o_mode_t groupperm, mask; 326 int grouppermfound, maskfound; 327 328 *fp = azerofile; 329 fp->fflags = 0; 330 fp->fnum = 0; 331 fp->ftype = '-'; 332 if (statarg || sflg || lflg || tflg) { 333 struct stat stb, stb1; 334 335 if ((*statf)(file, &stb) < 0) { 336 if (statf == lstat || lstat(file, &stb) < 0) { 337 if (errno == ENOENT) 338 (void) fprintf(stderr, 339 "%s not found\n", file); 340 else { 341 (void) fprintf(stderr, "ls: "); 342 perror(file); 343 } 344 return (0); 345 } 346 } 347 fp->fblks = stb.st_blocks; 348 fp->fsize = stb.st_size; 349 switch (stb.st_mode & S_IFMT) { 350 case S_IFDIR: 351 fp->ftype = 'd'; break; 352 case S_IFDOOR: 353 fp->ftype = 'D'; break; 354 case S_IFBLK: 355 fp->ftype = 'b'; fp->fsize = (off_t)stb.st_rdev; break; 356 case S_IFCHR: 357 fp->ftype = 'c'; fp->fsize = (off_t)stb.st_rdev; break; 358 case S_IFSOCK: 359 fp->ftype = 's'; fp->fsize = 0LL; break; 360 case S_IFIFO: 361 fp->ftype = 'p'; fp->fsize = 0LL; break; 362 case S_IFLNK: 363 fp->ftype = 'l'; 364 if (lflg) { 365 cc = readlink(file, buf, BUFSIZ); 366 if (cc >= 0) { 367 /* 368 * here we follow the symbolic 369 * link to generate the proper 370 * Fflg marker for the object, 371 * eg, /bin -> /pub/bin/ 372 */ 373 buf[cc] = 0; 374 if (Fflg && !stat(file, &stb1)) 375 switch (stb1.st_mode & S_IFMT) { 376 case S_IFDIR: 377 buf[cc++] = '/'; 378 break; 379 case S_IFDOOR: 380 buf[cc++] = '>'; 381 break; 382 case S_IFIFO: 383 buf[cc++] = '|'; 384 break; 385 case S_IFSOCK: 386 buf[cc++] = '='; 387 break; 388 default: 389 if ((stb1.st_mode & 390 ~S_IFMT) & 0111) 391 buf[cc++] = '*'; 392 break; 393 } 394 buf[cc] = 0; 395 fp->flinkto = savestr(buf); 396 } 397 break; 398 } 399 /* 400 * this is a hack from UCB to avoid having 401 * ls /bin behave differently from ls /bin/ 402 * when /bin is a symbolic link. We hack the 403 * hack to have that happen, but only for 404 * explicit arguments, by inspecting pnb. 405 */ 406 if (pnb != (off_t *)0 || stat(file, &stb1) < 0) 407 break; 408 if ((stb1.st_mode & S_IFMT) == S_IFDIR) { 409 stb = stb1; 410 fp->ftype = 'd'; 411 fp->fsize = stb.st_size; 412 fp->fblks = stb.st_blocks; 413 } 414 break; 415 } 416 fp->fnum = stb.st_ino; 417 fp->fflags = stb.st_mode & ~S_IFMT; 418 fp->fnl = stb.st_nlink; 419 fp->fuid = stb.st_uid; 420 fp->fgid = stb.st_gid; 421 422 /* ACL: check acl entries count */ 423 if ((aclcnt = acl(file, GETACLCNT, 0, NULL)) > 424 MIN_ACL_ENTRIES) { 425 426 /* this file has a non-trivial acl */ 427 428 fp->acl = '+'; 429 430 /* 431 * For files with non-trivial acls, the 432 * effective group permissions are the 433 * intersection of the GROUP_OBJ value and 434 * the CLASS_OBJ (acl mask) value. Determine 435 * both the GROUP_OBJ and CLASS_OBJ for this 436 * file and insert the logical AND of those 437 * two values in the group permissions field 438 * of the lflags value for this file. 439 */ 440 441 if ((aclp = (aclent_t *)malloc( 442 (sizeof (aclent_t)) * aclcnt)) == NULL) { 443 perror("ls"); 444 exit(2); 445 } 446 447 if (acl(file, GETACL, aclcnt, aclp) < 0) { 448 free(aclp); 449 (void) fprintf(stderr, "ls: "); 450 perror(file); 451 return (0); 452 } 453 454 /* 455 * Until found in acl list, assume maximum 456 * permissions for both group and mask. (Just 457 * in case the acl lacks either value for 458 * some reason.) 459 */ 460 groupperm = 07; 461 mask = 07; 462 grouppermfound = 0; 463 maskfound = 0; 464 for (tp = aclp; aclcnt--; tp++) { 465 if (tp->a_type == GROUP_OBJ) { 466 groupperm = tp->a_perm; 467 grouppermfound = 1; 468 continue; 469 } 470 if (tp->a_type == CLASS_OBJ) { 471 mask = tp->a_perm; 472 maskfound = 1; 473 } 474 if (grouppermfound && maskfound) 475 break; 476 } 477 478 free(aclp); 479 480 /* reset all the group bits */ 481 fp->fflags &= ~S_IRWXG; 482 483 /* 484 * Now set them to the logical AND of the 485 * GROUP_OBJ permissions and the acl mask. 486 */ 487 488 fp->fflags |= (groupperm & mask) << 3; 489 } else 490 fp->acl = ' '; 491 492 if (uflg) 493 fp->fmtime = stb.st_atime; 494 else if (cflg) 495 fp->fmtime = stb.st_ctime; 496 else 497 fp->fmtime = stb.st_mtime; 498 if (pnb) 499 *pnb += stb.st_blocks; 500 } 501 return (fp); 502 } 503 504 static void 505 formatf(struct afile *fp0, struct afile *fplast) 506 { 507 register struct afile *fp; 508 int width = 0, w, nentry = fplast - fp0; 509 int i, j, columns, lines; 510 char *cp; 511 512 if (fp0 == fplast) 513 return; 514 if (lflg || Cflg == 0) 515 columns = 1; 516 else { 517 for (fp = fp0; fp < fplast; fp++) { 518 int len = strlen(fmtentry(fp)); 519 520 if (len > width) 521 width = len; 522 } 523 if (usetabs) 524 width = (width + 8) &~ 7; 525 else 526 width += 2; 527 columns = twidth / width; 528 if (columns == 0) 529 columns = 1; 530 } 531 lines = (nentry + columns - 1) / columns; 532 for (i = 0; i < lines; i++) { 533 for (j = 0; j < columns; j++) { 534 fp = fp0 + j * lines + i; 535 cp = fmtentry(fp); 536 (void) printf("%s", cp); 537 if (fp + lines >= fplast) { 538 (void) printf("\n"); 539 break; 540 } 541 w = strlen(cp); 542 while (w < width) 543 if (usetabs) { 544 w = (w + 8) &~ 7; 545 (void) putchar('\t'); 546 } else { 547 w++; 548 (void) putchar(' '); 549 } 550 } 551 } 552 } 553 554 static int 555 fcmp(const void *arg1, const void *arg2) 556 { 557 const struct afile *f1 = arg1; 558 const struct afile *f2 = arg2; 559 560 if (dflg == 0 && fflg == 0) { 561 if ((f1->fflags&ISARG) && f1->ftype == 'd') { 562 if ((f2->fflags&ISARG) == 0 || f2->ftype != 'd') 563 return (1); 564 } else { 565 if ((f2->fflags&ISARG) && f2->ftype == 'd') 566 return (-1); 567 } 568 } 569 if (tflg) { 570 if (f2->fmtime == f1->fmtime) 571 return (0); 572 if (f2->fmtime > f1->fmtime) 573 return (rflg); 574 return (-rflg); 575 } 576 return (rflg * strcmp(f1->fname, f2->fname)); 577 } 578 579 static char * 580 cat(char *dir, char *file) 581 { 582 static char dfile[BUFSIZ]; 583 584 if (strlen(dir)+1+strlen(file)+1 > BUFSIZ) { 585 (void) fprintf(stderr, "ls: filename too long\n"); 586 exit(1); 587 } 588 if (strcmp(dir, "") == 0 || strcmp(dir, ".") == 0) { 589 (void) strcpy(dfile, file); 590 return (dfile); 591 } 592 (void) strcpy(dfile, dir); 593 if (dir[strlen(dir) - 1] != '/' && *file != '/') 594 (void) strcat(dfile, "/"); 595 (void) strcat(dfile, file); 596 return (dfile); 597 } 598 599 static char * 600 savestr(char *str) 601 { 602 char *cp = malloc(strlen(str) + 1); 603 604 if (cp == NULL) { 605 (void) fprintf(stderr, "ls: out of memory\n"); 606 exit(1); 607 } 608 (void) strcpy(cp, str); 609 return (cp); 610 } 611 612 static char *fmtinum(struct afile *); 613 static char *fmtsize(struct afile *); 614 static char *fmtlstuff(struct afile *); 615 static char *fmtmode(char *, int); 616 617 static char * 618 fmtentry(struct afile *fp) 619 { 620 static char fmtres[BUFSIZ]; 621 register char *cp, *dp; 622 623 (void) sprintf(fmtres, "%s%s%s", 624 iflg ? fmtinum(fp) : "", 625 sflg ? fmtsize(fp) : "", 626 lflg ? fmtlstuff(fp) : ""); 627 dp = &fmtres[strlen(fmtres)]; 628 for (cp = fp->fname; *cp; cp++) 629 if (qflg && !isprint((unsigned char)*cp)) 630 *dp++ = '?'; 631 else 632 *dp++ = *cp; 633 /* avoid both "->" and trailing marks */ 634 if (Fflg && ! (lflg && fp->flinkto)) { 635 if (fp->ftype == 'd') 636 *dp++ = '/'; 637 else if (fp->ftype == 'D') 638 *dp++ = '>'; 639 else if (fp->ftype == 'p') 640 *dp++ = '|'; 641 else if (fp->ftype == 'l') 642 *dp++ = '@'; 643 else if (fp->ftype == 's') 644 *dp++ = '='; 645 else if (fp->fflags & 0111) 646 *dp++ = '*'; 647 } 648 if (lflg && fp->flinkto) { 649 (void) strcpy(dp, " -> "); dp += 4; 650 for (cp = fp->flinkto; *cp; cp++) 651 if (qflg && !isprint((unsigned char) *cp)) 652 *dp++ = '?'; 653 else 654 *dp++ = *cp; 655 } 656 *dp++ = 0; 657 return (fmtres); 658 } 659 660 static char * 661 fmtinum(struct afile *p) 662 { 663 static char inumbuf[12]; 664 665 (void) sprintf(inumbuf, "%10llu ", p->fnum); 666 return (inumbuf); 667 } 668 669 static char * 670 fmtsize(struct afile *p) 671 { 672 static char sizebuf[32]; 673 674 (void) sprintf(sizebuf, (off_t)dbtokb(p->fblks) < 10000 ? "%4lld " : \ 675 "%lld ", (off_t)dbtokb(p->fblks)); 676 return (sizebuf); 677 } 678 679 static char * 680 fmtlstuff(struct afile *p) 681 { 682 static char lstuffbuf[256]; 683 char gname[32], uname[32], fsize[32], ftime[32]; 684 register char *lp = lstuffbuf; 685 686 /* type mode uname gname fsize ftime */ 687 /* get uname */ 688 { 689 char *cp = getname(p->fuid); 690 (void) sprintf(uname, "%-8s ", cp); 691 } 692 /* get gname */ 693 if (gflg) { 694 char *cp = getgroup(p->fgid); 695 (void) sprintf(gname, "%-8s ", cp); 696 } 697 /* get fsize */ 698 if (p->ftype == 'b' || p->ftype == 'c') 699 (void) sprintf(fsize, "%3ld,%4ld", 700 major(p->fsize), minor(p->fsize)); 701 else if (p->ftype == 's') 702 (void) sprintf(fsize, "%8d", 0); 703 else 704 (void) sprintf(fsize, p->fsize < 100000000 ? "%8lld" : \ 705 "%lld", p->fsize); 706 /* get ftime */ 707 { 708 char *cp = ctime(&p->fmtime); 709 if ((p->fmtime < sixmonthsago) || (p->fmtime > onehourfromnow)) 710 (void) sprintf(ftime, " %-7.7s %-4.4s ", cp+4, cp+20); 711 else 712 (void) sprintf(ftime, " %-12.12s ", cp+4); 713 } 714 /* splat */ 715 *lp++ = p->ftype; 716 lp = fmtmode(lp, p->fflags); 717 (void) sprintf(lp, "%c%3ld %s%s%s%s", 718 p->acl, p->fnl, uname, gflg ? gname : "", fsize, ftime); 719 return (lstuffbuf); 720 } 721 722 static int m1[] = 723 { 1, S_IREAD>>0, 'r', '-' }; 724 static int m2[] = 725 { 1, S_IWRITE>>0, 'w', '-' }; 726 static int m3[] = 727 { 3, S_ISUID|(S_IEXEC>>0), 's', S_IEXEC>>0, 'x', S_ISUID, 'S', '-' }; 728 static int m4[] = 729 { 1, S_IREAD>>3, 'r', '-' }; 730 static int m5[] = 731 { 1, S_IWRITE>>3, 'w', '-' }; 732 static int m6[] = 733 { 3, S_ISGID|(S_IEXEC>>3), 's', S_IEXEC>>3, 'x', S_ISGID, 'S', '-' }; 734 static int m7[] = 735 { 1, S_IREAD>>6, 'r', '-' }; 736 static int m8[] = 737 { 1, S_IWRITE>>6, 'w', '-' }; 738 static int m9[] = 739 { 3, S_ISVTX|(S_IEXEC>>6), 't', S_ISVTX, 'T', S_IEXEC>>6, 'x', '-' }; 740 741 static int *m[] = { m1, m2, m3, m4, m5, m6, m7, m8, m9}; 742 743 static char * 744 fmtmode(char *lp, int flags) 745 { 746 int **mp; 747 748 for (mp = &m[0]; mp < &m[sizeof (m)/sizeof (m[0])]; ) { 749 register int *pairp = *mp++; 750 register int n = *pairp++; 751 752 while (n-- > 0) { 753 if ((flags&*pairp) == *pairp) { 754 pairp++; 755 break; 756 } else 757 pairp += 2; 758 } 759 *lp++ = *pairp; 760 } 761 return (lp); 762 } 763 764 /* rest should be done with nameserver or database */ 765 766 #include <pwd.h> 767 #include <grp.h> 768 #include <utmpx.h> 769 770 #define NMAX (sizeof (((struct utmpx *)0)->ut_name)) 771 #define SCPYN(a, b) strncpy(a, b, NMAX) 772 773 774 static struct cachenode { /* this struct must be zeroed before using */ 775 struct cachenode *lesschild; /* subtree whose entries < val */ 776 struct cachenode *grtrchild; /* subtree whose entries > val */ 777 int val; /* the uid or gid of this entry */ 778 int initted; /* name has been filled in */ 779 char name[NMAX+1]; /* the string that val maps to */ 780 } *names, *groups; 781 782 static struct cachenode * 783 findincache(struct cachenode **head, id_t val) 784 { 785 register struct cachenode **parent = head; 786 register struct cachenode *c = *parent; 787 788 while (c != NULL) { 789 if (val == c->val) { 790 /* found it */ 791 return (c); 792 } else if (val < c->val) { 793 parent = &c->lesschild; 794 c = c->lesschild; 795 } else { 796 parent = &c->grtrchild; 797 c = c->grtrchild; 798 } 799 } 800 801 /* not in the cache, make a new entry for it */ 802 *parent = c = (struct cachenode *)calloc(1, sizeof (struct cachenode)); 803 c->val = val; 804 return (c); 805 } 806 807 static char * 808 getname(uid_t uid) 809 { 810 struct cachenode *c; 811 struct passwd *pw; 812 813 c = findincache(&names, uid); 814 if (c->initted == 0) { 815 if ((pw = getpwuid(uid)) != NULL) { 816 (void) SCPYN(&c->name[0], pw->pw_name); 817 } else { 818 (void) sprintf(&c->name[0], "%-8lu ", uid); 819 } 820 c->initted = 1; 821 } 822 return (&c->name[0]); 823 } 824 825 static char * 826 getgroup(gid_t gid) 827 { 828 struct cachenode *c; 829 struct group *gr; 830 831 c = findincache(&groups, gid); 832 if (c->initted == 0) { 833 if ((gr = getgrgid(gid)) != NULL) { 834 (void) SCPYN(&c->name[0], gr->gr_name); 835 } else { 836 (void) sprintf(&c->name[0], "%-8lu ", gid); 837 } 838 c->initted = 1; 839 } 840 return (&c->name[0]); 841 } 842