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.9 (Berkeley) 10/22/87"; 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 for (i = 0; i < argc; i++) { 158 if (gstat(fp, *argv, 1, (int *)0)) { 159 fp->fname = *argv; 160 fp->fflags |= ISARG; 161 fp++; 162 } 163 argv++; 164 } 165 fplast = fp; 166 qsort(fp0, fplast - fp0, sizeof (struct afile), fcmp); 167 if (dflg) { 168 formatf(fp0, fplast); 169 exit(0); 170 } 171 if (fflg) 172 fp = fp0; 173 else { 174 for (fp = fp0; fp < fplast && fp->ftype != 'd'; fp++) 175 continue; 176 formatf(fp0, fp); 177 } 178 if (fp < fplast) { 179 if (fp > fp0) 180 putchar('\n'); 181 for (;;) { 182 formatd(fp->fname, argc > 1); 183 while (subdirs) { 184 struct subdirs *t; 185 186 t = subdirs; subdirs = t->sd_next; 187 putchar('\n'); 188 formatd(t->sd_name, 1); 189 cfree(t->sd_name); 190 cfree((char *)t); 191 } 192 if (++fp == fplast) 193 break; 194 putchar('\n'); 195 } 196 } 197 exit(0); 198 } 199 200 formatd(name, title) 201 char *name; 202 int title; 203 { 204 register struct afile *fp; 205 register struct subdirs *dp; 206 struct afile *dfp0, *dfplast; 207 int nkb; 208 209 nkb = getdir(name, &dfp0, &dfplast); 210 if (dfp0 == 0) 211 return; 212 if (fflg == 0) 213 qsort(dfp0, dfplast - dfp0, sizeof (struct afile), fcmp); 214 if (title) 215 printf("%s:\n", name); 216 if (lflg || sflg) 217 printf("total %d\n", nkb); 218 formatf(dfp0, dfplast); 219 if (Rflg) 220 for (fp = dfplast - 1; fp >= dfp0; fp--) { 221 if (fp->ftype != 'd' || 222 !strcmp(fp->fname, ".") || 223 !strcmp(fp->fname, "..")) 224 continue; 225 dp = (struct subdirs *)malloc(sizeof (struct subdirs)); 226 dp->sd_name = savestr(cat(name, fp->fname)); 227 dp->sd_next = subdirs; subdirs = dp; 228 } 229 for (fp = dfp0; fp < dfplast; fp++) { 230 if ((fp->fflags&ISARG) == 0 && fp->fname) 231 cfree(fp->fname); 232 if (fp->flinkto) 233 cfree(fp->flinkto); 234 } 235 cfree((char *)dfp0); 236 } 237 238 getdir(dir, pfp0, pfplast) 239 char *dir; 240 struct afile **pfp0, **pfplast; 241 { 242 register struct afile *fp; 243 DIR *dirp; 244 register struct direct *dp; 245 int nb, nent = 20; 246 247 dirp = opendir(dir); 248 if (dirp == NULL) { 249 *pfp0 = *pfplast = NULL; 250 printf("%s unreadable\n", dir); /* not stderr! */ 251 return (0); 252 } 253 fp = *pfp0 = (struct afile *)calloc(nent, sizeof (struct afile)); 254 *pfplast = *pfp0 + nent; 255 nb = 0; 256 while (dp = readdir(dirp)) { 257 if (dp->d_ino == 0) 258 continue; 259 if (aflg == 0 && dp->d_name[0]=='.' && 260 (Aflg == 0 || dp->d_name[1]==0 || 261 dp->d_name[1]=='.' && dp->d_name[2]==0)) 262 continue; 263 if (gstat(fp, cat(dir, dp->d_name), Fflg+Rflg, &nb) == 0) 264 continue; 265 fp->fnum = dp->d_ino; 266 fp->fname = savestr(dp->d_name); 267 fp++; 268 if (fp == *pfplast) { 269 *pfp0 = (struct afile *)realloc((char *)*pfp0, 270 2 * nent * sizeof (struct afile)); 271 if (*pfp0 == 0) { 272 fputs("ls: out of memory\n", stderr); 273 exit(1); 274 } 275 fp = *pfp0 + nent; 276 *pfplast = fp + nent; 277 nent *= 2; 278 } 279 } 280 closedir(dirp); 281 *pfplast = fp; 282 return (kbytes(dbtob(nb))); 283 } 284 285 int stat(), lstat(); 286 287 struct afile * 288 gstat(fp, file, statarg, pnb) 289 register struct afile *fp; 290 char *file; 291 int statarg, *pnb; 292 { 293 int (*statf)() = Lflg ? stat : lstat; 294 char buf[BUFSIZ]; int cc; 295 static struct afile azerofile; 296 297 *fp = azerofile; 298 fp->fflags = 0; 299 fp->fnum = 0; 300 fp->ftype = '-'; 301 if (statarg || sflg || lflg || tflg) { 302 struct stat stb, stb1; 303 304 if ((*statf)(file, &stb) < 0) { 305 if (statf == lstat || lstat(file, &stb) < 0) { 306 fprintf(stderr, "%s not found\n", file); 307 return (0); 308 } 309 } 310 fp->fblks = stb.st_blocks; 311 fp->fsize = stb.st_size; 312 switch (stb.st_mode & S_IFMT) { 313 314 case S_IFDIR: 315 fp->ftype = 'd'; break; 316 case S_IFBLK: 317 fp->ftype = 'b'; fp->fsize = stb.st_rdev; break; 318 case S_IFCHR: 319 fp->ftype = 'c'; fp->fsize = stb.st_rdev; break; 320 case S_IFSOCK: 321 fp->ftype = 's'; fp->fsize = 0; break; 322 case S_IFLNK: 323 fp->ftype = 'l'; 324 if (lflg) { 325 cc = readlink(file, buf, BUFSIZ); 326 if (cc >= 0) { 327 buf[cc] = 0; 328 fp->flinkto = savestr(buf); 329 } 330 break; 331 } 332 if (stat(file, &stb1) < 0) 333 break; 334 if ((stb1.st_mode & S_IFMT) == S_IFDIR) { 335 stb = stb1; 336 fp->ftype = 'd'; 337 fp->fsize = stb.st_size; 338 fp->fblks = stb.st_blocks; 339 } 340 break; 341 } 342 fp->fnum = stb.st_ino; 343 fp->fflags = stb.st_mode & ~S_IFMT; 344 fp->fnl = stb.st_nlink; 345 fp->fuid = stb.st_uid; 346 fp->fgid = stb.st_gid; 347 if (uflg) 348 fp->fmtime = stb.st_atime; 349 else if (cflg) 350 fp->fmtime = stb.st_ctime; 351 else 352 fp->fmtime = stb.st_mtime; 353 if (pnb) 354 *pnb += stb.st_blocks; 355 } 356 return (fp); 357 } 358 359 formatf(fp0, fplast) 360 struct afile *fp0, *fplast; 361 { 362 register struct afile *fp; 363 register int i, j, w; 364 int width = 0, nentry = fplast - fp0; 365 int columns, lines; 366 char *cp; 367 368 if (fp0 == fplast) 369 return; 370 if (lflg || Cflg == 0) 371 columns = 1; 372 else { 373 for (fp = fp0; fp < fplast; fp++) { 374 int len = strlen(fmtentry(fp)); 375 376 if (len > width) 377 width = len; 378 } 379 if (usetabs) 380 width = (width + 8) &~ 7; 381 else 382 width += 2; 383 columns = twidth / width; 384 if (columns == 0) 385 columns = 1; 386 } 387 lines = (nentry + columns - 1) / columns; 388 for (i = 0; i < lines; i++) { 389 for (j = 0; j < columns; j++) { 390 fp = fp0 + j * lines + i; 391 cp = fmtentry(fp); 392 fputs(cp, stdout); 393 if (fp + lines >= fplast) { 394 putchar('\n'); 395 break; 396 } 397 w = strlen(cp); 398 while (w < width) 399 if (usetabs) { 400 w = (w + 8) &~ 7; 401 putchar('\t'); 402 } else { 403 w++; 404 putchar(' '); 405 } 406 } 407 } 408 } 409 410 fcmp(f1, f2) 411 register struct afile *f1, *f2; 412 { 413 414 if (dflg == 0 && fflg == 0) { 415 if ((f1->fflags&ISARG) && f1->ftype == 'd') { 416 if ((f2->fflags&ISARG) == 0 || f2->ftype != 'd') 417 return (1); 418 } else { 419 if ((f2->fflags&ISARG) && f2->ftype == 'd') 420 return (-1); 421 } 422 } 423 if (tflg) { 424 if (f2->fmtime == f1->fmtime) 425 return (0); 426 if (f2->fmtime > f1->fmtime) 427 return (rflg); 428 return (-rflg); 429 } 430 return (rflg * strcmp(f1->fname, f2->fname)); 431 } 432 433 char * 434 cat(dir, file) 435 char *dir, *file; 436 { 437 static char dfile[BUFSIZ]; 438 register int dlen; 439 440 if ((dlen = strlen(dir))+1+strlen(file)+1 > BUFSIZ) { 441 fputs("ls: filename too long\n", stderr); 442 exit(1); 443 } 444 if (!dir[0] || dir[0] == '.' && !dir[1]) 445 return(strcpy(dfile, file)); 446 (void) strcpy(dfile, dir); 447 if (dir[dlen - 1] != '/' && *file != '/') 448 dfile[dlen++] = '/'; 449 (void) strcpy(dfile + dlen, file); 450 return (dfile); 451 } 452 453 char * 454 savestr(str) 455 char *str; 456 { 457 char *cp = malloc(strlen(str) + 1); 458 459 if (cp == NULL) { 460 fputs("ls: out of memory\n", stderr); 461 exit(1); 462 } 463 return(strcpy(cp, str)); 464 } 465 466 char *fmtinum(), *fmtsize(), *fmtlstuff(), *fmtmode(); 467 468 char * 469 fmtentry(fp) 470 register struct afile *fp; 471 { 472 static char fmtres[BUFSIZ]; 473 register char *cp, *dp; 474 475 (void) sprintf(fmtres, "%s%s%s", 476 iflg ? fmtinum(fp) : "", 477 sflg ? fmtsize(fp) : "", 478 lflg ? fmtlstuff(fp) : ""); 479 dp = &fmtres[strlen(fmtres)]; 480 for (cp = fp->fname; *cp; cp++) 481 if (qflg && (*cp < ' ' || *cp >= 0177)) 482 *dp++ = '?'; 483 else 484 *dp++ = *cp; 485 if (Fflg) { 486 if (fp->ftype == 'd') 487 *dp++ = '/'; 488 else if (fp->ftype == 'l') 489 *dp++ = '@'; 490 else if (fp->ftype == 's') 491 *dp++ = '='; 492 else if (fp->fflags & 0111) 493 *dp++ = '*'; 494 } 495 if (lflg && fp->flinkto) { 496 (void) strcpy(dp, " -> "); dp += 4; 497 for (cp = fp->flinkto; *cp; cp++) 498 if (qflg && (*cp < ' ' || *cp >= 0177)) 499 *dp++ = '?'; 500 else 501 *dp++ = *cp; 502 } 503 *dp++ = 0; 504 return (fmtres); 505 } 506 507 char * 508 fmtinum(p) 509 register struct afile *p; 510 { 511 static char inumbuf[8]; 512 513 (void) sprintf(inumbuf, "%6ld ", p->fnum); 514 return (inumbuf); 515 } 516 517 char * 518 fmtsize(p) 519 register struct afile *p; 520 { 521 static char sizebuf[32]; 522 523 (void) sprintf(sizebuf, "%4ld ", kbytes(dbtob(p->fblks))); 524 return (sizebuf); 525 } 526 527 char * 528 fmtlstuff(p) 529 register struct afile *p; 530 { 531 static char lstuffbuf[256]; 532 char gname[32], uname[32], fsize[32], ftime[32]; 533 register char *lp = lstuffbuf; 534 535 /* type mode uname gname fsize ftime */ 536 /* get uname */ 537 { char *cp = getname(p->fuid); 538 if (cp) 539 (void) sprintf(uname, "%-9.9s", cp); 540 else 541 (void) sprintf(uname, "%-9u", p->fuid); 542 } 543 /* get gname */ 544 if (gflg) { 545 char *cp = getgroup(p->fgid); 546 if (cp) 547 (void) sprintf(gname, "%-9.9s", cp); 548 else 549 (void) sprintf(gname, "%-9u", p->fgid); 550 } 551 /* get fsize */ 552 if (p->ftype == 'b' || p->ftype == 'c') 553 (void) sprintf(fsize, "%3d,%4d", 554 major(p->fsize), minor(p->fsize)); 555 else if (p->ftype == 's') 556 (void) sprintf(fsize, "%8ld", 0L); 557 else 558 (void) sprintf(fsize, "%8ld", p->fsize); 559 /* get ftime */ 560 { char *cp = ctime(&p->fmtime); 561 if ((p->fmtime < sixmonthsago) || (p->fmtime > now)) 562 (void) sprintf(ftime, " %-7.7s %-4.4s ", cp+4, cp+20); 563 else 564 (void) sprintf(ftime, " %-12.12s ", cp+4); 565 } 566 /* splat */ 567 *lp++ = p->ftype; 568 lp = fmtmode(lp, p->fflags); 569 (void) sprintf(lp, "%3d %s%s%s%s", 570 p->fnl, uname, gflg ? gname : "", fsize, ftime); 571 return (lstuffbuf); 572 } 573 574 int m1[] = { 1, S_IREAD>>0, 'r', '-' }; 575 int m2[] = { 1, S_IWRITE>>0, 'w', '-' }; 576 int m3[] = { 3, S_ISUID|(S_IEXEC>>0), 's', S_ISUID, 'S', S_IEXEC>>0, 'x', '-' }; 577 int m4[] = { 1, S_IREAD>>3, 'r', '-' }; 578 int m5[] = { 1, S_IWRITE>>3, 'w', '-' }; 579 int m6[] = { 3, S_ISGID|(S_IEXEC>>3), 's', S_ISGID, 'S', S_IEXEC>>3, 'x', '-' }; 580 int m7[] = { 1, S_IREAD>>6, 'r', '-' }; 581 int m8[] = { 1, S_IWRITE>>6, 'w', '-' }; 582 int m9[] = { 3, S_ISVTX|(S_IEXEC>>6), 't', S_ISVTX, 'T', S_IEXEC>>6, 'x', '-' }; 583 584 int *m[] = { m1, m2, m3, m4, m5, m6, m7, m8, m9}; 585 586 char * 587 fmtmode(lp, flags) 588 char *lp; 589 register int flags; 590 { 591 int **mp; 592 593 for (mp = &m[0]; mp < &m[sizeof(m)/sizeof(m[0])]; ) { 594 register int *pairp = *mp++; 595 register int n = *pairp++; 596 597 while (--n >= 0 && (flags&*pairp) != *pairp) 598 pairp += 2; 599 *lp++ = pairp[n>=0]; 600 } 601 return (lp); 602 } 603 604 /* rest should be done with nameserver or database */ 605 606 #include <pwd.h> 607 #include <grp.h> 608 #include <utmp.h> 609 610 struct utmp utmp; 611 #define NMAX (sizeof (utmp.ut_name)) 612 #define SCPYN(a, b) strncpy(a, b, NMAX) 613 614 #define NCACHE 64 /* power of 2 */ 615 #define CAMASK NCACHE - 1 616 617 char * 618 getname(uid) 619 uid_t uid; 620 { 621 extern int _pw_stayopen; 622 static struct ncache { 623 uid_t uid; 624 char name[NMAX+1]; 625 } c_uid[NCACHE]; 626 register struct passwd *pw; 627 register struct ncache *cp; 628 629 _pw_stayopen = 1; 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