1 #ifndef lint 2 static char *sccsid = "@(#)ls.c 4.8 82/12/03"; 3 #endif 4 5 /* 6 * ls 7 * 8 * 4.2bsd version for symbolic links and variable length directory entries. 9 */ 10 11 #include <sys/param.h> 12 #include <sys/stat.h> 13 #include <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 time_t fmtime; /* time (modify or access or create) */ 28 char *fname; /* file name */ 29 char *flinkto; /* symbolic link value */ 30 }; 31 32 #define ISARG 0x8000 /* extra ``mode'' */ 33 34 struct subdirs { 35 char *sd_name; 36 struct subdirs *sd_next; 37 } *subdirs; 38 39 int aflg, dflg, gflg, lflg, sflg, tflg, uflg, iflg, fflg, cflg, rflg = 1; 40 int qflg, Aflg, Cflg, Fflg, Lflg, Rflg; 41 42 int usetabs; 43 44 time_t now, sixmonthsago; 45 46 char *dotp = "."; 47 48 struct afile *gstat(); 49 int fcmp(); 50 char *cat(), *savestr(); 51 char *fmtentry(); 52 char *getname(), *getgroup(); 53 54 char *ctime(); 55 char *malloc(), *calloc(), *realloc(); 56 char *sprintf(), *strcpy(), *strcat(); 57 58 main(argc, argv) 59 int argc; 60 char *argv[]; 61 { 62 int i; 63 struct afile *fp0, *fplast; 64 register struct afile *fp; 65 struct sgttyb sgbuf; 66 67 argc--, argv++; 68 if (getuid() == 0) 69 Aflg++; 70 (void) time(&now); sixmonthsago = now - 6L*30L*24L*60L*60L; now += 60; 71 if (isatty(1)) { 72 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 nkb, 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 nkb = 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, &nkb) == 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 (nkb); 263 } 264 265 int stat(), lstat(); 266 267 struct afile * 268 gstat(fp, file, statarg, pnkb) 269 register struct afile *fp; 270 char *file; 271 int statarg, *pnkb; 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 fprintf(stderr, "%s not found\n", file); 286 return (0); 287 } 288 fp->fsize = stb.st_size; 289 switch (stb.st_mode & S_IFMT) { 290 291 case S_IFDIR: 292 fp->ftype = 'd'; break; 293 case S_IFBLK: 294 fp->ftype = 'b'; fp->fsize = stb.st_rdev; break; 295 case S_IFCHR: 296 fp->ftype = 'c'; fp->fsize = stb.st_rdev; break; 297 case S_IFSOCK: 298 fp->ftype = 's'; fp->fsize = 0; break; 299 case S_IFLNK: 300 fp->ftype = 'l'; 301 if (lflg) { 302 cc = readlink(file, buf, BUFSIZ); 303 if (cc >= 0) { 304 buf[cc] = 0; 305 fp->flinkto = savestr(buf); 306 } 307 break; 308 } 309 if (stat(file, &stb1) < 0) 310 break; 311 if ((stb1.st_mode & S_IFMT) == S_IFDIR) { 312 stb = stb1; 313 fp->ftype = 'd'; 314 fp->fsize = stb.st_size; 315 } 316 break; 317 } 318 fp->fnum = stb.st_ino; 319 fp->fflags = stb.st_mode & ~S_IFMT; 320 fp->fnl = stb.st_nlink; 321 fp->fuid = stb.st_uid; 322 fp->fgid = stb.st_gid; 323 if (uflg) 324 fp->fmtime = stb.st_atime; 325 else if (cflg) 326 fp->fmtime = stb.st_ctime; 327 else 328 fp->fmtime = stb.st_mtime; 329 if (pnkb) 330 if (fp->ftype != 'b' && fp->ftype != 'c' && 331 fp->ftype != 's') 332 *pnkb += kbytes(fp->fsize); 333 } 334 return (fp); 335 } 336 337 formatf(fp0, fplast) 338 struct afile *fp0, *fplast; 339 { 340 register struct afile *fp; 341 int width = 0, w, nentry = fplast - fp0; 342 int i, j, columns, lines; 343 char *cp; 344 345 if (fp0 == fplast) 346 return; 347 if (lflg || Cflg == 0) 348 columns = 1; 349 else { 350 for (fp = fp0; fp < fplast; fp++) { 351 int len = strlen(fmtentry(fp)); 352 353 if (len > width) 354 width = len; 355 } 356 if (usetabs) 357 width = (width + 8) &~ 7; 358 else 359 width += 2; 360 columns = 80 / width; 361 if (columns == 0) 362 columns = 1; 363 } 364 lines = (nentry + columns - 1) / columns; 365 for (i = 0; i < lines; i++) { 366 for (j = 0; j < columns; j++) { 367 fp = fp0 + j * lines + i; 368 cp = fmtentry(fp); 369 printf("%s", cp); 370 if (fp + lines >= fplast) { 371 printf("\n"); 372 break; 373 } 374 w = strlen(cp); 375 while (w < width) 376 if (usetabs) { 377 w = (w + 8) &~ 7; 378 putchar('\t'); 379 } else { 380 w++; 381 putchar(' '); 382 } 383 } 384 } 385 } 386 387 fcmp(f1, f2) 388 register struct afile *f1, *f2; 389 { 390 391 if (dflg == 0 && fflg == 0) { 392 if ((f1->fflags&ISARG) && f1->ftype == 'd') { 393 if ((f2->fflags&ISARG) == 0 || f2->ftype != 'd') 394 return (1); 395 } else { 396 if ((f2->fflags&ISARG) && f2->ftype == 'd') 397 return (-1); 398 } 399 } 400 if (tflg) { 401 if (f2->fmtime == f1->fmtime) 402 return (0); 403 if (f2->fmtime > f1->fmtime) 404 return (rflg); 405 return (-rflg); 406 } 407 return (rflg * strcmp(f1->fname, f2->fname)); 408 } 409 410 char * 411 cat(dir, file) 412 char *dir, *file; 413 { 414 static char dfile[BUFSIZ]; 415 416 if (strlen(dir)+1+strlen(file)+1 > BUFSIZ) { 417 fprintf(stderr, "ls: filename too long\n"); 418 exit(1); 419 } 420 if (!strcmp(dir, "") || !strcmp(dir, ".")) { 421 (void) strcpy(dfile, file); 422 return (dfile); 423 } 424 (void) strcpy(dfile, dir); 425 if (dir[strlen(dir) - 1] != '/' && *file != '/') 426 (void) strcat(dfile, "/"); 427 (void) strcat(dfile, file); 428 return (dfile); 429 } 430 431 char * 432 savestr(str) 433 char *str; 434 { 435 char *cp = malloc(strlen(str) + 1); 436 437 if (cp == NULL) { 438 fprintf(stderr, "ls: out of memory\n"); 439 exit(1); 440 } 441 (void) strcpy(cp, str); 442 return (cp); 443 } 444 445 char *fmtinum(), *fmtsize(), *fmtlstuff(), *fmtmode(); 446 447 char * 448 fmtentry(fp) 449 register struct afile *fp; 450 { 451 static char fmtres[BUFSIZ]; 452 register char *cp, *dp; 453 454 (void) sprintf(fmtres, "%s%s%s", 455 iflg ? fmtinum(fp) : "", 456 sflg ? fmtsize(fp) : "", 457 lflg ? fmtlstuff(fp) : ""); 458 dp = &fmtres[strlen(fmtres)]; 459 for (cp = fp->fname; *cp; cp++) 460 if (qflg && (*cp < ' ' || *cp >= 0177)) 461 *dp++ = '?'; 462 else 463 *dp++ = *cp; 464 if (Fflg) { 465 if (fp->ftype == 'd') 466 *dp++ = '/'; 467 else if (fp->ftype == 'l') 468 *dp++ = '@'; 469 else if (fp->ftype == 's') 470 *dp++ = '='; 471 else if (fp->fflags & 0111) 472 *dp++ = '*'; 473 } 474 if (lflg && fp->flinkto) { 475 (void) strcpy(dp, " -> "); dp += 4; 476 for (cp = fp->flinkto; *cp; cp++) 477 if (qflg && (*cp < ' ' || *cp >= 0177)) 478 *dp++ = '?'; 479 else 480 *dp++ = *cp; 481 } 482 *dp++ = 0; 483 return (fmtres); 484 } 485 486 char * 487 fmtinum(p) 488 register struct afile *p; 489 { 490 static char inumbuf[8]; 491 492 (void) sprintf(inumbuf, "%5d ", p->fnum); 493 return (inumbuf); 494 } 495 496 char * 497 fmtsize(p) 498 register struct afile *p; 499 { 500 static char sizebuf[32]; 501 502 switch (p->ftype) { 503 504 case 'b': 505 case 'c': 506 case 's': 507 (void) sprintf(sizebuf, "%4ld ", 0); 508 break; 509 510 default: 511 (void) sprintf(sizebuf, "%4ld ", kbytes(p->fsize)); 512 break; 513 } 514 return (sizebuf); 515 } 516 517 char * 518 fmtlstuff(p) 519 register struct afile *p; 520 { 521 static char lstuffbuf[256]; 522 char gname[32], uname[32], fsize[32], ftime[32]; 523 register char *lp = lstuffbuf; 524 525 /* type mode uname gname fsize ftime */ 526 /* get uname */ 527 { char *cp = getname(p->fuid); 528 if (cp) 529 (void) sprintf(uname, "%-9.9s", cp); 530 else 531 (void) sprintf(uname, "%-9d", p->fuid); 532 } 533 /* get gname */ 534 if (gflg) { 535 char *cp = getgroup(p->fgid); 536 if (cp) 537 (void) sprintf(gname, "%-9.9s", cp); 538 else 539 (void) sprintf(gname, "%-9d", p->fgid); 540 } 541 /* get fsize */ 542 if (p->ftype == 'b' || p->ftype == 'c') 543 (void) sprintf(fsize, "%3d,%4d", 544 major(p->fsize), minor(p->fsize)); 545 else if (p->ftype == 's') 546 (void) sprintf(fsize, "%8ld", 0); 547 else 548 (void) sprintf(fsize, "%8ld", p->fsize); 549 /* get ftime */ 550 { char *cp = ctime(&p->fmtime); 551 if ((p->fmtime < sixmonthsago) || (p->fmtime > now)) 552 (void) sprintf(ftime, " %-7.7s %-4.4s ", cp+4, cp+20); 553 else 554 (void) sprintf(ftime, " %-12.12s ", cp+4); 555 } 556 /* splat */ 557 *lp++ = p->ftype; 558 lp = fmtmode(lp, p->fflags); 559 (void) sprintf(lp, "%3d %s%s%s%s", 560 p->fnl, uname, gflg ? gname : "", fsize, ftime); 561 return (lstuffbuf); 562 } 563 564 int m1[] = { 1, S_IREAD>>0, 'r', '-' }; 565 int m2[] = { 1, S_IWRITE>>0, 'w', '-' }; 566 int m3[] = { 2, S_ISUID, 's', S_IEXEC>>0, 'x', '-' }; 567 int m4[] = { 1, S_IREAD>>3, 'r', '-' }; 568 int m5[] = { 1, S_IWRITE>>3, 'w', '-' }; 569 int m6[] = { 2, S_ISGID, 's', S_IEXEC>>3, 'x', '-' }; 570 int m7[] = { 1, S_IREAD>>6, 'r', '-' }; 571 int m8[] = { 1, S_IWRITE>>6, 'w', '-' }; 572 int m9[] = { 2, S_ISVTX, 't', S_IEXEC>>6, 'x', '-' }; 573 574 int *m[] = { m1, m2, m3, m4, m5, m6, m7, m8, m9}; 575 576 char * 577 fmtmode(lp, flags) 578 char *lp; 579 int flags; 580 { 581 int **mp; 582 583 for (mp = &m[0]; mp < &m[sizeof(m)/sizeof(m[0])]; ) { 584 register int *pairp = *mp++; 585 register int n = *pairp++; 586 587 while (--n >= 0 && (flags&*pairp++) == 0) 588 pairp++; 589 *lp++ = *pairp; 590 } 591 return (lp); 592 } 593 594 /* rest should be done with nameserver or database */ 595 596 #include <pwd.h> 597 #include <grp.h> 598 #include <utmp.h> 599 600 struct utmp utmp; 601 602 #define NUID 2048 603 #define NGID 300 604 #define NMAX (sizeof (utmp.ut_name)) 605 606 char names[NUID][NMAX+1]; 607 char groups[NGID][NMAX+1]; 608 609 char * 610 getname(uid) 611 { 612 register struct passwd *pw; 613 static init; 614 struct passwd *getpwent(); 615 616 if (uid >= 0 && uid < NUID && names[uid][0]) 617 return (&names[uid][0]); 618 if (init == 2) 619 return (0); 620 if (init == 0) 621 setpwent(), init = 1; 622 while (pw = getpwent()) { 623 if (pw->pw_uid < 0 || pw->pw_uid >= NUID) 624 continue; 625 if (names[pw->pw_uid][0]) 626 continue; 627 strncpy(names[pw->pw_uid], pw->pw_name, NMAX); 628 if (pw->pw_uid == uid) { 629 return (&names[uid][0]); 630 } 631 } 632 init = 2; 633 endpwent(); 634 return (0); 635 } 636 637 char * 638 getgroup(gid) 639 { 640 register struct group *gr; 641 static init; 642 struct group *getgrent(); 643 644 if (gid >= 0 && gid < NGID && groups[gid][0]) 645 return (&groups[gid][0]); 646 if (init == 2) 647 return (0); 648 if (init == 0) 649 setgrent(), init = 1; 650 while (gr = getgrent()) { 651 if (gr->gr_gid < 0 || gr->gr_gid >= NGID) 652 continue; 653 if (groups[gr->gr_gid][0]) 654 continue; 655 strncpy(groups[gr->gr_gid], gr->gr_name, NMAX); 656 if (gr->gr_gid == gid) { 657 return (&groups[gid][0]); 658 } 659 } 660 init = 2; 661 endgrent(); 662 return (0); 663 } 664