1: #define UCB /* Controls output format for -F */ 2: 3: /* 4: * ls - list file or directory 5: * 6: * this version of ls is designed for graphic terminals and to 7: * list directories with lots of files in them compactly. 8: * It supports three variants for listings: 9: * 10: * 1) Columnar output. 11: * 2) Stream output. 12: * 3) Old one per line format. 13: * 14: * Columnar output is the default. 15: * If, however, the standard output is not a teletype, the default 16: * is one-per-line. 17: * 18: * With columnar output, the items are sorted down the columns. 19: * We use columns only for a directory we are interpreting. 20: * Thus, in particular, we do not use columns for 21: * 22: * ls /usr/bin/p* 23: * 24: * This version of ls also prints non-printing characters as '?' if 25: * the standard output is a teletype. 26: * 27: * Flags relating to these and other new features are: 28: * 29: * -m force stream output. 30: * 31: * -1 force one entry per line, e.g. to a teletype 32: * 33: * -q force non-printings to be '?'s, e.g. to a file 34: * 35: * -C force columnar output, e.g. into a file 36: * 37: * -n like -l, but user/group id's in decimal rather than 38: * looking in /etc/passwd to save time 39: * 40: * -F turns on the "flagging" of executables and directories 41: * 42: * -L don't read symbolic links 43: * 44: * -R causes ls to recurse through the branches of the subtree 45: * ala find 46: */ 47: 48: #include <sys/param.h> 49: #include <sys/stat.h> 50: #include <sys/dir.h> 51: #include <stdio.h> 52: #include <ctype.h> 53: #include <pwd.h> 54: #include <grp.h> 55: #include <utmp.h> 56: #include <sgtty.h> 57: #include <varargs.h> 58: #ifdef UCB_QUOTAS 59: #include <sys/inode.h> 60: #include <sys/qstat.h> 61: #endif 62: 63: #ifndef NFILES 64: #define NFILES 1024 65: #endif 66: #ifndef NUID 67: #define NUID 512 /* must not be a multiple of 5 */ 68: #endif 69: #ifndef NGID 70: #define NGID 32 /* must not be a multiple of 5 */ 71: #endif 72: 73: struct utmp utmp; 74: #define NMAX (sizeof (utmp.ut_name)) 75: 76: #define MAXFILEWIDTH 14 77: FILE *pwdf, *dirf; 78: 79: struct lbuf { 80: union { 81: char lname[DIRSIZ + 1]; 82: char *namep; 83: } ln; 84: char ltype; 85: #ifdef UCB_SYMLINKS 86: char *llinkto; 87: #endif 88: ino_t lnum; 89: short lflags; 90: short lnl; 91: short luid; 92: short lgid; 93: long lsize; 94: long lmtime; 95: #ifdef UCB_QUOTAS 96: long lqused; 97: long lqmax; 98: #endif 99: }; 100: 101: struct dchain { 102: char *dc_name; /* the path name */ 103: struct dchain *dc_next; /* the next directory on the chain */ 104: }; 105: 106: struct dchain *dfirst; /* the start of the directory chain */ 107: struct dchain *cdfirst; /* the start of the current directory chain */ 108: struct dchain *dtemp; /* temporary used when linking */ 109: char *curdir; /* the current directory */ 110: 111: int aflg, bflg, dflg, lflg, sflg, tflg, uflg, iflg, fflg, gflg, cflg; 112: int Aflg, nflg, qflg, Fflg, Lflg, Rflg, across, Cflg; 113: int nopad; 114: int tabflg; 115: int rflg = 1; 116: long year; 117: int flags; 118: long tblocks; 119: int statreq; 120: int xtraent; /* for those switches which print out a total */ 121: struct lbuf *flist[NFILES]; 122: struct lbuf **lastp = flist; 123: struct lbuf **firstp = flist; 124: char *dotp = "."; 125: 126: char *makename(); 127: struct lbuf *gstat(); 128: char *ctime(); 129: long nblock(); 130: char *getname(); 131: char *getgroup(); 132: 133: #define ISARG 0100000 134: int colwidth; 135: int filewidth; 136: int fixedwidth; 137: int outcol; 138: 139: extern char _sobuf[]; 140: 141: main(argc, argv) 142: int argc; 143: char *argv[]; 144: { 145: int i, width; 146: register struct lbuf *ep; 147: register struct lbuf **slastp; 148: struct lbuf **epp; 149: struct lbuf lb; 150: char *t; 151: char *cp; 152: int compar(); 153: struct sgttyb sgbuf; 154: 155: Fflg = 0; 156: tabflg = 0; 157: Aflg = getuid() == 0; 158: setbuf(stdout, _sobuf); 159: time (&lb.lmtime); 160: year = lb.lmtime - 6L*30L*24L*60L*60L; /* 6 months ago */ 161: qflg = gtty(1, &sgbuf) == 0; 162: 163: /* guarantee at least on column width */ 164: fixedwidth = 2; 165: 166: /* 167: * If the standard output is not a teletype, 168: * then we default to one-per-line format 169: * otherwise decide between stream and 170: * columnar based on our name. 171: */ 172: if (qflg) { 173: Cflg = 1; 174: if ((sgbuf.sg_flags & XTABS) == 0) 175: tabflg++; 176: for (cp = argv[0]; cp[0] && cp[1]; cp++) 177: continue; 178: /* 179: * Certain kinds of links (l, ll, lr, lf, lx) cause some 180: * various options to be turned on. 181: */ 182: switch (cp[0]) { 183: case 'l': 184: if (cp[-1] == 'l') { 185: /* ll => -l */ 186: lflg = 1; 187: statreq++; 188: xtraent++; 189: } else { 190: /* l => -m */ 191: nopad = 1; 192: Cflg = 0; 193: } 194: break; 195: case 'x': /* lx => -x */ 196: across = 1; 197: break; 198: case 'f': /* lf => -F */ 199: Fflg = 1; 200: break; 201: case 'r': /* lr => -R */ 202: Rflg = 1; 203: break; 204: } 205: } else { 206: tabflg++; 207: } 208: 209: while (--argc > 0 && *argv[1] == '-') { 210: argv++; 211: while (*++*argv) switch (**argv) { 212: /* 213: * C - force columnar output 214: */ 215: case 'C': 216: Cflg = 1; 217: nopad = 0; 218: continue; 219: /* 220: * m - force stream output 221: */ 222: case 'm': 223: Cflg = 0; 224: nopad = 1; 225: continue; 226: /* 227: * x - force sort across 228: */ 229: case 'x': 230: across = 1; 231: nopad = 0; 232: Cflg = 1; 233: continue; 234: /* 235: * q - force ?'s in output 236: */ 237: case 'q': 238: qflg = 1; 239: bflg = 0; 240: continue; 241: /* 242: * b - force octal value in output 243: */ 244: case 'b': 245: bflg = 1; 246: qflg = 0; 247: continue; 248: /* 249: * 1 - force 1/line in output 250: */ 251: case '1': 252: Cflg = 0; 253: nopad = 0; 254: continue; 255: /* STANDARD FLAGS */ 256: case 'a': 257: aflg++; 258: continue; 259: 260: case 'A': 261: Aflg = !Aflg; 262: continue; 263: 264: case 'c': 265: cflg++; 266: continue; 267: 268: case 's': 269: fixedwidth += 5; 270: sflg++; 271: statreq++; 272: xtraent++; 273: continue; 274: 275: case 'd': 276: dflg++; 277: continue; 278: 279: /* 280: * n - don't look in password file 281: */ 282: case 'n': 283: nflg++; 284: case 'l': 285: lflg++; 286: statreq++; 287: xtraent++; 288: continue; 289: 290: case 'r': 291: rflg = -1; 292: continue; 293: 294: case 't': 295: tflg++; 296: statreq++; 297: continue; 298: 299: case 'u': 300: uflg++; 301: continue; 302: 303: case 'i': 304: fixedwidth += 6; 305: iflg++; 306: continue; 307: 308: case 'f': 309: fflg++; 310: continue; 311: 312: case 'g': 313: gflg++; 314: continue; 315: 316: case 'F': 317: Fflg++; 318: continue; 319: 320: case 'L': 321: Lflg++; 322: continue; 323: 324: case 'R': 325: Rflg++; 326: continue; 327: 328: default: 329: fprintf (stderr, "usage: ls [-1ACFLRabcdfgilmnqrstux] [files]\n"); 330: exit(1); 331: } 332: } 333: if (Fflg) 334: #ifdef UCB 335: fixedwidth++; 336: #else 337: fixedwidth += 2; 338: #endif 339: if (fflg) { 340: aflg++; 341: lflg = 0; 342: sflg = 0; 343: tflg = 0; 344: statreq = 0; 345: xtraent = 0; 346: } 347: if(lflg) { 348: Cflg = 0; 349: t = "/etc/passwd"; 350: if (gflg) 351: t = "/etc/group"; 352: nopad = 0; 353: fixedwidth = 70; 354: pwdf = fopen(t, "r"); 355: } 356: if (argc==0) { 357: argc++; 358: argv = &dotp - 1; 359: } 360: for (i=0; i < argc; i++) { 361: argv++; 362: if (Cflg) { 363: width = strlen (*argv); 364: if (width > filewidth) 365: filewidth = width; 366: } 367: if ((ep = gstat(*argv, 1))==NULL) 368: continue; 369: ep->ln.namep = *argv; 370: ep->lflags |= ISARG; 371: } 372: if (!Cflg) 373: filewidth = MAXFILEWIDTH; 374: else 375: colwidth = fixedwidth + filewidth; 376: qsort(firstp, lastp - firstp, sizeof *lastp, compar); 377: slastp = lastp; 378: /* For each argument user typed */ 379: for (epp=firstp; epp<slastp; epp++) { 380: ep = *epp; 381: if (ep->ltype=='d' && dflg==0 || fflg) 382: pdirectory(ep->ln.namep, (argc>1), slastp); 383: else 384: pentry(ep); 385: 386: /* -R: print subdirectories found */ 387: while (dfirst || cdfirst) { 388: /* Place direct subdirs on front in right order */ 389: while (cdfirst) { 390: /* reverse cdfirst onto front of dfirst */ 391: dtemp = cdfirst; 392: cdfirst = cdfirst -> dc_next; 393: dtemp -> dc_next = dfirst; 394: dfirst = dtemp; 395: } 396: /* take off first dir on dfirst & print it */ 397: dtemp = dfirst; 398: dfirst = dfirst->dc_next; 399: pdirectory (dtemp->dc_name, 1, firstp); 400: cfree (dtemp->dc_name); 401: cfree (dtemp); 402: } 403: } 404: if (outcol) 405: putc('\n', stdout); 406: fflush(stdout); 407: } 408: 409: /* 410: * pdirectory: print the directory name, labelling it if title is 411: * nonzero, using lp as the place to start reading in the dir. 412: */ 413: pdirectory (name, title, lp) 414: char *name; 415: int title; 416: struct lbuf **lp; 417: { 418: register struct dchain *dp; 419: register struct lbuf *ap; 420: register char *pname; 421: struct lbuf **app; 422: 423: filewidth = 0; 424: curdir = name; 425: if (title) 426: printf("\n%s:\n", name); 427: lastp = lp; 428: readdir(name); 429: if (!Cflg) 430: filewidth = MAXFILEWIDTH; 431: colwidth = fixedwidth + filewidth; 432: #ifdef notdef 433: /* Taken out because it appears this is done below in pem. */ 434: if (tabflg) { 435: if (colwidth <= 8) 436: colwidth = 8; 437: else 438: if (colwidth <= 16) 439: colwidth = 16; 440: } 441: #endif 442: if (fflg==0) 443: qsort(lp,lastp - lp,sizeof *lastp,compar); 444: if (Rflg) for (app=lastp-1; app>=lp; app--) { 445: ap = *app; 446: if (ap->ltype == 'd' && strcmp(ap->ln.lname, ".") && 447: strcmp(ap->ln.lname, "..")) { 448: dp = (struct dchain *) calloc(1, sizeof(struct dchain)); 449: pname = makename (curdir, ap->ln.lname); 450: dp->dc_name = (char *) calloc(1, strlen(pname)+1); 451: strcpy(dp->dc_name, pname); 452: dp -> dc_next = dfirst; 453: dfirst = dp; 454: } 455: } 456: if (lflg || sflg) 457: printf("total %D", tblocks); 458: pem(lp, lastp); 459: newline(); 460: } 461: 462: /* 463: * pem: print 'em. Print a list of files (e.g. a directory) bounded 464: * by slp and lp. 465: */ 466: pem(slp, lp) 467: register struct lbuf **slp, **lp; 468: { 469: int ncols, nrows, row, col; 470: register struct lbuf **ep; 471: 472: if (tabflg) { 473: if (colwidth <= 9) 474: colwidth = 8; 475: else 476: if (colwidth <= 17) 477: colwidth = 16; 478: } 479: ncols = 80 / colwidth; 480: if (ncols == 1 || Cflg == 0) { 481: for (ep = slp; ep < lp; ep++) 482: pentry(*ep); 483: return; 484: } 485: if (across) { 486: for (ep = slp; ep < lp; ep++) 487: pentry(*ep); 488: return; 489: } 490: if (xtraent) 491: slp--; 492: nrows = (lp - slp - 1) / ncols + 1; 493: for (row = 0; row < nrows; row++) { 494: col = row == 0 && xtraent; 495: for (; col < ncols; col++) { 496: ep = slp + (nrows * col) + row; 497: if (ep < lp) 498: pentry(*ep); 499: } 500: if (outcol) 501: printf("\n"); 502: } 503: } 504: 505: /* 506: * pputchar: like putchar but knows how to handle control chars. 507: * CAUTION: if you make ctrl chars print in ^x notation, or any 508: * other notation which is wider than one character, the column 509: * nature of things (such as files with 14 letter names) will be 510: * messed up. Weigh this carefully! 511: */ 512: pputchar(c) 513: char c; 514: { 515: char cc; 516: 517: switch (c) { 518: case '\t': 519: outcol = (outcol + 8) &~ 7; 520: break; 521: case '\n': 522: outcol = 0; 523: break; 524: default: 525: if (c < ' ' || c >= 0177) { 526: if (qflg) 527: c = '?'; 528: else if (bflg) { 529: outcol += 3; 530: putc ('\\', stdout); 531: cc = '0' + (c>>6 & 07); 532: putc (cc, stdout); 533: cc = '0' + (c>>3 & 07); 534: putc (cc, stdout); 535: c = '0' + (c & 07); 536: } 537: } 538: outcol++; 539: break; 540: } 541: putc(c, stdout); 542: } 543: 544: newline() 545: { 546: if (outcol) 547: putc('\n', stdout); 548: outcol = 0; 549: } 550: 551: /* 552: * column: get to the beginning of the next column. 553: */ 554: column() 555: { 556: 557: if (outcol == 0) 558: return; 559: if (nopad) { 560: putc(',', stdout); 561: outcol++; 562: if (outcol + colwidth + 2 > 80) { 563: putc('\n', stdout); 564: outcol = 0; 565: return; 566: } 567: putc(' ', stdout); 568: outcol++; 569: return; 570: } 571: if (Cflg == 0) { 572: putc('\n', stdout); 573: return; 574: } 575: if ((outcol / colwidth + 2) * colwidth > 80) { 576: putc('\n', stdout); 577: outcol = 0; 578: return; 579: } 580: if (tabflg && (colwidth <= 16)) { 581: if (colwidth > 8) 582: if ((outcol % 16) < 8) { 583: outcol += 8 - (outcol % 8); 584: putc ('\t', stdout); 585: } 586: outcol += 8 - (outcol % 8); 587: putc ('\t', stdout); 588: return; 589: } 590: do { 591: outcol++; 592: putc(' ', stdout); 593: } while (outcol % colwidth); 594: } 595: 596: 597: /* 598: * nblock: the number of 1024 byte blocks a size byte file takes up. 599: */ 600: long 601: nblock(size) 602: long size; 603: { 604: return((size+1023)>>10); 605: } 606: 607: /* 608: * This code handles the rwx- business. 609: * You figure it out. 610: */ 611: int m1[] = { 1, S_IREAD>>0, 'r', '-' }; 612: int m2[] = { 1, S_IWRITE>>0, 'w', '-' }; 613: int m3[] = { 2, S_ISUID, 's', S_IEXEC>>0, 'x', '-' }; 614: int m4[] = { 1, S_IREAD>>3, 'r', '-' }; 615: int m5[] = { 1, S_IWRITE>>3, 'w', '-' }; 616: int m6[] = { 2, S_ISGID, 's', S_IEXEC>>3, 'x', '-' }; 617: int m7[] = { 1, S_IREAD>>6, 'r', '-' }; 618: int m8[] = { 1, S_IWRITE>>6, 'w', '-' }; 619: int m9[] = { 2, S_ISVTX, 't', S_IEXEC>>6, 'x', '-' }; 620: 621: int *m[] = { m1, m2, m3, m4, m5, m6, m7, m8, m9}; 622: 623: pmode(aflag) 624: { 625: register int **mp; 626: 627: flags = aflag; 628: for (mp = &m[0]; mp < &m[sizeof(m)/sizeof(m[0])];) 629: select(*mp++); 630: } 631: 632: select(pairp) 633: register int *pairp; 634: { 635: register int n; 636: 637: n = *pairp++; 638: while (--n>=0 && (flags&*pairp++)==0) 639: pairp++; 640: pputchar(*pairp); 641: } 642: 643: /* 644: * returns cat(dir, "/", file), unless dir ends in /, when it doesn't // 645: */ 646: char * 647: makename(dir, file) 648: char *dir, *file; 649: { 650: static char dfile[100]; 651: register char *dp, *fp; 652: register int i; 653: 654: dp = dfile; 655: fp = dir; 656: while (*fp) 657: *dp++ = *fp++; 658: if (*(dp-1) != '/') 659: *dp++ = '/'; 660: fp = file; 661: for (i=0; i<DIRSIZ; i++) 662: *dp++ = *fp++; 663: *dp = 0; 664: return(dfile); 665: } 666: 667: /* 668: * readdir: read in the directory whose name is dir, 669: * starting at lastp. 670: */ 671: readdir(dir) 672: char *dir; 673: { 674: static struct direct dentry; 675: register int j, width; 676: register struct lbuf *ep; 677: 678: if ((dirf = fopen(dir, "r")) == NULL) { 679: printf("%s unreadable\n", dir); 680: return; 681: } 682: tblocks = 0; 683: for(;;) { 684: if (fread(&dentry, sizeof(dentry), 1, dirf) != 1) 685: break; 686: if (dentry.d_ino==0 || 687: aflg==0 && dentry.d_name[0]=='.' && ( 688: !Aflg || 689: dentry.d_name[1]=='\0' 690: || dentry.d_name[1]=='.' && dentry.d_name[2]=='\0')) 691: continue; 692: if (Cflg) { 693: width = strlen (dentry.d_name); 694: if (width > filewidth) 695: filewidth = width; 696: } 697: ep = gstat(makename(dir, dentry.d_name), Fflg || Rflg); 698: if (ep==NULL) 699: continue; 700: if (ep->lnum != -1) 701: ep->lnum = dentry.d_ino; 702: for (j=0; j<DIRSIZ; j++) 703: ep->ln.lname[j] = dentry.d_name[j]; 704: } 705: fclose(dirf); 706: } 707: 708: /* 709: * stat the given file and return an lbuf containing it. 710: * argfl is nonzero if a stat is required because the file is 711: * an argument, rather than having been found in a directory. 712: */ 713: int stat(), lstat(); 714: 715: struct lbuf * 716: gstat(file, argfl) 717: char *file; 718: { 719: struct stat statb; 720: #ifdef UCB_SYMLINKS 721: int (*statf)() = Lflg ? stat : lstat; 722: char buf[BUFSIZ]; 723: int cc; 724: #else 725: int (*statf)() = stat; 726: #endif 727: register struct lbuf *rep; 728: static int nomocore; 729: #ifdef UCB_QUOTAS 730: struct qstat qstatb; 731: #endif 732: 733: if (nomocore) 734: return(NULL); 735: rep = (struct lbuf *)malloc(sizeof(struct lbuf)); 736: if (rep==NULL) { 737: fprintf(stderr, "ls: out of memory\n"); 738: nomocore = 1; 739: return(NULL); 740: } 741: if (lastp >= &flist[NFILES]) { 742: static int msg; 743: lastp--; 744: if (msg==0) { 745: fprintf(stderr, "ls: too many files\n"); 746: msg++; 747: } 748: } 749: *lastp++ = rep; 750: rep->lflags = 0; 751: rep->lnum = 0; 752: rep->ltype = '-'; 753: if (argfl || statreq) { 754: if ((*statf)(file, &statb)<0) { 755: printf("%s not found\n", file); 756: statb.st_ino = -1; 757: statb.st_size = 0; 758: statb.st_mode = 0; 759: if (argfl) { 760: lastp--; 761: return(0); 762: } 763: } 764: rep->lnum = statb.st_ino; 765: rep->lsize = statb.st_size; 766: switch(statb.st_mode&S_IFMT) { 767: 768: case S_IFDIR: 769: rep->ltype = 'd'; 770: break; 771: 772: case S_IFBLK: 773: rep->ltype = 'b'; 774: rep->lsize = statb.st_rdev; 775: break; 776: 777: case S_IFCHR: 778: rep->ltype = 'c'; 779: rep->lsize = statb.st_rdev; 780: break; 781: 782: #ifdef UCB_QUOTAS 783: case S_IFQUOT: 784: qstat(file, &qstatb); 785: rep->ltype = 'q'; 786: rep->lqused = qstatb.qs_un.qs_qused; 787: rep->lqmax = qstatb.qs_un.qs_qmax; 788: break; 789: #endif 790: 791: #ifdef UCB_SYMLINKS 792: case S_IFLNK: 793: rep->ltype = 'l'; 794: if (lflg) { 795: cc = readlink(file, buf, BUFSIZ); 796: if (cc >= 0) { 797: buf[cc] = '\0'; 798: rep->llinkto = savestr(buf); 799: } 800: } 801: break; 802: #endif 803: 804: case S_IFMPB: 805: rep->ltype = 'M'; 806: rep->lsize = statb.st_rdev; 807: break; 808: 809: case S_IFMPC: 810: rep->ltype = 'm'; 811: rep->lsize = statb.st_rdev; 812: break; 813: } 814: rep->lflags = statb.st_mode & ~S_IFMT; 815: rep->luid = statb.st_uid; 816: rep->lgid = statb.st_gid; 817: rep->lnl = statb.st_nlink; 818: if(uflg) 819: rep->lmtime = statb.st_atime; 820: else if (cflg) 821: rep->lmtime = statb.st_ctime; 822: else 823: rep->lmtime = statb.st_mtime; 824: tblocks += nblock(statb.st_size); 825: } 826: return(rep); 827: } 828: 829: /* 830: * decide whether to print pp1 before or after pp2, based on their 831: * names, various times, and the r flag. 832: */ 833: compar(pp1, pp2) 834: struct lbuf **pp1, **pp2; 835: { 836: register struct lbuf *p1, *p2; 837: 838: p1 = *pp1; 839: p2 = *pp2; 840: if (dflg==0) { 841: if (p1->lflags&ISARG && p1->ltype=='d') { 842: if (!(p2->lflags&ISARG && p2->ltype=='d')) 843: return(1); 844: } else { 845: if (p2->lflags&ISARG && p2->ltype=='d') 846: return(-1); 847: } 848: } 849: if (tflg) { 850: if(p2->lmtime == p1->lmtime) 851: return(0); 852: if(p2->lmtime > p1->lmtime) 853: return(rflg); 854: return(-rflg); 855: } 856: return(rflg * strcmp(p1->lflags&ISARG? p1->ln.namep: p1->ln.lname, 857: p2->lflags&ISARG? p2->ln.namep: p2->ln.lname)); 858: } 859: 860: /* 861: * print the entry pointed at by ap 862: */ 863: pentry(ap) 864: struct lbuf *ap; 865: { 866: struct { char dminor, dmajor;}; 867: register struct lbuf *p; 868: register char *cp; 869: char fname[100]; 870: char *pname, *name; 871: struct passwd *getpwuid(); 872: struct passwd *pwptr; 873: struct group *getgrgid(); 874: 875: fname[0] = 0; 876: p = ap; 877: if (p->lnum == -1) 878: return; 879: column(); 880: if (iflg) 881: if (nopad && !lflg) 882: printf("%d ", p->lnum); 883: else 884: printf("%5d ", p->lnum); 885: if (sflg) 886: switch (p->ltype) { 887: 888: case 'b': 889: case 'c': 890: case 'm': 891: case 'M': 892: if (nopad && !lflg) 893: printf("%D ", 0); 894: else 895: printf("%4D ", 0); 896: break; 897: 898: default: 899: if (nopad && !lflg) 900: printf("%D ", nblock(p->lsize)); 901: else 902: printf("%4D ", nblock(p->lsize)); 903: break; 904: } 905: if (lflg) { 906: pputchar(p->ltype); 907: pmode(p->lflags); 908: printf("%2d ", p->lnl); 909: if(gflg) { 910: name = getgroup(p->lgid); 911: if (nflg == 0 && name != 0) 912: printf("%-9.9s", name); 913: else 914: printf("%-9d", p->lgid); 915: } else { 916: #ifndef UCB_PWHASH 917: if (nflg == 0 && (name = getname(p->luid))) { 918: printf("%-9.9s", name); 919: } 920: #else 921: pwptr = getpwuid(p->luid); 922: if (nflg == 0 && pwptr != 0) 923: printf("%-9.9s", pwptr->pw_name); 924: #endif 925: else 926: printf("%-9d", p->luid); 927: name = getgroup(p->lgid); 928: if (nflg == 0 && name != 0) 929: printf("%-9.9s", name); 930: else 931: printf("%-9d", p->lgid); 932: } 933: switch (p->ltype) { 934: 935: case 'b': 936: case 'c': 937: case 'm': 938: case 'M': 939: printf("%3d,%3d", 940: major((int)p->lsize), minor((int)p->lsize)); 941: break; 942: #ifdef UCB_QUOTAS 943: case 'q': 944: printf("%4ld/%4ld", p->lqused, p->lqmax); 945: break; 946: #endif 947: default: 948: printf("%7ld", p->lsize); 949: } 950: cp = ctime(&p->lmtime); 951: if(p->lmtime < year) 952: printf(" %-7.7s %-4.4s ", cp+4, cp+20); else 953: printf(" %-12.12s ", cp+4); 954: } 955: if (p->lflags & ISARG) 956: strncat (fname, p->ln.namep, 98); 957: else { 958: strncat (fname, p->ln.lname, DIRSIZ); 959: } 960: #ifndef UCB 961: if (Fflg) { 962: if (p->ltype == 'd') 963: strcat (fname, "]"); 964: else if (!nopad) 965: strcat (fname, " "); 966: } 967: #else 968: if (Fflg) { 969: if (p->ltype == 'd') 970: strcat (fname, "/"); 971: else if (p->ltype == 'l') 972: strcat (fname, "@"); 973: else if (p->lflags & 0111) 974: strcat (fname, "*"); 975: else if (!nopad) 976: strcat (fname, " "); 977: } 978: #endif 979: 980: #ifdef UCB_SYMLINKS 981: if (lflg && (p->ln.llinkto != (char *) NULL)) { 982: strcat (fname, " -> "); 983: strcat(fname, p->ln.llinkto); 984: } 985: #endif 986: printf ("%s", fname); 987: free(ap); 988: } 989: 990: /* char printf_id[] = "@(#) printf.c:2.2 6/5/79";*/ 991: 992: /* 993: * This version of printf is compatible with the Version 7 C 994: * printf. The differences are only minor except that this 995: * printf assumes it is to print through pputchar. Version 7 996: * printf is more general (and is much larger) and includes 997: * provisions for floating point. 998: */ 999: 1000: #define MAXOCT 11 /* Maximum octal digits in a long */ 1001: #define MAXINT 32767 /* largest normal length positive integer */ 1002: #define BIG 1000000000 /* largest power of 10 less than an unsigned long */ 1003: #define MAXDIGS 10 /* number of digits in BIG */ 1004: 1005: static int width, sign, fill; 1006: 1007: char *b_dconv(); 1008: 1009: printf(va_alist) 1010: va_dcl 1011: { 1012: va_list ap; 1013: register char *fmt; 1014: char fcode; 1015: int prec; 1016: int length,mask1,nbits,n; 1017: long int mask2, num; 1018: register char *bptr; 1019: char *ptr; 1020: char buf[134]; 1021: 1022: va_start(ap); 1023: fmt = va_arg(ap,char *); 1024: for (;;) { 1025: /* process format string first */ 1026: while ((fcode = *fmt++)!='%') { 1027: /* ordinary (non-%) character */ 1028: if (fcode=='\0') 1029: return; 1030: pputchar(fcode); 1031: } 1032: /* length modifier: -1 for h, 1 for l, 0 for none */ 1033: length = 0; 1034: /* check for a leading - sign */ 1035: sign = 0; 1036: if (*fmt == '-') { 1037: sign++; 1038: fmt++; 1039: } 1040: /* a '0' may follow the - sign */ 1041: /* this is the requested fill character */ 1042: fill = 1; 1043: if (*fmt == '0') { 1044: fill--; 1045: fmt++; 1046: } 1047: 1048: /* Now comes a digit string which may be a '*' */ 1049: if (*fmt == '*') { 1050: width = va_arg(ap, int); 1051: if (width < 0) { 1052: width = -width; 1053: sign = !sign; 1054: } 1055: fmt++; 1056: } 1057: else { 1058: width = 0; 1059: while (*fmt>='0' && *fmt<='9') 1060: width = width * 10 + (*fmt++ - '0'); 1061: } 1062: 1063: /* maybe a decimal point followed by more digits (or '*') */ 1064: if (*fmt=='.') { 1065: if (*++fmt == '*') { 1066: prec = va_arg(ap, int); 1067: fmt++; 1068: } 1069: else { 1070: prec = 0; 1071: while (*fmt>='0' && *fmt<='9') 1072: prec = prec * 10 + (*fmt++ - '0'); 1073: } 1074: } 1075: else 1076: prec = -1; 1077: 1078: /* 1079: * At this point, "sign" is nonzero if there was 1080: * a sign, "fill" is 0 if there was a leading 1081: * zero and 1 otherwise, "width" and "prec" 1082: * contain numbers corresponding to the digit 1083: * strings before and after the decimal point, 1084: * respectively, and "fmt" addresses the next 1085: * character after the whole mess. If there was 1086: * no decimal point, "prec" will be -1. 1087: */ 1088: switch (*fmt) { 1089: case 'L': 1090: case 'l': 1091: length = 2; 1092: /* no break!! */ 1093: case 'h': 1094: case 'H': 1095: length--; 1096: fmt++; 1097: break; 1098: } 1099: 1100: /* 1101: * At exit from the following switch, we will 1102: * emit the characters starting at "bptr" and 1103: * ending at "ptr"-1, unless fcode is '\0'. 1104: */ 1105: switch (fcode = *fmt++) { 1106: /* process characters and strings first */ 1107: case 'c': 1108: buf[0] = va_arg(ap, int); 1109: ptr = bptr = &buf[0]; 1110: if (buf[0] != '\0') 1111: ptr++; 1112: break; 1113: case 's': 1114: bptr = va_arg(ap,char *); 1115: if (bptr==0) 1116: bptr = "(null pointer)"; 1117: if (prec < 0) 1118: prec = MAXINT; 1119: for (n=0; *bptr++ && n < prec; n++) ; 1120: ptr = --bptr; 1121: bptr -= n; 1122: break; 1123: case 'O': 1124: length = 1; 1125: fcode = 'o'; 1126: /* no break */ 1127: case 'o': 1128: case 'X': 1129: case 'x': 1130: if (length > 0) 1131: num = va_arg(ap,long); 1132: else 1133: num = (unsigned)va_arg(ap,int); 1134: if (fcode=='o') { 1135: mask1 = 0x7; 1136: mask2 = 0x1fffffffL; 1137: nbits = 3; 1138: } 1139: else { 1140: mask1 = 0xf; 1141: mask2 = 0x0fffffffL; 1142: nbits = 4; 1143: } 1144: n = (num!=0); 1145: bptr = buf + MAXOCT + 3; 1146: /* shift and mask for speed */ 1147: do 1148: if (((int) num & mask1) < 10) 1149: *--bptr = ((int) num & mask1) + 060; 1150: else 1151: *--bptr = ((int) num & mask1) + 0127; 1152: while (num = (num >> nbits) & mask2); 1153: 1154: if (fcode=='o') { 1155: if (n) 1156: *--bptr = '0'; 1157: } 1158: else 1159: if (!sign && fill <= 0) { 1160: pputchar('0'); 1161: pputchar(fcode); 1162: width -= 2; 1163: } 1164: else { 1165: *--bptr = fcode; 1166: *--bptr = '0'; 1167: } 1168: ptr = buf + MAXOCT + 3; 1169: break; 1170: case 'D': 1171: case 'U': 1172: case 'I': 1173: length = 1; 1174: 1175: fcode = fcode + 'a' - 'A'; 1176: /* no break */ 1177: case 'd': 1178: case 'i': 1179: case 'u': 1180: if (length > 0) 1181: num = va_arg(ap,long); 1182: else { 1183: n = va_arg(ap,int); 1184: if (fcode=='u') 1185: num = (unsigned) n; 1186: else 1187: num = (long) n; 1188: } 1189: if (n = (fcode != 'u' && num < 0)) 1190: num = -num; 1191: /* now convert to digits */ 1192: bptr = b_dconv(num, buf); 1193: if (n) 1194: *--bptr = '-'; 1195: if (fill == 0) 1196: fill = -1; 1197: ptr = buf + MAXDIGS + 1; 1198: break; 1199: default: 1200: /* not a control character, 1201: * print it. 1202: */ 1203: ptr = bptr = &fcode; 1204: ptr++; 1205: break; 1206: } 1207: if (fcode != '\0') 1208: b_emit(bptr,ptr); 1209: } 1210: va_end(ap); 1211: } 1212: 1213: /* b_dconv converts the unsigned long integer "value" to 1214: * printable decimal and places it in "buffer", right-justified. 1215: * The value returned is the address of the first non-zero character, 1216: * or the address of the last character if all are zero. 1217: * The result is NOT null terminated, and is MAXDIGS characters long, 1218: * starting at buffer[1] (to allow for insertion of a sign). 1219: * 1220: * This program assumes it is running on 2's complement machine 1221: * with reasonable overflow treatment. 1222: */ 1223: char * 1224: b_dconv(value, buffer) 1225: long value; 1226: char *buffer; 1227: { 1228: register char *bp; 1229: register int svalue; 1230: int n; 1231: long lval; 1232: 1233: bp = buffer; 1234: 1235: /* zero is a special case */ 1236: if (value == 0) { 1237: bp += MAXDIGS; 1238: *bp = '0'; 1239: return(bp); 1240: } 1241: 1242: /* develop the leading digit of the value in "n" */ 1243: n = 0; 1244: while (value < 0) { 1245: value -= BIG; /* will eventually underflow */ 1246: n++; 1247: } 1248: while ((lval = value - BIG) >= 0) { 1249: value = lval; 1250: n++; 1251: } 1252: 1253: /* stash it in buffer[1] to allow for a sign */ 1254: bp[1] = n + '0'; 1255: /* 1256: * Now develop the rest of the digits. Since speed counts here, 1257: * we do it in two loops. The first gets "value" down until it 1258: * is no larger than MAXINT. The second one uses integer divides 1259: * rather than long divides to speed it up. 1260: */ 1261: bp += MAXDIGS + 1; 1262: while (value > MAXINT) { 1263: *--bp = (int)(value % 10) + '0'; 1264: value /= 10; 1265: } 1266: 1267: /* cannot lose precision */ 1268: svalue = value; 1269: while (svalue > 0) { 1270: *--bp = (svalue % 10) + '0'; 1271: svalue /= 10; 1272: } 1273: 1274: /* fill in intermediate zeroes if needed */ 1275: if (buffer[1] != '0') { 1276: while (bp > buffer + 2) 1277: *--bp = '0'; 1278: --bp; 1279: } 1280: return(bp); 1281: } 1282: 1283: /* 1284: * This program sends string "s" to pputchar. The character after 1285: * the end of "s" is given by "send". This allows the size of the 1286: * field to be computed; it is stored in "alen". "width" contains the 1287: * user specified length. If width<alen, the width will be taken to 1288: * be alen. "sign" is zero if the string is to be right-justified 1289: * in the field, nonzero if it is to be left-justified. "fill" is 1290: * 0 if the string is to be padded with '0', positive if it is to be 1291: * padded with ' ', and negative if an initial '-' should appear before 1292: * any padding in right-justification (to avoid printing "-3" as 1293: * "000-3" where "-0003" was intended). 1294: */ 1295: b_emit(s, send) 1296: register char *s; 1297: char *send; 1298: { 1299: char cfill; 1300: register int alen; 1301: int npad; 1302: 1303: alen = send - s; 1304: if (alen > width) 1305: width = alen; 1306: cfill = fill>0? ' ': '0'; 1307: 1308: /* we may want to print a leading '-' before anything */ 1309: if (*s == '-' && fill < 0) { 1310: pputchar(*s++); 1311: alen--; 1312: width--; 1313: } 1314: npad = width - alen; 1315: 1316: /* emit any leading pad characters */ 1317: if (!sign) 1318: while (--npad >= 0) 1319: pputchar(cfill); 1320: 1321: /* emit the string itself */ 1322: while (--alen >= 0) 1323: pputchar(*s++); 1324: 1325: /* emit trailing pad characters */ 1326: if (sign) 1327: while (--npad >= 0) 1328: pputchar(cfill); 1329: } 1330: 1331: #ifndef UCB_PWHASH 1332: 1333: struct nametable { 1334: char nt_name[NMAX+1]; 1335: unsigned short nt_id; 1336: }; 1337: struct nametable unames[NUID]; 1338: struct nametable gnames[NGID]; 1339: 1340: 1341: struct nametable * 1342: findslot (id, tbl, len) 1343: unsigned short id; 1344: struct nametable *tbl; 1345: int len; 1346: { 1347: register struct nametable *nt, *nt_start; 1348: 1349: /* 1350: * find the id or an empty slot. 1351: * return NULL if neither found. 1352: */ 1353: 1354: nt = nt_start = tbl + (id % (len - 20)); 1355: while (nt->nt_name[0] && nt->nt_id != id) 1356: { 1357: if ((nt += 5) >= &tbl[len]) 1358: nt -= len; 1359: if (nt == nt_start) 1360: return((struct nametable *)NULL); 1361: } 1362: return(nt); 1363: } 1364: 1365: char * 1366: getname (uid) 1367: unsigned short uid; 1368: { 1369: register struct passwd *pw; 1370: static int init = 0; 1371: struct passwd *getpwent(); 1372: register struct nametable *n; 1373: 1374: /* 1375: * find uid in hashed table; add it if not found. 1376: * return pointer to name. 1377: */ 1378: 1379: if ((n = findslot(uid, unames, NUID)) == NULL) 1380: return((char *)NULL); 1381: 1382: if (n->nt_name[0]) /* occupied? */ 1383: return(n->nt_name); 1384: 1385: switch (init) { 1386: case 0: 1387: setpwent(); 1388: init = 1; 1389: /* intentional fall-thru */ 1390: case 1: 1391: while (pw = getpwent()) { 1392: if (pw->pw_uid < 0) 1393: continue; 1394: n = findslot(pw->pw_uid, unames, NUID); 1395: if (n == NULL) { 1396: endpwent(); 1397: init = 2; 1398: return((char *)NULL); 1399: } 1400: if (n->nt_name[0]) 1401: continue; /* duplicate, not uid */ 1402: strncpy(n->nt_name, pw->pw_name, NMAX); 1403: n->nt_id = pw->pw_uid; 1404: if (pw->pw_uid == uid) 1405: return (n->nt_name); 1406: } 1407: endpwent(); 1408: init = 2; 1409: /* intentional fall-thru */ 1410: case 2: 1411: return ((char *)NULL); 1412: } 1413: } 1414: 1415: char * 1416: getgroup (gid) 1417: unsigned short gid; 1418: { 1419: register struct group *gr; 1420: static int init = 0; 1421: struct group *getgrent(); 1422: register struct nametable *n; 1423: 1424: /* 1425: * find gid in hashed table; add it if not found. 1426: * return pointer to name. 1427: */ 1428: 1429: if ((n = findslot(gid, gnames, NGID)) == NULL) 1430: return((char *)NULL); 1431: 1432: if (n->nt_name[0]) /* occupied? */ 1433: return(n->nt_name); 1434: 1435: switch (init) { 1436: case 0: 1437: setgrent(); 1438: init = 1; 1439: /* intentional fall-thru */ 1440: case 1: 1441: while (gr = getgrent()) { 1442: if (gr->gr_gid < 0) 1443: continue; 1444: n = findslot(gr->gr_gid, gnames, NGID); 1445: if (n == NULL) { 1446: endgrent(); 1447: init = 2; 1448: return((char *)NULL); 1449: } 1450: if (n->nt_name[0]) 1451: continue; /* duplicate, not gid */ 1452: strncpy(n->nt_name, gr->gr_name, NMAX); 1453: n->nt_id = gr->gr_gid; 1454: if (gr->gr_gid == gid) 1455: return (n->nt_name); 1456: } 1457: endgrent(); 1458: init = 2; 1459: /* intentional fall-thru */ 1460: case 2: 1461: return ((char *)NULL); 1462: } 1463: } 1464: #endif 1465: 1466: #ifdef UCB_SYMLINKS 1467: char * 1468: savestr(str) 1469: char *str; 1470: { 1471: char *cp = malloc(strlen(str) + 1); 1472: 1473: if (cp == NULL) { 1474: fprintf(stderr, "ls: out of memory\n"); 1475: exit(1); 1476: } 1477: (void) strcpy(cp, str); 1478: return(cp); 1479: } 1480: #endif