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