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