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