/* * list file or directory; * define DOTSUP to suppress listing of files beginning with dot */ #include <sys/param.h> #include <sys/stat.h> #include <sys/dir.h> #include <stdio.h> #define DOTSUP 1 #define NFILES 1024 #define ISARG 0100000 /* this bit equals 1 in lflags of structure lbuf * if *namep is to be used; */ #define DIRECT 10 /* Number of direct blocks */ #define INDIR 128 /* Number of pointers in an indirect block */ #define INSHFT 7 /* Number of right shifts to divide by INDIR */ struct lbuf { union { char lname[DIRSIZ]; /* used for filename in a directory */ char *namep; /* for name in ls-command; */ } ln; char ltype; /* filetype */ unsigned short lnum; /* inode number of file */ short lflags; /* 0777 bits are used as various r,w,x permissions */ short lnl; /* number of links to file */ unsigned short luid; unsigned short lgid; long lsize; /* filesize or major/minor device numbers */ long lmtime; }; struct lbuf *flist[NFILES]; struct lbuf **lastp = flist; struct lbuf **firstp = flist; struct lbuf *gstat(); FILE *pwdfu, *pwdfg, *dirf; int aflg, dflg, lflg, sflg, tflg, uflg, iflg, fflg, gflg, cflg, Gflg; int rflg = 1; /* initialized to 1 for special use in compar() */ int flags; unsigned lastuid = -1, lastgid = -1; int statreq; /* is > 0 if any of sflg, lflg, tflg are on */ char *dotp = "."; char *makename(); char tbufu[16], tbufg[16]; /* assumed 15 = max. length of user/group name */ char *ctime(); char stdbuf[BUFSIZ]; long nblock(); long tblocks; /* total number of blocks of files in a directory */ long year, now; main(argc, argv) char *argv[]; { extern char *optarg; extern int optind; int amino, opterr=0; int c; register struct lbuf *ep, **ep1; register struct lbuf **slastp; struct lbuf **epp; struct lbuf lb; char *t; int i,err=0; int compar(); long time(); #ifdef STANDALONE if (argv[0][0] == '\0') argc = getargv("ls", &argv, 0); #endif setbuf(stdout, stdbuf); lb.lmtime = time((long *) NULL); year = lb.lmtime - 6L*30L*24L*60L*60L; /* 6 months ago */ now = lb.lmtime + 60; while ((c=getopt(argc, argv, "asdglrtucifob")) != EOF) switch(c) { case 'a': aflg++; continue; case 's': sflg++; statreq++; continue; case 'd': dflg++; continue; case 'g': gflg++; Gflg--; lflg++; statreq++; continue; case 'l': case 'b': lflg++; Gflg++; statreq++; continue; case 'r': rflg = -1; continue; case 't': tflg++; statreq++; continue; case 'u': uflg++; continue; case 'c': cflg++; continue; case 'i': iflg++; continue; case 'f': fflg++; continue; case 'o': Gflg--; lflg++; statreq++; continue; case '?': opterr++; continue; } if(opterr) { fprintf(stderr,"usage: ls -asdglrtucifob file . . .\n"); exit(2); } if (fflg) { aflg++; lflg = 0; sflg = 0; tflg = 0; statreq = 0; } if(lflg) { if ((Gflg > 0) || !(gflg > 0)) if ((pwdfu = fopen("/etc/passwd", "r")) == NULL) { fprintf(stderr,"%s file cannot be opened for reading","/etc/passwd"); exit(2); } if ((Gflg > 0) || (gflg > 0)) if ((pwdfg = fopen("/etc/group", "r")) == NULL) { fprintf(stderr,"%s file cannot be opened for reading","/etc/group"); exit(2); } } if ((amino=(argc-optind))==0) { /* case when no names are given * in ls-command and current * directory is to be used */ argv[optind] = dotp; } for (i=0; i < (amino ? amino : 1); i++) { if ((ep = gstat((*argv[optind] ? argv[optind] : dotp), 1))==NULL) { err = 2; optind++; continue; } ep->ln.namep = (*argv[optind] ? argv[optind] : dotp); ep->lflags |= ISARG; optind++; } qsort(firstp, lastp - firstp, sizeof *lastp, compar); slastp = lastp; /* first empty slot in flist[]; starting here * the contents of a directory whose * name appears in ls-command is read * by readdir(); */ for (epp=firstp; epp<slastp; epp++) { ep = *epp; if (ep->ltype=='d' && dflg==0 || fflg) { if (amino>1) printf("\n%s:\n", ep->ln.namep); lastp = slastp; readdir(ep->ln.namep); /* the resulting data lies between slastp and * lastp which is reset by readdir(); */ if (fflg==0) qsort(slastp,lastp - slastp,sizeof *lastp,compar); if (lflg || sflg) printf("total %ld\n", tblocks); for (ep1=slastp; ep1<lastp; ep1++) pentry(*ep1); } else pentry(ep); } exit(err); } pentry(ap) /* print one output line; * if uid/gid is not found in the appropriate * file (passwd/group), then print uid/gid instead of * user/group name; */ struct lbuf *ap; { struct { char dminor, dmajor; }; unsigned short t; register struct lbuf *p; register char *cp; p = ap; if (iflg) printf("%5u ", p->lnum); if (sflg) { if (p->ltype != 'b' && p->ltype != 'c') printf("%4ld ", nblock(p->lsize)); else printf("%4d ",0); /* for special files of type 'b', 'c'; */ } if (lflg) { putchar(p->ltype); pmode(p->lflags); printf("%4d ", p->lnl); if ((Gflg > 0) || !(gflg > 0)) if(getname(p->luid, tbufu, 0)==0) printf("%-9.9s", tbufu); else printf("%-9u", p->luid); if ((Gflg > 0) || (gflg > 0)) if(getname(p->lgid, tbufg, 1)==0) printf("%-9.9s", tbufg); else printf("%-9u", p->lgid); if (p->ltype=='b' || p->ltype=='c') printf("%3d,%3d", major((int)p->lsize), minor((int)p->lsize)); else printf("%7ld", p->lsize); cp = ctime(&p->lmtime); if((p->lmtime < year) || (p->lmtime > now)) printf(" %-7.7s %-4.4s ", cp+4, cp+20); else printf(" %-12.12s ", cp+4); } if (p->lflags&ISARG) printf("%s\n", p->ln.namep); else printf("%.14s\n", p->ln.lname); /* 14 = DIRSIZ */ } getname(uid, buf, type) /* get name from passwd/group file for a given uid/gid * and store it in buf; lastuid is set to uid; * returns -1 if uid is not in file */ unsigned uid; int type; char buf[]; { char c; register i, j, n; if (uid==(type ? lastgid : lastuid)) return(0); rewind(type ? pwdfg : pwdfu); if(type) lastgid = -1; else lastuid = -1; do { i = 0; j = 0; n = 0; while((c=fgetc(type ? pwdfg : pwdfu)) != '\n') { /* '\n' indicates end of * a per user/group record * in passwd/group file; */ if (c==EOF) return(-1); else if (c==':') j++; else if (j==0) buf[i++] = c; else if (j==2) n = n*10 + (int)(c-'0'); } } while (n != uid); buf[i] = '\0'; if (type) lastgid = uid; else lastuid = uid; return(0); } long nblock(size) long size; { long blocks, tot; blocks = tot = (size + 511) >> 9; if(blocks > DIRECT) tot += ((blocks - DIRECT - 1) >> INSHFT) + 1; if(blocks > DIRECT + INDIR) tot += ((blocks - DIRECT - INDIR - 1) >> (INSHFT * 2)) + 1; if(blocks > DIRECT + INDIR + INDIR*INDIR) tot++; return(tot); } pmode(aflag) /* print various r,w,x permissions; */ { /* these arrays are declared static to allow initializations */ static int m0[] = { 1, S_IREAD>>0, 'r', '-' }; static int m1[] = { 1, S_IWRITE>>0, 'w', '-' }; static int m2[] = { 3, S_ISUID|S_IEXEC, 's', S_IEXEC, 'x', S_ISUID, 'S', '-' }; static int m3[] = { 1, S_IREAD>>3, 'r', '-' }; static int m4[] = { 1, S_IWRITE>>3, 'w', '-' }; static int m5[] = { 3, S_ISGID|(S_IEXEC>>3),'s', S_IEXEC>>3,'x', S_ISGID,'S', '-'}; static int m6[] = { 1, S_IREAD>>6, 'r', '-' }; static int m7[] = { 1, S_IWRITE>>6, 'w', '-' }; static int m8[] = { 3, S_ISVTX|(S_IEXEC>>6),'t', S_IEXEC>>6,'x', S_ISVTX,'T', '-'}; static int *m[] = { m0, m1, m2, m3, m4, m5, m6, m7, m8}; register int **mp; flags = aflag; for (mp = &m[0]; mp < &m[sizeof(m)/sizeof(m[0])];) select(*mp++); } select(pairp) register int *pairp; { register int n; n = *pairp++; while (n-->0 && (flags & *pairp) != *pairp++) pairp++; putchar(*pairp); } char * makename(dir, file) /* returns pathname of the form dir/file; * dir is a null-terminated string; */ char *dir, *file; { static char dfile[100+DIRSIZ]; /* 100 is assumed to be max. length of a * file/dir name in ls-command; * dfile is static as this is returned * by makename(); */ register char *dp, *fp; register int i; dp = dfile; fp = dir; while (*fp) *dp++ = *fp++; *dp++ = '/'; fp = file; for (i=0; i<DIRSIZ; i++) *dp++ = *fp++; *dp = '\0'; /* filenames in a directory were not null-terminated */ return(dfile); } readdir(dir) /* read each filename in directory dir and store its * status in flist[] at lastp; * use makename() to form pathname dir/filename; */ char *dir; { struct direct dentry; register int j; register struct lbuf *ep; if ((dirf = fopen(dir, "r")) == NULL) { fflush(stdout); fprintf(stderr, "%s unreadable\n", dir); return; } else { tblocks = 0; for(;;) { if (fread((char *) &dentry, sizeof(dentry), 1, dirf) != 1) break; /* end of directory */ if (dentry.d_ino==0 || aflg==0 && dentry.d_name[0]=='.' # ifndef DOTSUP && (dentry.d_name[1]=='\0' || dentry.d_name[1]=='.' && dentry.d_name[2]=='\0') # endif ) /* check for directory items '.', '..', * and items without valid inode-number; */ continue; ep = gstat(makename(dir, dentry.d_name), 0); if (ep==NULL) continue; else { ep->lnum = dentry.d_ino; for (j=0; j<DIRSIZ; j++) ep->ln.lname[j] = dentry.d_name[j]; } } fclose(dirf); } } struct lbuf * gstat(file, argfl) /* get status of file and recomputes tblocks; * argfl = 1 if file is a name in ls-command and = 0 * for filename in a directory whose name is an * argument in the command; * stores a pointer in flist[] at lastp and * returns that pointer; * returns NULL if failed; */ char *file; { struct stat statb; register struct lbuf *rep; static int nomocore; if (nomocore) return(NULL); else if (lastp >= &flist[NFILES]) { /* flist is full; it is * assumed that ls-command itself * does not have more than * NFILES names; */ fprintf(stderr, "ls: too many files\n"); return(NULL); } rep = (struct lbuf *)malloc((unsigned)sizeof(struct lbuf)); if (rep==NULL) { /* print message first time out of core */ fprintf(stderr, "ls: out of memory\n"); nomocore = 1; return(NULL); } *lastp++ = rep; rep->lflags = 0; if (argfl || statreq) { if (stat(file, &statb)<0) { fprintf(stderr, "%s not found\n", file); lastp--; /* skip this name in ls-command * or file in the directory being read; */ return(NULL); } else { rep->lnum = statb.st_ino; rep->lsize = statb.st_size; switch(statb.st_mode&S_IFMT) { case S_IFDIR: rep->ltype = 'd'; break; case S_IFBLK: rep->ltype = 'b'; rep->lsize = statb.st_rdev; break; case S_IFCHR: rep->ltype = 'c'; rep->lsize = statb.st_rdev; break; case S_IFIFO: rep->ltype = 'p'; break; default: rep->ltype = '-'; } rep->lflags = statb.st_mode & ~S_IFMT; /* mask ISARG and other file-type bits */ rep->luid = statb.st_uid; rep->lgid = statb.st_gid; rep->lnl = statb.st_nlink; if(uflg) rep->lmtime = statb.st_atime; else if (cflg) rep->lmtime = statb.st_ctime; else rep->lmtime = statb.st_mtime; if (rep->ltype != 'b' && rep->ltype != 'c') tblocks += nblock(statb.st_size); } return(rep); } } compar(pp1, pp2) /* return >0 if item pointed by pp2 should appear first */ struct lbuf **pp1, **pp2; { register struct lbuf *p1, *p2; p1 = *pp1; p2 = *pp2; if (dflg==0) { /* compare two names in ls-command one of which is file * and the other is a directory; * this portion is not used for comparing files within * a directory name of ls-command; */ if (p1->lflags&ISARG && p1->ltype=='d') { if (!(p2->lflags&ISARG && p2->ltype=='d')) return(1); } else { if (p2->lflags&ISARG && p2->ltype=='d') return(-1); } } if (tflg) { if(p2->lmtime == p1->lmtime) return(0); else if(p2->lmtime > p1->lmtime) return(rflg); else return(-rflg); } else return(rflg * strcmp(p1->lflags&ISARG? p1->ln.namep: p1->ln.lname, p2->lflags&ISARG? p2->ln.namep: p2->ln.lname)); }