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