1 #ifndef lint 2 static char *sccsid = "@(#)ls.c 4.18 (Berkeley) 08/15/83"; 3 #endif 4 5 /* 6 * ls 7 * 8 * 4.2bsd version for symbolic links, variable length 9 * directory entries, block size in the inode, etc. 10 */ 11 #include <sys/param.h> 12 #include <sys/stat.h> 13 #include <sys/dir.h> 14 #include <stdio.h> 15 #include <sgtty.h> 16 17 #define kbytes(size) (((size) + 1023) / 1024) 18 19 struct afile { 20 char ftype; /* file type, e.g. 'd', 'c', 'f' */ 21 ino_t fnum; /* inode number of file */ 22 short fflags; /* mode&~S_IFMT, perhaps ISARG */ 23 short fnl; /* number of links */ 24 short fuid; /* owner id */ 25 short fgid; /* group id */ 26 long fsize; /* file size */ 27 long fblks; /* number of blocks used */ 28 time_t fmtime; /* time (modify or access or create) */ 29 char *fname; /* file name */ 30 char *flinkto; /* symbolic link value */ 31 }; 32 33 #define ISARG 0x8000 /* extra ``mode'' */ 34 35 struct subdirs { 36 char *sd_name; 37 struct subdirs *sd_next; 38 } *subdirs; 39 40 int aflg, dflg, gflg, lflg, sflg, tflg, uflg, iflg, fflg, cflg, rflg = 1; 41 int qflg, Aflg, Cflg, Fflg, Lflg, Rflg; 42 43 int usetabs; 44 45 time_t now, sixmonthsago; 46 47 char *dotp = "."; 48 49 struct afile *gstat(); 50 int fcmp(); 51 char *cat(), *savestr(); 52 char *fmtentry(); 53 char *getname(), *getgroup(); 54 55 char *ctime(); 56 char *malloc(), *calloc(), *realloc(); 57 char *sprintf(), *strcpy(), *strcat(); 58 59 main(argc, argv) 60 int argc; 61 char *argv[]; 62 { 63 int i; 64 struct afile *fp0, *fplast; 65 register struct afile *fp; 66 struct sgttyb sgbuf; 67 68 argc--, argv++; 69 if (getuid() == 0) 70 Aflg++; 71 (void) time(&now); sixmonthsago = now - 6L*30L*24L*60L*60L; now += 60; 72 if (isatty(1)) { 73 qflg = Cflg = 1; 74 (void) gtty(1, &sgbuf); 75 if ((sgbuf.sg_flags & XTABS) == 0) 76 usetabs = 1; 77 } else 78 usetabs = 1; 79 while (argc > 0 && **argv == '-') { 80 (*argv)++; 81 while (**argv) switch (*(*argv)++) { 82 83 case 'C': 84 Cflg = 1; break; 85 case 'q': 86 qflg = 1; break; 87 case '1': 88 Cflg = 0; break; 89 case 'a': 90 aflg++; break; 91 case 'A': 92 Aflg++; break; 93 case 'c': 94 cflg++; break; 95 case 's': 96 sflg++; break; 97 case 'd': 98 dflg++; break; 99 case 'g': 100 gflg++; break; 101 case 'l': 102 lflg++; break; 103 case 'r': 104 rflg = -1; break; 105 case 't': 106 tflg++; break; 107 case 'u': 108 uflg++; break; 109 case 'i': 110 iflg++; break; 111 case 'f': 112 fflg++; break; 113 case 'L': 114 Lflg++; break; 115 case 'F': 116 Fflg++; break; 117 case 'R': 118 Rflg++; break; 119 } 120 argc--, argv++; 121 } 122 if (fflg) { 123 aflg++; lflg = 0; sflg = 0; tflg = 0; 124 } 125 if (lflg) 126 Cflg = 0; 127 if (argc == 0) { 128 argc++; 129 argv = &dotp; 130 } 131 fp = (struct afile *)calloc(argc, sizeof (struct afile)); 132 if (fp == 0) { 133 fprintf(stderr, "ls: out of memory\n"); 134 exit(1); 135 } 136 fp0 = fp; 137 for (i = 0; i < argc; i++) { 138 if (gstat(fp, *argv, 1, (int *)0)) { 139 fp->fname = *argv; 140 fp->fflags |= ISARG; 141 fp++; 142 } 143 argv++; 144 } 145 fplast = fp; 146 qsort(fp0, fplast - fp0, sizeof (struct afile), fcmp); 147 if (dflg) { 148 formatf(fp0, fplast); 149 exit(0); 150 } 151 if (fflg) 152 fp = fp0; 153 else { 154 for (fp = fp0; fp < fplast && fp->ftype != 'd'; fp++) 155 continue; 156 formatf(fp0, fp); 157 } 158 if (fp < fplast) { 159 if (fp > fp0) 160 printf("\n"); 161 for (;;) { 162 formatd(fp->fname, argc > 1); 163 while (subdirs) { 164 struct subdirs *t; 165 166 t = subdirs; subdirs = t->sd_next; 167 printf("\n"); 168 formatd(t->sd_name, 1); 169 cfree(t->sd_name); 170 cfree((char *)t); 171 } 172 if (++fp == fplast) 173 break; 174 printf("\n"); 175 } 176 } 177 exit(0); 178 } 179 180 formatd(name, title) 181 char *name; 182 int title; 183 { 184 register struct afile *fp; 185 register struct subdirs *dp; 186 struct afile *dfp0, *dfplast; 187 int nkb; 188 189 nkb = getdir(name, &dfp0, &dfplast); 190 if (dfp0 == 0) 191 return; 192 if (fflg == 0) 193 qsort(dfp0, dfplast - dfp0, sizeof (struct afile), fcmp); 194 if (title) 195 printf("%s:\n", name); 196 if (lflg || sflg) 197 printf("total %ld\n", nkb); 198 formatf(dfp0, dfplast); 199 if (Rflg) 200 for (fp = dfplast; fp >= dfp0; fp--) { 201 if (fp->ftype != 'd' || 202 !strcmp(fp->fname, ".") || 203 !strcmp(fp->fname, "..")) 204 continue; 205 dp = (struct subdirs *)malloc(sizeof (struct subdirs)); 206 dp->sd_name = savestr(cat(name, fp->fname)); 207 dp->sd_next = subdirs; subdirs = dp; 208 } 209 for (fp = dfp0; fp < dfplast; fp++) { 210 if ((fp->fflags&ISARG) == 0 && fp->fname) 211 cfree(fp->fname); 212 if (fp->flinkto) 213 cfree(fp->flinkto); 214 } 215 cfree((char *)dfp0); 216 } 217 218 getdir(dir, pfp0, pfplast) 219 char *dir; 220 struct afile **pfp0, **pfplast; 221 { 222 register struct afile *fp; 223 DIR *dirp; 224 register struct direct *dp; 225 int nb, nent = 20; 226 227 dirp = opendir(dir); 228 if (dirp == NULL) { 229 *pfp0 = *pfplast = NULL; 230 printf("%s unreadable\n", dir); /* not stderr! */ 231 return (0); 232 } 233 fp = *pfp0 = (struct afile *)calloc(nent, sizeof (struct afile)); 234 *pfplast = *pfp0 + nent; 235 nb = 0; 236 while (dp = readdir(dirp)) { 237 if (dp->d_ino == 0) 238 continue; 239 if (aflg == 0 && dp->d_name[0]=='.' && 240 (Aflg == 0 || dp->d_name[1]==0 || 241 dp->d_name[1]=='.' && dp->d_name[2]==0)) 242 continue; 243 if (gstat(fp, cat(dir, dp->d_name), Fflg+Rflg, &nb) == 0) 244 continue; 245 fp->fnum = dp->d_ino; 246 fp->fname = savestr(dp->d_name); 247 fp++; 248 if (fp == *pfplast) { 249 *pfp0 = (struct afile *)realloc((char *)*pfp0, 250 2 * nent * sizeof (struct afile)); 251 if (*pfp0 == 0) { 252 fprintf(stderr, "ls: out of memory\n"); 253 exit(1); 254 } 255 fp = *pfp0 + nent; 256 *pfplast = fp + nent; 257 nent *= 2; 258 } 259 } 260 closedir(dirp); 261 *pfplast = fp; 262 return (kbytes(dbtob(nb))); 263 } 264 265 int stat(), lstat(); 266 267 struct afile * 268 gstat(fp, file, statarg, pnb) 269 register struct afile *fp; 270 char *file; 271 int statarg, *pnb; 272 { 273 int (*statf)() = Lflg ? stat : lstat; 274 char buf[BUFSIZ]; int cc; 275 static struct afile azerofile; 276 277 *fp = azerofile; 278 fp->fflags = 0; 279 fp->fnum = 0; 280 fp->ftype = '-'; 281 if (statarg || sflg || lflg || tflg) { 282 struct stat stb, stb1; 283 284 if ((*statf)(file, &stb) < 0) { 285 if (statf == lstat || lstat(file, &stb) < 0) { 286 fprintf(stderr, "%s not found\n", file); 287 return (0); 288 } 289 } 290 fp->fblks = stb.st_blocks; 291 fp->fsize = stb.st_size; 292 switch (stb.st_mode & S_IFMT) { 293 294 case S_IFDIR: 295 fp->ftype = 'd'; break; 296 case S_IFBLK: 297 fp->ftype = 'b'; fp->fsize = stb.st_rdev; break; 298 case S_IFCHR: 299 fp->ftype = 'c'; fp->fsize = stb.st_rdev; break; 300 case S_IFSOCK: 301 fp->ftype = 's'; fp->fsize = 0; break; 302 case S_IFLNK: 303 fp->ftype = 'l'; 304 if (lflg) { 305 cc = readlink(file, buf, BUFSIZ); 306 if (cc >= 0) { 307 buf[cc] = 0; 308 fp->flinkto = savestr(buf); 309 } 310 break; 311 } 312 if (stat(file, &stb1) < 0) 313 break; 314 if ((stb1.st_mode & S_IFMT) == S_IFDIR) { 315 stb = stb1; 316 fp->ftype = 'd'; 317 fp->fsize = stb.st_size; 318 fp->fblks = stb.st_blocks; 319 } 320 break; 321 } 322 fp->fnum = stb.st_ino; 323 fp->fflags = stb.st_mode & ~S_IFMT; 324 fp->fnl = stb.st_nlink; 325 fp->fuid = stb.st_uid; 326 fp->fgid = stb.st_gid; 327 if (uflg) 328 fp->fmtime = stb.st_atime; 329 else if (cflg) 330 fp->fmtime = stb.st_ctime; 331 else 332 fp->fmtime = stb.st_mtime; 333 if (pnb) 334 *pnb += stb.st_blocks; 335 } 336 return (fp); 337 } 338 339 formatf(fp0, fplast) 340 struct afile *fp0, *fplast; 341 { 342 register struct afile *fp; 343 int width = 0, w, nentry = fplast - fp0; 344 int i, j, columns, lines; 345 char *cp; 346 347 if (fp0 == fplast) 348 return; 349 if (lflg || Cflg == 0) 350 columns = 1; 351 else { 352 for (fp = fp0; fp < fplast; fp++) { 353 int len = strlen(fmtentry(fp)); 354 355 if (len > width) 356 width = len; 357 } 358 if (usetabs) 359 width = (width + 8) &~ 7; 360 else 361 width += 2; 362 columns = 80 / width; 363 if (columns == 0) 364 columns = 1; 365 } 366 lines = (nentry + columns - 1) / columns; 367 for (i = 0; i < lines; i++) { 368 for (j = 0; j < columns; j++) { 369 fp = fp0 + j * lines + i; 370 cp = fmtentry(fp); 371 printf("%s", cp); 372 if (fp + lines >= fplast) { 373 printf("\n"); 374 break; 375 } 376 w = strlen(cp); 377 while (w < width) 378 if (usetabs) { 379 w = (w + 8) &~ 7; 380 putchar('\t'); 381 } else { 382 w++; 383 putchar(' '); 384 } 385 } 386 } 387 } 388 389 fcmp(f1, f2) 390 register struct afile *f1, *f2; 391 { 392 393 if (dflg == 0 && fflg == 0) { 394 if ((f1->fflags&ISARG) && f1->ftype == 'd') { 395 if ((f2->fflags&ISARG) == 0 || f2->ftype != 'd') 396 return (1); 397 } else { 398 if ((f2->fflags&ISARG) && f2->ftype == 'd') 399 return (-1); 400 } 401 } 402 if (tflg) { 403 if (f2->fmtime == f1->fmtime) 404 return (0); 405 if (f2->fmtime > f1->fmtime) 406 return (rflg); 407 return (-rflg); 408 } 409 return (rflg * strcmp(f1->fname, f2->fname)); 410 } 411 412 char * 413 cat(dir, file) 414 char *dir, *file; 415 { 416 static char dfile[BUFSIZ]; 417 418 if (strlen(dir)+1+strlen(file)+1 > BUFSIZ) { 419 fprintf(stderr, "ls: filename too long\n"); 420 exit(1); 421 } 422 if (!strcmp(dir, "") || !strcmp(dir, ".")) { 423 (void) strcpy(dfile, file); 424 return (dfile); 425 } 426 (void) strcpy(dfile, dir); 427 if (dir[strlen(dir) - 1] != '/' && *file != '/') 428 (void) strcat(dfile, "/"); 429 (void) strcat(dfile, file); 430 return (dfile); 431 } 432 433 char * 434 savestr(str) 435 char *str; 436 { 437 char *cp = malloc(strlen(str) + 1); 438 439 if (cp == NULL) { 440 fprintf(stderr, "ls: out of memory\n"); 441 exit(1); 442 } 443 (void) strcpy(cp, str); 444 return (cp); 445 } 446 447 char *fmtinum(), *fmtsize(), *fmtlstuff(), *fmtmode(); 448 449 char * 450 fmtentry(fp) 451 register struct afile *fp; 452 { 453 static char fmtres[BUFSIZ]; 454 register char *cp, *dp; 455 456 (void) sprintf(fmtres, "%s%s%s", 457 iflg ? fmtinum(fp) : "", 458 sflg ? fmtsize(fp) : "", 459 lflg ? fmtlstuff(fp) : ""); 460 dp = &fmtres[strlen(fmtres)]; 461 for (cp = fp->fname; *cp; cp++) 462 if (qflg && (*cp < ' ' || *cp >= 0177)) 463 *dp++ = '?'; 464 else 465 *dp++ = *cp; 466 if (Fflg) { 467 if (fp->ftype == 'd') 468 *dp++ = '/'; 469 else if (fp->ftype == 'l') 470 *dp++ = '@'; 471 else if (fp->ftype == 's') 472 *dp++ = '='; 473 else if (fp->fflags & 0111) 474 *dp++ = '*'; 475 } 476 if (lflg && fp->flinkto) { 477 (void) strcpy(dp, " -> "); dp += 4; 478 for (cp = fp->flinkto; *cp; cp++) 479 if (qflg && (*cp < ' ' || *cp >= 0177)) 480 *dp++ = '?'; 481 else 482 *dp++ = *cp; 483 } 484 *dp++ = 0; 485 return (fmtres); 486 } 487 488 char * 489 fmtinum(p) 490 register struct afile *p; 491 { 492 static char inumbuf[8]; 493 494 (void) sprintf(inumbuf, "%5d ", p->fnum); 495 return (inumbuf); 496 } 497 498 char * 499 fmtsize(p) 500 register struct afile *p; 501 { 502 static char sizebuf[32]; 503 504 (void) sprintf(sizebuf, "%4ld ", kbytes(dbtob(p->fblks))); 505 return (sizebuf); 506 } 507 508 char * 509 fmtlstuff(p) 510 register struct afile *p; 511 { 512 static char lstuffbuf[256]; 513 char gname[32], uname[32], fsize[32], ftime[32]; 514 register char *lp = lstuffbuf; 515 516 /* type mode uname gname fsize ftime */ 517 /* get uname */ 518 { char *cp = getname(p->fuid); 519 if (cp) 520 (void) sprintf(uname, "%-9.9s", cp); 521 else 522 (void) sprintf(uname, "%-9d", p->fuid); 523 } 524 /* get gname */ 525 if (gflg) { 526 char *cp = getgroup(p->fgid); 527 if (cp) 528 (void) sprintf(gname, "%-9.9s", cp); 529 else 530 (void) sprintf(gname, "%-9d", p->fgid); 531 } 532 /* get fsize */ 533 if (p->ftype == 'b' || p->ftype == 'c') 534 (void) sprintf(fsize, "%3d,%4d", 535 major(p->fsize), minor(p->fsize)); 536 else if (p->ftype == 's') 537 (void) sprintf(fsize, "%8ld", 0); 538 else 539 (void) sprintf(fsize, "%8ld", p->fsize); 540 /* get ftime */ 541 { char *cp = ctime(&p->fmtime); 542 if ((p->fmtime < sixmonthsago) || (p->fmtime > now)) 543 (void) sprintf(ftime, " %-7.7s %-4.4s ", cp+4, cp+20); 544 else 545 (void) sprintf(ftime, " %-12.12s ", cp+4); 546 } 547 /* splat */ 548 *lp++ = p->ftype; 549 lp = fmtmode(lp, p->fflags); 550 (void) sprintf(lp, "%3d %s%s%s%s", 551 p->fnl, uname, gflg ? gname : "", fsize, ftime); 552 return (lstuffbuf); 553 } 554 555 int m1[] = { 1, S_IREAD>>0, 'r', '-' }; 556 int m2[] = { 1, S_IWRITE>>0, 'w', '-' }; 557 int m3[] = { 2, S_ISUID, 's', S_IEXEC>>0, 'x', '-' }; 558 int m4[] = { 1, S_IREAD>>3, 'r', '-' }; 559 int m5[] = { 1, S_IWRITE>>3, 'w', '-' }; 560 int m6[] = { 2, S_ISGID, 's', S_IEXEC>>3, 'x', '-' }; 561 int m7[] = { 1, S_IREAD>>6, 'r', '-' }; 562 int m8[] = { 1, S_IWRITE>>6, 'w', '-' }; 563 int m9[] = { 2, S_ISVTX, 't', S_IEXEC>>6, 'x', '-' }; 564 565 int *m[] = { m1, m2, m3, m4, m5, m6, m7, m8, m9}; 566 567 char * 568 fmtmode(lp, flags) 569 char *lp; 570 int flags; 571 { 572 int **mp; 573 574 for (mp = &m[0]; mp < &m[sizeof(m)/sizeof(m[0])]; ) { 575 register int *pairp = *mp++; 576 register int n = *pairp++; 577 578 while (--n >= 0 && (flags&*pairp++) == 0) 579 pairp++; 580 *lp++ = *pairp; 581 } 582 return (lp); 583 } 584 585 /* rest should be done with nameserver or database */ 586 587 #include <pwd.h> 588 #include <grp.h> 589 #include <utmp.h> 590 591 struct utmp utmp; 592 593 #define NUID 2048 594 #define NGID 300 595 #define NMAX (sizeof (utmp.ut_name)) 596 597 char names[NUID][NMAX+1]; 598 char outrangename[NMAX+1]; 599 int outrangeuid = -1; 600 char groups[NGID][NMAX+1]; 601 char outrangegroup[NMAX+1]; 602 int outrangegid = -1; 603 604 char * 605 getname(uid) 606 { 607 register struct passwd *pw; 608 static init; 609 struct passwd *getpwent(); 610 611 if (uid >= 0 && uid < NUID && names[uid][0]) 612 return (&names[uid][0]); 613 if (uid >= 0 && uid == outrangeuid) 614 return (outrangename); 615 if (init == 2) { 616 if (uid < NUID) 617 return (0); 618 setpwent(); 619 while (pw = getpwent()) { 620 if (pw->pw_uid != uid) 621 continue; 622 outrangeuid = pw->pw_uid; 623 strncpy(outrangename, pw->pw_name, NUID); 624 endpwent(); 625 return (outrangename); 626 } 627 endpwent(); 628 return (0); 629 } 630 if (init == 0) 631 setpwent(), init = 1; 632 while (pw = getpwent()) { 633 if (pw->pw_uid < 0 || pw->pw_uid >= NUID) { 634 if (pw->pw_uid == uid) { 635 outrangeuid = pw->pw_uid; 636 strncpy(outrangename, pw->pw_name, NUID); 637 return (outrangename); 638 } 639 continue; 640 } 641 if (names[pw->pw_uid][0]) 642 continue; 643 strncpy(names[pw->pw_uid], pw->pw_name, NMAX); 644 if (pw->pw_uid == uid) { 645 return (&names[uid][0]); 646 } 647 } 648 init = 2; 649 endpwent(); 650 return (0); 651 } 652 653 char * 654 getgroup(gid) 655 { 656 register struct group *gr; 657 static init; 658 struct group *getgrent(); 659 660 if (gid >= 0 && gid < NGID && groups[gid][0]) 661 return (&groups[gid][0]); 662 if (gid >= 0 && gid == outrangegid) 663 return (outrangegroup); 664 if (init == 2) { 665 if (gid < NGID) 666 return (0); 667 setgrent(); 668 while (gr = getgrent()) { 669 if (gr->gr_gid != gid) 670 continue; 671 outrangegid = gr->gr_gid; 672 strncpy(outrangegroup, gr->gr_name, NGID); 673 endgrent(); 674 return (outrangegroup); 675 } 676 endgrent(); 677 return (0); 678 } 679 if (init == 0) 680 setgrent(), init = 1; 681 while (gr = getgrent()) { 682 if (gr->gr_gid < 0 || gr->gr_gid >= NGID) { 683 if (gr->gr_gid == gid) { 684 outrangegid = gr->gr_gid; 685 strncpy(outrangegroup, gr->gr_name, NGID); 686 return (outrangegroup); 687 } 688 continue; 689 } 690 if (groups[gr->gr_gid][0]) 691 continue; 692 strncpy(groups[gr->gr_gid], gr->gr_name, NMAX); 693 if (gr->gr_gid == gid) { 694 return (&groups[gid][0]); 695 } 696 } 697 init = 2; 698 endgrent(); 699 return (0); 700 } 701