00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
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
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
00055 #if __minix
00056 #define BLOCK 1024
00057 #else
00058 #define BLOCK 512
00059 #endif
00060
00061
00062 #if !__minix
00063 #define ST_BLOCKS 1
00064 #endif
00065
00066
00067
00068
00069
00070 int ncols= 79;
00071
00072 #define NSEP 3
00073
00074 #define MAXCOLS 128
00075
00076 char *arg0;
00077 int uid, gid;
00078 int ex= 0;
00079 int istty;
00080
00081
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
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";
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
00132 {
00133 fprintf(stderr, "%s: %s: %s\n", arg0, f, strerror(errno));
00134 ex= 1;
00135 }
00136
00137
00138
00139
00140 #define NNAMES (1 << (sizeof(int) + sizeof(char *)))
00141 enum whatmap { PASSWD, GROUP };
00142
00143 struct idname {
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
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
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
00175 sprintf(noname, "%u", id);
00176 name= noname;
00177 }
00178
00179
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
00194
00195
00196
00197 char *path;
00198 int plen= 0, pidx= 0;
00199
00200 void addpath(int *didx, char *name)
00201
00202
00203
00204 {
00205 if (plen == 0) path= (char *) allocate((plen= 32) * sizeof(path[0]));
00206
00207 if (pidx == 1 && path[0] == '.') pidx= 0;
00208
00209 *didx= pidx;
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;
00224
00225
00226 }
00227
00228 #define delpath(didx) (path[pidx= didx]= 0)
00229
00230 int field = 0;
00231
00232
00233 #define L_INODE 0x0001
00234 #define L_BLOCKS 0x0002
00235 #define L_EXTRA 0x0004
00236 #define L_MODE 0x0008
00237 #define L_LONG 0x0010
00238 #define L_GROUP 0x0020
00239 #define L_BYTIME 0x0040
00240 #define L_ATIME 0x0080
00241 #define L_CTIME 0x0100
00242 #define L_MARK 0x0200
00243 #define L_MARKDIR 0x0400
00244 #define L_TYPE 0x0800
00245 #define L_LONGTIME 0x1000
00246 #define L_DIR 0x2000
00247 #define L_KMG 0x4000
00248
00249 struct file {
00250 struct file *next;
00251 char *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)
00285
00286 #define FUTURE ( 1*7*24*3600L)
00287
00288 static char *timestamp(struct file *f)
00289
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; }
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
00328 {
00329 static char rwx[] = "drwxr-x--x";
00330
00331 rwx[0] = ifmt(f->mode);
00332
00333
00334
00335
00336
00337 if (field & L_EXTRA) {
00338 int mode = f->mode, ucase= 0;
00339
00340 if (uid == f->uid) {
00341
00342 ucase= (mode<<3) | (mode<<6);
00343
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 {
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
00390 #define T 1000L
00391
00392 char *cxsize(struct file *f)
00393
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) {
00402 numeral((int) f->size, &p);
00403 return siz;
00404 }
00405 z= (f->size + K-1) / K;
00406
00407 if (z <= 999) {
00408 numeral((int) z, &p);
00409 *p = 'k';
00410 } else
00411 if (z*10 <= 99*T) {
00412 z= (z*10 + T-1) / T;
00413 numeral((int) z / 10, &p);
00414 *p++ = '.';
00415 numeral((int) z % 10, &p);
00416 *p = 'm';
00417 } else
00418 if (z <= 999*T) {
00419 numeral((int) ((z + T-1) / T), &p);
00420 *p = 'm';
00421 } else {
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
00432
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
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
00452
00453
00454 {
00455 struct file *l1, **mid;
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
00515 {
00516 if (!present('f') && *al != nil && (*al)->next != nil) {
00517 CMP= namecmp;
00518
00519 if (!(field & L_BYTIME)) {
00520
00521
00522 if (present('r')) { rCMP= CMP; CMP= revcmp; }
00523 mergesort(al);
00524 } else {
00525
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
00541
00542 if (field & L_TYPE) {
00543 CMP= typecmp;
00544 mergesort(al);
00545 }
00546 }
00547 }
00548
00549 struct file *newfile(char *name)
00550
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
00561 {
00562 new->next= *flist;
00563 *flist= new;
00564 }
00565
00566 void delfile(struct file *old)
00567
00568 {
00569 free((void *) old->name);
00570 free((void *) old);
00571 }
00572
00573 struct file *popfile(struct file **flist)
00574
00575 {
00576 struct file *f;
00577
00578 f= *flist;
00579 *flist= f->next;
00580 return f;
00581 }
00582
00583 int dotflag(char *name)
00584
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
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
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
00641
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
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
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
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
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;
00718 #define spaces(n) (nsp= (n))
00719 #define terpri() (nsp= 0, putchar('\n'))
00720
00721 void print1(struct file *f, int col, int doit)
00722
00723
00724
00725 {
00726 int width= 0, n;
00727 char *p;
00728 unsigned char *f1width = fieldwidth[col];
00729
00730 while (nsp>0) { putchar(' '); nsp--; }
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
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
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
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];
00898 int nfiles, nlines;
00899
00900 void columnise(struct file *flist, int nplin)
00901
00902
00903
00904 {
00905 int i, j;
00906
00907 nlines= (nfiles + nplin - 1) / nplin;
00908
00909 filecol[0]= flist;
00910
00911 for (i=1; i<nplin; i++) {
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
00920
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
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
00963
00964
00965
00966 {
00967 struct file *dlist= nil, **afl= &flist, **adl= &dlist;
00968 int nplin;
00969 static int white = 1;
00970
00971
00972
00973
00974 fflush(stdout);
00975
00976 if (field != 0 || state != BOTTOM) {
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
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
01019 nplin= !present('C') ? 1 : nfiles < MAXCOLS ? nfiles : MAXCOLS;
01020
01021 while (!print(flist, nplin, 0)) nplin--;
01022
01023 print(flist, nplin, 1);
01024 white = 0;
01025 }
01026
01027 while (flist != nil) {
01028 if (state == FLOATING && (flist->mode & S_IFMT) == S_IFDIR) {
01029
01030 pushfile(adl, popfile(&flist));
01031 adl= &(*adl)->next;
01032 } else {
01033 delfile(popfile(&flist));
01034 }
01035 }
01036
01037 while (dlist != nil) {
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 }