ls.c

Go to the documentation of this file.
00001 /*      ls 5.4 - List files.                            Author: Kees J. Bot
00002  *                                                              25 Apr 1989
00003  *
00004  * About the amount of bytes for heap + stack under Minix:
00005  * Ls needs a average amount of 42 bytes per unserviced directory entry, so
00006  * scanning 10 directory levels deep in an ls -R with 100 entries per directory
00007  * takes 42000 bytes of heap.  So giving ls 10000 bytes is tight, 20000 is
00008  * usually enough, 40000 is pessimistic.
00009  */
00010 
00011 /* The array l_ifmt[] is used in an 'ls -l' to map the type of a file to a
00012  * letter.  This is done so that ls can list any future file or device type
00013  * other than symlinks, without recompilation.  (Yes it's dirty.)
00014  */
00015 char l_ifmt[] = "0pcCd?bB-?l?s???";
00016 
00017 #define ifmt(mode)      l_ifmt[((mode) >> 12) & 0xF]
00018 
00019 #define nil 0
00020 #include <stdio.h>
00021 #include <string.h>
00022 #include <sys/types.h>
00023 #include <sys/stat.h>
00024 #include <stddef.h>
00025 #include <stdlib.h>
00026 #include <unistd.h>
00027 #include <dirent.h>
00028 #include <time.h>
00029 #include <pwd.h>
00030 #include <grp.h>
00031 #include <errno.h>
00032 #include <fcntl.h>
00033 #include <limits.h>
00034 #include <termios.h>
00035 #include <sys/ioctl.h>
00036 
00037 #ifndef major
00038 #define major(dev)      ((int) (((dev) >> 8) & 0xFF))
00039 #define minor(dev)      ((int) (((dev) >> 0) & 0xFF))
00040 #endif
00041 
00042 #if !__minix
00043 #define SUPER_ID        uid     /* Let -A flag be default for SUPER_ID == 0. */
00044 #else
00045 #define SUPER_ID        gid
00046 #endif
00047 
00048 #ifdef S_IFLNK
00049 int (*status)(const char *file, struct stat *stp);
00050 #else
00051 #define status  stat
00052 #endif
00053 
00054 /* Basic disk block size is 512 except for one niche O.S. */
00055 #if __minix
00056 #define BLOCK   1024
00057 #else
00058 #define BLOCK    512
00059 #endif
00060 
00061 /* Assume other systems have st_blocks. */
00062 #if !__minix
00063 #define ST_BLOCKS 1
00064 #endif
00065 
00066 /* Some terminals ignore more than 80 characters on a line.  Dumb ones wrap
00067  * when the cursor hits the side.  Nice terminals don't wrap until they have
00068  * to print the 81st character.  Whether we like it or not, no column 80.
00069  */
00070 int ncols= 79;
00071 
00072 #define NSEP    3       /* # spaces between columns. */
00073 
00074 #define MAXCOLS 128     /* Max # of files per line. */
00075 
00076 char *arg0;     /* Last component of argv[0]. */
00077 int uid, gid;   /* callers id. */
00078 int ex= 0;      /* Exit status to be. */
00079 int istty;      /* Output is on a terminal. */
00080 
00081 /* Safer versions of malloc and realloc: */
00082 
00083 void heaperr(void)
00084 {
00085         fprintf(stderr, "%s: Out of memory\n", arg0);
00086         exit(-1);
00087 }
00088 
00089 void *allocate(size_t n)
00090 /* Deliver or die. */
00091 {
00092         void *a;
00093 
00094         if ((a= malloc(n)) == nil) heaperr();
00095         return a;
00096 }
00097 
00098 void *reallocate(void *a, size_t n)
00099 {
00100         if ((a= realloc(a, n)) == nil) heaperr();
00101         return a;
00102 }
00103 
00104 char allowed[] = "acdfghilnpqrstu1ACDFLMRTX";
00105 char flags[sizeof(allowed)];
00106 
00107 char arg0flag[] = "cdfmrtx";    /* These in argv[0] go to upper case. */
00108 
00109 void setflags(char *flgs)
00110 {
00111         int c;
00112 
00113         while ((c= *flgs++) != 0) {
00114                 if (strchr(allowed, c) == nil) {
00115                         fprintf(stderr, "Usage: %s [-%s] [file ...]\n",
00116                                 arg0, allowed);
00117                         exit(1);
00118                 } else
00119                 if (strchr(flags, c) == nil) {
00120                         flags[strlen(flags)] = c;
00121                 }
00122         }
00123 }
00124 
00125 int present(int f)
00126 {
00127         return f == 0 || strchr(flags, f) != nil;
00128 }
00129 
00130 void report(char *f)
00131 /* Like perror(3), but in the style: "ls: junk: No such file or directory. */
00132 {
00133         fprintf(stderr, "%s: %s: %s\n", arg0, f, strerror(errno));
00134         ex= 1;
00135 }
00136 
00137 /* Two functions, uidname and gidname, translate id's to readable names.
00138  * All names are remembered to avoid searching the password file.
00139  */
00140 #define NNAMES  (1 << (sizeof(int) + sizeof(char *)))
00141 enum whatmap { PASSWD, GROUP };
00142 
00143 struct idname {         /* Hash list of names. */
00144         struct idname   *next;
00145         char            *name;
00146         uid_t           id;
00147 } *uids[NNAMES], *gids[NNAMES];
00148 
00149 char *idname(unsigned id, enum whatmap map)
00150 /* Return name for a given user/group id. */
00151 {
00152         struct idname *i;
00153         struct idname **ids= &(map == PASSWD ? uids : gids)[id % NNAMES];
00154 
00155         while ((i= *ids) != nil && id < i->id) ids= &i->next;
00156 
00157         if (i == nil || id != i->id) {
00158                 /* Not found, go look in the password or group map. */
00159                 char *name= nil;
00160                 char noname[3 * sizeof(uid_t)];
00161 
00162                 if (!present('n')) {
00163                         if (map == PASSWD) {
00164                                 struct passwd *pw= getpwuid(id);
00165 
00166                                 if (pw != nil) name= pw->pw_name;
00167                         } else {
00168                                 struct group *gr= getgrgid(id);
00169 
00170                                 if (gr != nil) name= gr->gr_name;
00171                         }
00172                 }
00173                 if (name == nil) {
00174                         /* Can't find it, weird.  Use numerical "name." */
00175                         sprintf(noname, "%u", id);
00176                         name= noname;
00177                 }
00178 
00179                 /* Add a new id-to-name cell. */
00180                 i= allocate(sizeof(*i));
00181                 i->id= id;
00182                 i->name= allocate(strlen(name) + 1);
00183                 strcpy(i->name, name);
00184                 i->next= *ids;
00185                 *ids= i;
00186         }
00187         return i->name;
00188 }
00189 
00190 #define uidname(uid)    idname((uid), PASSWD)
00191 #define gidname(gid)    idname((gid), GROUP)
00192 
00193 /* Path name construction, addpath adds a component, delpath removes it.
00194  * The string path is used throughout the program as the file under examination.
00195  */
00196 
00197 char *path;     /* Path name constructed in path[]. */
00198 int plen= 0, pidx= 0;   /* Lenght/index for path[]. */
00199 
00200 void addpath(int *didx, char *name)
00201 /* Add a component to path. (name may also be a full path at the first call)
00202  * The index where the current path ends is stored in *pdi.
00203  */
00204 {
00205         if (plen == 0) path= (char *) allocate((plen= 32) * sizeof(path[0]));
00206 
00207         if (pidx == 1 && path[0] == '.') pidx= 0;       /* Remove "." */
00208 
00209         *didx= pidx;    /* Record point to go back to for delpath. */
00210 
00211         if (pidx > 0 && path[pidx-1] != '/') path[pidx++]= '/';
00212 
00213         do {
00214                 if (*name != '/' || pidx == 0 || path[pidx-1] != '/') {
00215                         if (pidx == plen) {
00216                                 path= (char *) reallocate((void *) path,
00217                                                 (plen*= 2) * sizeof(path[0]));
00218                         }
00219                         path[pidx++]= *name;
00220                 }
00221         } while (*name++ != 0);
00222 
00223         --pidx;         /* Put pidx back at the null.  The path[pidx++]= '/'
00224                          * statement will overwrite it at the next call.
00225                          */
00226 }
00227 
00228 #define delpath(didx)   (path[pidx= didx]= 0)   /* Remove component. */
00229 
00230 int field = 0;  /* (used to be) Fields that must be printed. */
00231                 /* (now) Effects triggered by certain flags. */
00232 
00233 #define L_INODE         0x0001  /* -i */
00234 #define L_BLOCKS        0x0002  /* -s */
00235 #define L_EXTRA         0x0004  /* -X */
00236 #define L_MODE          0x0008  /* -lMX */
00237 #define L_LONG          0x0010  /* -l */
00238 #define L_GROUP         0x0020  /* -g */
00239 #define L_BYTIME        0x0040  /* -tuc */
00240 #define L_ATIME         0x0080  /* -u */
00241 #define L_CTIME         0x0100  /* -c */
00242 #define L_MARK          0x0200  /* -F */
00243 #define L_MARKDIR       0x0400  /* -p */
00244 #define L_TYPE          0x0800  /* -D */
00245 #define L_LONGTIME      0x1000  /* -T */
00246 #define L_DIR           0x2000  /* -d */
00247 #define L_KMG           0x4000  /* -h */
00248 
00249 struct file {           /* A file plus stat(2) information. */
00250         struct file     *next;  /* Lists are made of them. */
00251         char            *name;  /* Null terminated name. */
00252         ino_t           ino;
00253         mode_t          mode;
00254         uid_t           uid;
00255         gid_t           gid;
00256         nlink_t         nlink;
00257         dev_t           rdev;
00258         off_t           size;
00259         time_t          mtime;
00260         time_t          atime;
00261         time_t          ctime;
00262 #if ST_BLOCKS
00263         long            blocks;
00264 #endif
00265 };
00266 
00267 void setstat(struct file *f, struct stat *stp)
00268 {
00269         f->ino=         stp->st_ino;
00270         f->mode=        stp->st_mode;
00271         f->nlink=       stp->st_nlink;
00272         f->uid=         stp->st_uid;
00273         f->gid=         stp->st_gid;
00274         f->rdev=        stp->st_rdev;
00275         f->size=        stp->st_size;
00276         f->mtime=       stp->st_mtime;
00277         f->atime=       stp->st_atime;
00278         f->ctime=       stp->st_ctime;
00279 #if ST_BLOCKS
00280         f->blocks=      stp->st_blocks;
00281 #endif
00282 }
00283 
00284 #define PAST    (26*7*24*3600L) /* Half a year ago. */
00285 /* Between PAST and FUTURE from now a time is printed, otherwise a year. */
00286 #define FUTURE  ( 1*7*24*3600L) /* One week. */
00287 
00288 static char *timestamp(struct file *f)
00289 /* Transform the right time field into something readable. */
00290 {
00291         struct tm *tm;
00292         time_t t;
00293         static time_t now;
00294         static int drift= 0;
00295         static char date[] = "Jan 19 03:14:07 2038";
00296         static char month[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
00297 
00298         t= f->mtime;
00299         if (field & L_ATIME) t= f->atime;
00300         if (field & L_CTIME) t= f->ctime;
00301 
00302         tm= localtime(&t);
00303         if (--drift < 0) { time(&now); drift= 50; }     /* limit time() calls */
00304 
00305         if (field & L_LONGTIME) {
00306                 sprintf(date, "%.3s %2d %02d:%02d:%02d %d",
00307                         month + 3*tm->tm_mon,
00308                         tm->tm_mday,
00309                         tm->tm_hour, tm->tm_min, tm->tm_sec,
00310                         1900 + tm->tm_year);
00311         } else
00312         if (t < now - PAST || t > now + FUTURE) {
00313                 sprintf(date, "%.3s %2d  %d",
00314                         month + 3*tm->tm_mon,
00315                         tm->tm_mday,
00316                         1900 + tm->tm_year);
00317         } else {
00318                 sprintf(date, "%.3s %2d %02d:%02d",
00319                         month + 3*tm->tm_mon,
00320                         tm->tm_mday,
00321                         tm->tm_hour, tm->tm_min);
00322         }
00323         return date;
00324 }
00325 
00326 char *permissions(struct file *f)
00327 /* Compute long or short rwx bits. */
00328 {
00329         static char rwx[] = "drwxr-x--x";
00330 
00331         rwx[0] = ifmt(f->mode);
00332         /* Note that rwx[0] is a guess for the more alien file types.  It is
00333          * correct for BSD4.3 and derived systems.  I just don't know how
00334          * "standardized" these numbers are.
00335          */
00336 
00337         if (field & L_EXTRA) {          /* Short style */
00338                 int mode = f->mode, ucase= 0;
00339 
00340                 if (uid == f->uid) {    /* What group of bits to use. */
00341                         /* mode<<= 0, */
00342                         ucase= (mode<<3) | (mode<<6);
00343                         /* Remember if group or others have permissions. */
00344                 } else
00345                 if (gid == f->gid) {
00346                         mode<<= 3;
00347                 } else {
00348                         mode<<= 6;
00349                 }
00350                 rwx[1]= mode&S_IRUSR ? (ucase&S_IRUSR ? 'R' : 'r') : '-';
00351                 rwx[2]= mode&S_IWUSR ? (ucase&S_IWUSR ? 'W' : 'w') : '-';
00352 
00353                 if (mode&S_IXUSR) {
00354                         static char sbit[]= { 'x', 'g', 'u', 's' };
00355 
00356                         rwx[3]= sbit[(f->mode&(S_ISUID|S_ISGID))>>10];
00357                         if (ucase&S_IXUSR) rwx[3] += 'A'-'a';
00358                 } else {
00359                         rwx[3]= f->mode&(S_ISUID|S_ISGID) ? '=' : '-';
00360                 }
00361                 rwx[4]= 0;
00362         } else {                /* Long form. */
00363                 char *p= rwx+1;
00364                 int mode= f->mode;
00365 
00366                 do {
00367                         p[0] = (mode & S_IRUSR) ? 'r' : '-';
00368                         p[1] = (mode & S_IWUSR) ? 'w' : '-';
00369                         p[2] = (mode & S_IXUSR) ? 'x' : '-';
00370                         mode<<= 3;
00371                 } while ((p+=3) <= rwx+7);
00372 
00373                 if (f->mode&S_ISUID) rwx[3]= f->mode&(S_IXUSR>>0) ? 's' : '=';
00374                 if (f->mode&S_ISGID) rwx[6]= f->mode&(S_IXUSR>>3) ? 's' : '=';
00375                 if (f->mode&S_ISVTX) rwx[9]= f->mode&(S_IXUSR>>6) ? 't' : '=';
00376         }
00377         return rwx;
00378 }
00379 
00380 void numeral(int i, char **pp)
00381 {
00382         char itoa[3*sizeof(int)], *a=itoa;
00383 
00384         do *a++ = i%10 + '0'; while ((i/=10) > 0);
00385 
00386         do *(*pp)++ = *--a; while (a>itoa);
00387 }
00388 
00389 #define K       1024L           /* A kilobyte counts in multiples of K */
00390 #define T       1000L           /* A megabyte in T*K, a gigabyte in T*T*K */
00391 
00392 char *cxsize(struct file *f)
00393 /* Try and fail to turn a 32 bit size into 4 readable characters. */
00394 {
00395         static char siz[] = "1.2m";
00396         char *p= siz;
00397         off_t z;
00398 
00399         siz[1]= siz[2]= siz[3]= 0;
00400 
00401         if (f->size <= 5*K) {   /* <= 5K prints as is. */
00402                 numeral((int) f->size, &p);
00403                 return siz;
00404         }
00405         z= (f->size + K-1) / K;
00406 
00407         if (z <= 999) {         /* Print as 123k. */
00408                 numeral((int) z, &p);
00409                 *p = 'k';       /* Can't use 'K', looks bad */
00410         } else
00411         if (z*10 <= 99*T) {     /* 1.2m (Try ls -X /dev/at0) */
00412                 z= (z*10 + T-1) / T;    /* Force roundup */
00413                 numeral((int) z / 10, &p);
00414                 *p++ = '.';
00415                 numeral((int) z % 10, &p);
00416                 *p = 'm';
00417         } else
00418         if (z <= 999*T) {       /* 123m */
00419                 numeral((int) ((z + T-1) / T), &p);
00420                 *p = 'm';
00421         } else {                /* 1.2g */
00422                 z= (z*10 + T*T-1) / (T*T);
00423                 numeral((int) z / 10, &p);
00424                 *p++ = '.';
00425                 numeral((int) z % 10, &p);
00426                 *p = 'g';
00427         }
00428         return siz;
00429 }
00430 
00431 /* Transform size of file to number of blocks.  This was once a function that
00432  * guessed the number of indirect blocks, but that nonsense has been removed.
00433  */
00434 #if ST_BLOCKS
00435 #define nblocks(f)      ((f)->blocks)
00436 #else
00437 #define nblocks(f)      (((f)->size + BLOCK-1) / BLOCK)
00438 #endif
00439 
00440 /* From number of blocks to kilobytes. */
00441 #if BLOCK < 1024
00442 #define nblk2k(nb)      (((nb) + (1024 / BLOCK - 1)) / (1024 / BLOCK))
00443 #else
00444 #define nblk2k(nb)      ((nb) * (BLOCK / 1024))
00445 #endif
00446 
00447 static int (*CMP)(struct file *f1, struct file *f2);
00448 static int (*rCMP)(struct file *f1, struct file *f2);
00449 
00450 static void mergesort(struct file **al)
00451 /* This is either a stable mergesort, or thermal noise, I'm no longer sure.
00452  * It must be called like this: if (L != nil && L->next != nil) mergesort(&L);
00453  */
00454 {
00455         /* static */ struct file *l1, **mid;  /* Need not be local */
00456         struct file *l2;
00457 
00458         l1= *(mid= &(*al)->next);
00459         do {
00460                 if ((l1= l1->next) == nil) break;
00461                 mid= &(*mid)->next;
00462         } while ((l1= l1->next) != nil);
00463 
00464         l2= *mid;
00465         *mid= nil;
00466 
00467         if ((*al)->next != nil) mergesort(al);
00468         if (l2->next != nil) mergesort(&l2);
00469 
00470         l1= *al;
00471         for (;;) {
00472                 if ((*CMP)(l1, l2) <= 0) {
00473                         if ((l1= *(al= &l1->next)) == nil) {
00474                                 *al= l2;
00475                                 break;
00476                         }
00477                 } else {
00478                         *al= l2;
00479                         l2= *(al= &l2->next);
00480                         *al= l1;
00481                         if (l2 == nil) break;
00482                 }
00483         }
00484 }
00485 
00486 int namecmp(struct file *f1, struct file *f2)
00487 {
00488         return strcmp(f1->name, f2->name);
00489 }
00490 
00491 int mtimecmp(struct file *f1, struct file *f2)
00492 {
00493         return f1->mtime == f2->mtime ? 0 : f1->mtime > f2->mtime ? -1 : 1;
00494 }
00495 
00496 int atimecmp(struct file *f1, struct file *f2)
00497 {
00498         return f1->atime == f2->atime ? 0 : f1->atime > f2->atime ? -1 : 1;
00499 }
00500 
00501 int ctimecmp(struct file *f1, struct file *f2)
00502 {
00503         return f1->ctime == f2->ctime ? 0 : f1->ctime > f2->ctime ? -1 : 1;
00504 }
00505 
00506 int typecmp(struct file *f1, struct file *f2)
00507 {
00508         return ifmt(f1->mode) - ifmt(f2->mode);
00509 }
00510 
00511 int revcmp(struct file *f1, struct file *f2) { return (*rCMP)(f2, f1); }
00512 
00513 static void sort(struct file **al)
00514 /* Sort the files according to the flags. */
00515 {
00516         if (!present('f') && *al != nil && (*al)->next != nil) {
00517                 CMP= namecmp;
00518 
00519                 if (!(field & L_BYTIME)) {
00520                         /* Sort on name */
00521 
00522                         if (present('r')) { rCMP= CMP; CMP= revcmp; }
00523                         mergesort(al);
00524                 } else {
00525                         /* Sort on name first, then sort on time. */
00526 
00527                         mergesort(al);
00528                         if (field & L_CTIME) {
00529                                 CMP= ctimecmp;
00530                         } else
00531                         if (field & L_ATIME) {
00532                                 CMP= atimecmp;
00533                         } else {
00534                                 CMP= mtimecmp;
00535                         }
00536 
00537                         if (present('r')) { rCMP= CMP; CMP= revcmp; }
00538                         mergesort(al);
00539                 }
00540                 /* Separate by file type if so desired. */
00541 
00542                 if (field & L_TYPE) {
00543                         CMP= typecmp;
00544                         mergesort(al);
00545                 }
00546         }
00547 }
00548 
00549 struct file *newfile(char *name)
00550 /* Create file structure for given name. */
00551 {
00552         struct file *new;
00553 
00554         new= (struct file *) allocate(sizeof(*new));
00555         new->name= strcpy((char *) allocate(strlen(name)+1), name);
00556         return new;
00557 }
00558 
00559 void pushfile(struct file **flist, struct file *new)
00560 /* Add file to the head of a list. */
00561 {
00562         new->next= *flist;
00563         *flist= new;
00564 }
00565 
00566 void delfile(struct file *old)
00567 /* Release old file structure. */
00568 {
00569         free((void *) old->name);
00570         free((void *) old);
00571 }
00572 
00573 struct file *popfile(struct file **flist)
00574 /* Pop file off top of file list. */
00575 {
00576         struct file *f;
00577 
00578         f= *flist;
00579         *flist= f->next;
00580         return f;
00581 }
00582 
00583 int dotflag(char *name)
00584 /* Return flag that would make ls list this name: -a or -A. */
00585 {
00586         if (*name++ != '.') return 0;
00587 
00588         switch (*name++) {
00589         case 0:         return 'a';                     /* "." */
00590         case '.':       if (*name == 0) return 'a';     /* ".." */
00591         default:        return 'A';                     /* ".*" */
00592         }
00593 }
00594 
00595 int adddir(struct file **aflist, char *name)
00596 /* Add directory entries of directory name to a file list. */
00597 {
00598         DIR *d;
00599         struct dirent *e;
00600 
00601         if (access(name, 0) < 0) {
00602                 report(name);
00603                 return 0;
00604         }
00605 
00606         if ((d= opendir(name)) == nil) {
00607                 report(name);
00608                 return 0;
00609         }
00610         while ((e= readdir(d)) != nil) {
00611                 if (e->d_ino != 0 && present(dotflag(e->d_name))) {
00612                         pushfile(aflist, newfile(e->d_name));
00613                         aflist= &(*aflist)->next;
00614                 }
00615         }
00616         closedir(d);
00617         return 1;
00618 }
00619 
00620 off_t countblocks(struct file *flist)
00621 /* Compute total block count for a list of files. */
00622 {
00623         off_t cb = 0;
00624 
00625         while (flist != nil) {
00626                 switch (flist->mode & S_IFMT) {
00627                 case S_IFDIR:
00628                 case S_IFREG:
00629 #ifdef S_IFLNK
00630                 case S_IFLNK:
00631 #endif
00632                         cb += nblocks(flist);
00633                 }
00634                 flist= flist->next;
00635         }
00636         return cb;
00637 }
00638 
00639 void printname(char *name)
00640 /* Print a name with control characters as '?' (unless -q).  The terminal is
00641  * assumed to be eight bit clean.
00642  */
00643 {
00644         int c, q= present('q');
00645 
00646         while ((c= (unsigned char) *name++) != 0) {
00647                 if (q && (c < ' ' || c == 0177)) c= '?';
00648                 putchar(c);
00649         }
00650 }
00651 
00652 int mark(struct file *f, int doit)
00653 {
00654         int c;
00655 
00656         c= 0;
00657 
00658         if (field & L_MARK) {
00659                 switch (f->mode & S_IFMT) {
00660                 case S_IFDIR:   c= '/'; break;
00661 #ifdef S_IFIFO
00662                 case S_IFIFO:   c= '|'; break;
00663 #endif
00664 #ifdef S_IFLNK
00665                 case S_IFLNK:   c= '@'; break;
00666 #endif
00667 #ifdef S_IFSOCK
00668                 case S_IFSOCK:  c= '='; break;
00669 #endif
00670                 case S_IFREG:
00671                         if (f->mode & (S_IXUSR | S_IXGRP | S_IXOTH)) c= '*';
00672                         break;
00673                 }
00674         } else
00675         if (field & L_MARKDIR) {
00676                 if (S_ISDIR(f->mode)) c= '/';
00677         }
00678 
00679         if (doit && c != 0) putchar(c);
00680         return c;
00681 }
00682 
00683 /* Width of entire column, and of several fields. */
00684 enum { W_COL, W_INO, W_BLK, W_NLINK, W_UID, W_GID, W_SIZE, W_NAME, MAXFLDS };
00685 
00686 unsigned char fieldwidth[MAXCOLS][MAXFLDS];
00687 
00688 void maxise(unsigned char *aw, int w)
00689 /* Set *aw to the larger of it and w. */
00690 {
00691         if (w > *aw) {
00692                 if (w > UCHAR_MAX) w= UCHAR_MAX;
00693                 *aw= w;
00694         }
00695 }
00696 
00697 int numwidth(unsigned long n)
00698 /* Compute width of 'n' when printed. */
00699 {
00700         int width= 0;
00701 
00702         do { width++; } while ((n /= 10) > 0);
00703         return width;
00704 }
00705 
00706 #if !__minix
00707 int numxwidth(unsigned long n)
00708 /* Compute width of 'n' when printed in hex. */
00709 {
00710         int width= 0;
00711 
00712         do { width++; } while ((n /= 16) > 0);
00713         return width;
00714 }
00715 #endif
00716 
00717 static int nsp= 0;      /* This many spaces have not been printed yet. */
00718 #define spaces(n)       (nsp= (n))
00719 #define terpri()        (nsp= 0, putchar('\n'))         /* No trailing spaces */
00720 
00721 void print1(struct file *f, int col, int doit)
00722 /* Either compute the number of spaces needed to print file f (doit == 0) or
00723  * really print it (doit == 1).
00724  */
00725 {
00726         int width= 0, n;
00727         char *p;
00728         unsigned char *f1width = fieldwidth[col];
00729 
00730         while (nsp>0) { putchar(' '); nsp--; }/* Fill gap between two columns */
00731 
00732         if (field & L_INODE) {
00733                 if (doit) {
00734                         printf("%*d ", f1width[W_INO], f->ino);
00735                 } else {
00736                         maxise(&f1width[W_INO], numwidth(f->ino));
00737                         width++;
00738                 }
00739         }
00740         if (field & L_BLOCKS) {
00741                 unsigned long nb= nblk2k(nblocks(f));
00742                 if (doit) {
00743                         printf("%*lu ", f1width[W_BLK], nb);
00744                 } else {
00745                         maxise(&f1width[W_BLK], numwidth(nb));
00746                         width++;
00747                 }
00748         }
00749         if (field & L_MODE) {
00750                 if (doit) {
00751                         printf("%s ", permissions(f));
00752                 } else {
00753                         width+= (field & L_EXTRA) ? 5 : 11;
00754                 }
00755         }
00756         if (field & L_EXTRA) {
00757                 p= cxsize(f);
00758                 n= strlen(p)+1;
00759 
00760                 if (doit) {
00761                         n= f1width[W_SIZE] - n;
00762                         while (n > 0) { putchar(' '); --n; }
00763                         printf("%s ", p);
00764                 } else {
00765                         maxise(&f1width[W_SIZE], n);
00766                 }
00767         }
00768         if (field & L_LONG) {
00769                 if (doit) {
00770                         printf("%*u ", f1width[W_NLINK], (unsigned) f->nlink);
00771                 } else {
00772                         maxise(&f1width[W_NLINK], numwidth(f->nlink));
00773                         width++;
00774                 }
00775                 if (!(field & L_GROUP)) {
00776                         if (doit) {
00777                                 printf("%-*s  ", f1width[W_UID],
00778                                                         uidname(f->uid));
00779                         } else {
00780                                 maxise(&f1width[W_UID],
00781                                                 strlen(uidname(f->uid)));
00782                                 width+= 2;
00783                         }
00784                 }
00785                 if (doit) {
00786                         printf("%-*s  ", f1width[W_GID], gidname(f->gid));
00787                 } else {
00788                         maxise(&f1width[W_GID], strlen(gidname(f->gid)));
00789                         width+= 2;
00790                 }
00791 
00792                 switch (f->mode & S_IFMT) {
00793                 case S_IFBLK:
00794                 case S_IFCHR:
00795 #ifdef S_IFMPB
00796                 case S_IFMPB:
00797 #endif
00798 #ifdef S_IFMPC
00799                 case S_IFMPC:
00800 #endif
00801 #if __minix
00802                         if (doit) {
00803                                 printf("%*d, %3d ", f1width[W_SIZE] - 5,
00804                                         major(f->rdev), minor(f->rdev));
00805                         } else {
00806                                 maxise(&f1width[W_SIZE],
00807                                                 numwidth(major(f->rdev)) + 5);
00808                                 width++;
00809                         }
00810 #else /* !__minix */
00811                         if (doit) {
00812                                 printf("%*lX ", f1width[W_SIZE],
00813                                         (unsigned long) f->rdev);
00814                         } else {
00815                                 maxise(&f1width[W_SIZE], numwidth(f->rdev));
00816                                 width++;
00817                         }
00818 #endif /* !__minix */
00819                         break;
00820                 default:
00821                         if (field & L_KMG) {
00822                                 p= cxsize(f);
00823                                 n= strlen(p)+1;
00824 
00825                                 if (doit) {
00826                                         n= f1width[W_SIZE] - n;
00827                                         while (n > 0) { putchar(' '); --n; }
00828                                         printf("%s ", p);
00829                                 } else {
00830                                         maxise(&f1width[W_SIZE], n);
00831                                 }
00832                         } else {
00833                                 if (doit) {
00834                                         printf("%*lu ", f1width[W_SIZE],
00835                                                 (unsigned long) f->size);
00836                                 } else {
00837                                         maxise(&f1width[W_SIZE],
00838                                                 numwidth(f->size));
00839                                         width++;
00840                                 }
00841                         }
00842                 }
00843 
00844                 if (doit) {
00845                         printf("%s ", timestamp(f));
00846                 } else {
00847                         width+= (field & L_LONGTIME) ? 21 : 13;
00848                 }
00849         }
00850 
00851         n= strlen(f->name);
00852         if (doit) {
00853                 printname(f->name);
00854                 if (mark(f, 1) != 0) n++;
00855 #ifdef S_IFLNK
00856                 if ((field & L_LONG) && (f->mode & S_IFMT) == S_IFLNK) {
00857                         char *buf;
00858                         int r, didx;
00859 
00860                         buf= (char *) allocate(((size_t) f->size + 1)
00861                                                         * sizeof(buf[0]));
00862                         addpath(&didx, f->name);
00863                         r= readlink(path, buf, (int) f->size);
00864                         delpath(didx);
00865                         if (r > 0) buf[r] = 0; else r=1, strcpy(buf, "?");
00866                         printf(" -> ");
00867                         printname(buf);
00868                         free((void *) buf);
00869                         n+= 4 + r;
00870                 }
00871 #endif
00872                 spaces(f1width[W_NAME] - n);
00873         } else {
00874                 if (mark(f, 0) != 0) n++;
00875 #ifdef S_IFLNK
00876                 if ((field & L_LONG) && (f->mode & S_IFMT) == S_IFLNK) {
00877                         n+= 4 + (int) f->size;
00878                 }
00879 #endif
00880                 maxise(&f1width[W_NAME], n + NSEP);
00881 
00882                 for (n= 1; n < MAXFLDS; n++) width+= f1width[n];
00883                 maxise(&f1width[W_COL], width);
00884         }
00885 }
00886 
00887 int countfiles(struct file *flist)
00888 /* Return number of files in the list. */
00889 {
00890         int n= 0;
00891 
00892         while (flist != nil) { n++; flist= flist->next; }
00893 
00894         return n;
00895 }
00896 
00897 struct file *filecol[MAXCOLS];  /* filecol[i] is list of files for column i. */
00898 int nfiles, nlines;     /* # files to print, # of lines needed. */
00899 
00900 void columnise(struct file *flist, int nplin)
00901 /* Chop list of files up in columns.  Note that 3 columns are used for 5 files
00902  * even though nplin may be 4, filecol[3] will simply be nil.
00903  */
00904 {
00905         int i, j;
00906 
00907         nlines= (nfiles + nplin - 1) / nplin;   /* nlines needed for nfiles */
00908 
00909         filecol[0]= flist;
00910 
00911         for (i=1; i<nplin; i++) {       /* Give nlines files to each column. */
00912                 for (j=0; j<nlines && flist != nil; j++) flist= flist->next;
00913 
00914                 filecol[i]= flist;
00915         }
00916 }
00917 
00918 int print(struct file *flist, int nplin, int doit)
00919 /* Try (doit == 0), or really print the list of files over nplin columns.
00920  * Return true if it can be done in nplin columns or if nplin == 1.
00921  */
00922 {
00923         register struct file *f;
00924         register int col, fld, totlen;
00925 
00926         columnise(flist, nplin);
00927 
00928         if (!doit) {
00929                 for (col= 0; col < nplin; col++) {
00930                         for (fld= 0; fld < MAXFLDS; fld++) {
00931                                 fieldwidth[col][fld]= 0;
00932                         }
00933                 }
00934         }
00935 
00936         while (--nlines >= 0) {
00937                 totlen= 0;
00938 
00939                 for (col= 0; col < nplin; col++) {
00940                         if ((f= filecol[col]) != nil) {
00941                                 filecol[col]= f->next;
00942                                 print1(f, col, doit);
00943                         }
00944                         if (!doit && nplin > 1) {
00945                                 /* See if this line is not too long. */
00946                                 if (fieldwidth[col][W_COL] == UCHAR_MAX) {
00947                                         return 0;
00948                                 }
00949                                 totlen+= fieldwidth[col][W_COL];
00950                                 if (totlen > ncols+NSEP) return 0;
00951                         }
00952                 }
00953                 if (doit) terpri();
00954         }
00955         return 1;
00956 }
00957 
00958 enum depth { SURFACE, SURFACE1, SUBMERGED };
00959 enum state { BOTTOM, SINKING, FLOATING };
00960 
00961 void listfiles(struct file *flist, enum depth depth, enum state state)
00962 /* Main workhorse of ls, it sorts and prints the list of files.  Flags:
00963  * depth: working with the command line / just one file / listing dir.
00964  * state: How "recursive" do we have to be.
00965  */
00966 {
00967         struct file *dlist= nil, **afl= &flist, **adl= &dlist;
00968         int nplin;
00969         static int white = 1;   /* Nothing printed yet. */
00970 
00971         /* Flush everything previously printed, so new error output will
00972          * not intermix with files listed earlier.
00973          */
00974         fflush(stdout);
00975 
00976         if (field != 0 || state != BOTTOM) {    /* Need stat(2) info. */
00977                 while (*afl != nil) {
00978                         static struct stat st;
00979                         int r, didx;
00980 
00981                         addpath(&didx, (*afl)->name);
00982 
00983                         if ((r= status(path, &st)) < 0
00984 #ifdef S_IFLNK
00985                                 && (status == lstat || lstat(path, &st) < 0)
00986 #endif
00987                         ) {
00988                                 if (depth != SUBMERGED || errno != ENOENT)
00989                                         report((*afl)->name);
00990                                 delfile(popfile(afl));
00991                         } else {
00992                                 setstat(*afl, &st);
00993                                 afl= &(*afl)->next;
00994                         }
00995                         delpath(didx);
00996                 }
00997         }
00998         sort(&flist);
00999 
01000         if (depth == SUBMERGED && (field & (L_BLOCKS | L_LONG))) {
01001                 printf("total %ld\n", nblk2k(countblocks(flist)));
01002         }
01003 
01004         if (state == SINKING || depth == SURFACE1) {
01005         /* Don't list directories themselves, list their contents later. */
01006                 afl= &flist;
01007                 while (*afl != nil) {
01008                         if (((*afl)->mode & S_IFMT) == S_IFDIR) {
01009                                 pushfile(adl, popfile(afl));
01010                                 adl= &(*adl)->next;
01011                         } else {
01012                                 afl= &(*afl)->next;
01013                         }
01014                 }
01015         }
01016 
01017         if ((nfiles= countfiles(flist)) > 0) {
01018                 /* Print files in how many columns? */
01019                 nplin= !present('C') ? 1 : nfiles < MAXCOLS ? nfiles : MAXCOLS;
01020 
01021                 while (!print(flist, nplin, 0)) nplin--;        /* Try first */
01022 
01023                 print(flist, nplin, 1);         /* Then do it! */
01024                 white = 0;
01025         }
01026 
01027         while (flist != nil) {  /* Destroy file list */
01028                 if (state == FLOATING && (flist->mode & S_IFMT) == S_IFDIR) {
01029                         /* But keep these directories for ls -R. */
01030                         pushfile(adl, popfile(&flist));
01031                         adl= &(*adl)->next;
01032                 } else {
01033                         delfile(popfile(&flist));
01034                 }
01035         }
01036 
01037         while (dlist != nil) {  /* List directories */
01038                 if (dotflag(dlist->name) != 'a' || depth != SUBMERGED) {
01039                         int didx;
01040 
01041                         addpath(&didx, dlist->name);
01042 
01043                         flist= nil;
01044                         if (adddir(&flist, path)) {
01045                                 if (depth != SURFACE1) {
01046                                         if (!white) putchar('\n');
01047                                         printf("%s:\n", path);
01048                                         white = 0;
01049                                 }
01050                                 listfiles(flist, SUBMERGED,
01051                                         state == FLOATING ? FLOATING : BOTTOM);
01052                         }
01053                         delpath(didx);
01054                 }
01055                 delfile(popfile(&dlist));
01056         }
01057 }
01058 
01059 int main(int argc, char **argv)
01060 {
01061         struct file *flist= nil, **aflist= &flist;
01062         enum depth depth;
01063         char *lsflags;
01064         struct winsize ws;
01065 
01066         uid= geteuid();
01067         gid= getegid();
01068 
01069         if ((arg0= strrchr(argv[0], '/')) == nil) arg0= argv[0]; else arg0++;
01070         argv++;
01071 
01072         if (strcmp(arg0, "ls") != 0) {
01073                 char *p= arg0+1;
01074 
01075                 while (*p != 0) {
01076                         if (strchr(arg0flag, *p) != nil) *p += 'A' - 'a';
01077                         p++;
01078                 }
01079                 setflags(arg0+1);
01080         }
01081         while (*argv != nil && (*argv)[0] == '-') {
01082                 if ((*argv)[1] == '-' && (*argv)[2] == 0) {
01083                         argv++;
01084                         break;
01085                 }
01086                 setflags(*argv++ + 1);
01087         }
01088 
01089         istty= isatty(1);
01090 
01091         if (istty && (lsflags= getenv("LSOPTS")) != nil) {
01092                 if (*lsflags == '-') lsflags++;
01093                 setflags(lsflags);
01094         }
01095 
01096         if (!present('1') && !present('C') && !present('l')
01097                 && (istty || present('M') || present('X') || present('F'))
01098         ) setflags("C");
01099 
01100         if (istty) setflags("q");
01101 
01102         if (SUPER_ID == 0 || present('a')) setflags("A");
01103 
01104         if (present('i')) field|= L_INODE;
01105         if (present('s')) field|= L_BLOCKS;
01106         if (present('M')) field|= L_MODE;
01107         if (present('X')) field|= L_EXTRA | L_MODE;
01108         if (present('t')) field|= L_BYTIME;
01109         if (present('u')) field|= L_ATIME;
01110         if (present('c')) field|= L_CTIME;
01111         if (present('l')) field|= L_MODE | L_LONG;
01112         if (present('g')) field|= L_MODE | L_LONG | L_GROUP;
01113         if (present('F')) field|= L_MARK;
01114         if (present('p')) field|= L_MARKDIR;
01115         if (present('D')) field|= L_TYPE;
01116         if (present('T')) field|= L_MODE | L_LONG | L_LONGTIME;
01117         if (present('d')) field|= L_DIR;
01118         if (present('h')) field|= L_KMG;
01119         if (field & L_LONG) field&= ~L_EXTRA;
01120 
01121 #ifdef S_IFLNK
01122         status= present('L') ? stat : lstat;
01123 #endif
01124 
01125         if (present('C')) {
01126                 int t= istty ? 1 : open("/dev/tty", O_WRONLY);
01127 
01128                 if (t >= 0 && ioctl(t, TIOCGWINSZ, &ws) == 0 && ws.ws_col > 0)
01129                         ncols= ws.ws_col - 1;
01130 
01131                 if (t != 1 && t != -1) close(t);
01132         }
01133 
01134         depth= SURFACE;
01135 
01136         if (*argv == nil) {
01137                 if (!(field & L_DIR)) depth= SURFACE1;
01138                 pushfile(aflist, newfile("."));
01139         } else {
01140                 if (argv[1] == nil && !(field & L_DIR)) depth= SURFACE1;
01141 
01142                 do {
01143                         pushfile(aflist, newfile(*argv++));
01144                         aflist= &(*aflist)->next;
01145                 } while (*argv!=nil);
01146         }
01147         listfiles(flist, depth,
01148                 (field & L_DIR) ? BOTTOM : present('R') ? FLOATING : SINKING);
01149         return ex;
01150 }

Generated on Fri Apr 14 22:57:08 2006 for minix by  doxygen 1.4.6