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