1 /* ls 5.4 - List files. Author: Kees J. Bot
4 * About the amount of bytes for heap + stack under Minix:
5 * Ls needs a average amount of 42 bytes per unserviced directory entry, so
6 * scanning 10 directory levels deep in an ls -R with 100 entries per directory
7 * takes 42000 bytes of heap. So giving ls 10000 bytes is tight, 20000 is
8 * usually enough, 40000 is pessimistic.
11 /* The array l_ifmt[] is used in an 'ls -l' to map the type of a file to a
12 * letter. This is done so that ls can list any future file or device type
13 * other than symlinks, without recompilation. (Yes it's dirty.)
15 char l_ifmt
[] = "0pcCd?bB-?l?s???";
17 #define ifmt(mode) l_ifmt[((mode) >> 12) & 0xF]
22 #include <sys/types.h>
35 #include <sys/ioctl.h>
38 #define major(dev) ((int) (((dev) >> 8) & 0xFF))
39 #define minor(dev) ((int) (((dev) >> 0) & 0xFF))
43 #define SUPER_ID uid /* Let -A flag be default for SUPER_ID == 0. */
49 int (*status
)(const char *file
, struct stat
*stp
);
54 /* Basic disk block size is 512 except for one niche O.S. */
61 /* Assume other systems have st_blocks. */
66 /* Some terminals ignore more than 80 characters on a line. Dumb ones wrap
67 * when the cursor hits the side. Nice terminals don't wrap until they have
68 * to print the 81st character. Whether we like it or not, no column 80.
72 #define NSEP 3 /* # spaces between columns. */
74 #define MAXCOLS 128 /* Max # of files per line. */
76 char *arg0
; /* Last component of argv[0]. */
77 int uid
, gid
; /* callers id. */
78 int ex
= 0; /* Exit status to be. */
79 int istty
; /* Output is on a terminal. */
81 /* Safer versions of malloc and realloc: */
85 fprintf(stderr
, "%s: Out of memory\n", arg0
);
89 void *allocate(size_t n
)
94 if ((a
= malloc(n
)) == nil
) heaperr();
98 void *reallocate(void *a
, size_t n
)
100 if ((a
= realloc(a
, n
)) == nil
) heaperr();
104 char allowed
[] = "acdfghilnpqrstu1ACDFLMRTX";
105 char flags
[sizeof(allowed
)];
107 char arg0flag
[] = "cdfmrtx"; /* These in argv[0] go to upper case. */
109 void setflags(char *flgs
)
113 while ((c
= *flgs
++) != 0) {
114 if (strchr(allowed
, c
) == nil
) {
115 fprintf(stderr
, "Usage: %s [-%s] [file ...]\n",
119 if (strchr(flags
, c
) == nil
) {
120 flags
[strlen(flags
)] = c
;
127 return f
== 0 || strchr(flags
, f
) != nil
;
131 /* Like perror(3), but in the style: "ls: junk: No such file or directory. */
133 fprintf(stderr
, "%s: %s: %s\n", arg0
, f
, strerror(errno
));
137 /* Two functions, uidname and gidname, translate id's to readable names.
138 * All names are remembered to avoid searching the password file.
140 #define NNAMES (1 << (sizeof(int) + sizeof(char *)))
141 enum whatmap
{ PASSWD
, GROUP
};
143 struct idname
{ /* Hash list of names. */
147 } *uids
[NNAMES
], *gids
[NNAMES
];
149 char *idname(unsigned id
, enum whatmap map
)
150 /* Return name for a given user/group id. */
153 struct idname
**ids
= &(map
== PASSWD
? uids
: gids
)[id
% NNAMES
];
155 while ((i
= *ids
) != nil
&& id
< i
->id
) ids
= &i
->next
;
157 if (i
== nil
|| id
!= i
->id
) {
158 /* Not found, go look in the password or group map. */
160 char noname
[3 * sizeof(uid_t
)];
164 struct passwd
*pw
= getpwuid(id
);
166 if (pw
!= nil
) name
= pw
->pw_name
;
168 struct group
*gr
= getgrgid(id
);
170 if (gr
!= nil
) name
= gr
->gr_name
;
174 /* Can't find it, weird. Use numerical "name." */
175 sprintf(noname
, "%u", id
);
179 /* Add a new id-to-name cell. */
180 i
= allocate(sizeof(*i
));
182 i
->name
= allocate(strlen(name
) + 1);
183 strcpy(i
->name
, name
);
190 #define uidname(uid) idname((uid), PASSWD)
191 #define gidname(gid) idname((gid), GROUP)
193 /* Path name construction, addpath adds a component, delpath removes it.
194 * The string path is used throughout the program as the file under examination.
197 char *path
; /* Path name constructed in path[]. */
198 int plen
= 0, pidx
= 0; /* Lenght/index for path[]. */
200 void addpath(int *didx
, char *name
)
201 /* Add a component to path. (name may also be a full path at the first call)
202 * The index where the current path ends is stored in *pdi.
205 if (plen
== 0) path
= (char *) allocate((plen
= 32) * sizeof(path
[0]));
207 if (pidx
== 1 && path
[0] == '.') pidx
= 0; /* Remove "." */
209 *didx
= pidx
; /* Record point to go back to for delpath. */
211 if (pidx
> 0 && path
[pidx
-1] != '/') path
[pidx
++]= '/';
214 if (*name
!= '/' || pidx
== 0 || path
[pidx
-1] != '/') {
216 path
= (char *) reallocate((void *) path
,
217 (plen
*= 2) * sizeof(path
[0]));
221 } while (*name
++ != 0);
223 --pidx
; /* Put pidx back at the null. The path[pidx++]= '/'
224 * statement will overwrite it at the next call.
228 #define delpath(didx) (path[pidx= didx]= 0) /* Remove component. */
230 int field
= 0; /* (used to be) Fields that must be printed. */
231 /* (now) Effects triggered by certain flags. */
233 #define L_INODE 0x0001 /* -i */
234 #define L_BLOCKS 0x0002 /* -s */
235 #define L_EXTRA 0x0004 /* -X */
236 #define L_MODE 0x0008 /* -lMX */
237 #define L_LONG 0x0010 /* -l */
238 #define L_GROUP 0x0020 /* -g */
239 #define L_BYTIME 0x0040 /* -tuc */
240 #define L_ATIME 0x0080 /* -u */
241 #define L_CTIME 0x0100 /* -c */
242 #define L_MARK 0x0200 /* -F */
243 #define L_MARKDIR 0x0400 /* -p */
244 #define L_TYPE 0x0800 /* -D */
245 #define L_LONGTIME 0x1000 /* -T */
246 #define L_DIR 0x2000 /* -d */
247 #define L_KMG 0x4000 /* -h */
249 struct file
{ /* A file plus stat(2) information. */
250 struct file
*next
; /* Lists are made of them. */
251 char *name
; /* Null terminated name. */
267 void setstat(struct file
*f
, struct stat
*stp
)
270 f
->mode
= stp
->st_mode
;
271 f
->nlink
= stp
->st_nlink
;
274 f
->rdev
= stp
->st_rdev
;
275 f
->size
= stp
->st_size
;
276 f
->mtime
= stp
->st_mtime
;
277 f
->atime
= stp
->st_atime
;
278 f
->ctime
= stp
->st_ctime
;
280 f
->blocks
= stp
->st_blocks
;
284 #define PAST (26*7*24*3600L) /* Half a year ago. */
285 /* Between PAST and FUTURE from now a time is printed, otherwise a year. */
286 #define FUTURE ( 1*7*24*3600L) /* One week. */
288 static char *timestamp(struct file
*f
)
289 /* Transform the right time field into something readable. */
295 static char date
[] = "Jan 19 03:14:07 2038";
296 static char month
[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
299 if (field
& L_ATIME
) t
= f
->atime
;
300 if (field
& L_CTIME
) t
= f
->ctime
;
303 if (--drift
< 0) { time(&now
); drift
= 50; } /* limit time() calls */
305 if (field
& L_LONGTIME
) {
306 sprintf(date
, "%.3s %2d %02d:%02d:%02d %d",
307 month
+ 3*tm
->tm_mon
,
309 tm
->tm_hour
, tm
->tm_min
, tm
->tm_sec
,
312 if (t
< now
- PAST
|| t
> now
+ FUTURE
) {
313 sprintf(date
, "%.3s %2d %d",
314 month
+ 3*tm
->tm_mon
,
318 sprintf(date
, "%.3s %2d %02d:%02d",
319 month
+ 3*tm
->tm_mon
,
321 tm
->tm_hour
, tm
->tm_min
);
326 char *permissions(struct file
*f
)
327 /* Compute long or short rwx bits. */
329 static char rwx
[] = "drwxr-x--x";
331 rwx
[0] = ifmt(f
->mode
);
332 /* Note that rwx[0] is a guess for the more alien file types. It is
333 * correct for BSD4.3 and derived systems. I just don't know how
334 * "standardized" these numbers are.
337 if (field
& L_EXTRA
) { /* Short style */
338 int mode
= f
->mode
, ucase
= 0;
340 if (uid
== f
->uid
) { /* What group of bits to use. */
342 ucase
= (mode
<<3) | (mode
<<6);
343 /* Remember if group or others have permissions. */
350 rwx
[1]= mode
&S_IRUSR
? (ucase
&S_IRUSR
? 'R' : 'r') : '-';
351 rwx
[2]= mode
&S_IWUSR
? (ucase
&S_IWUSR
? 'W' : 'w') : '-';
354 static char sbit
[]= { 'x', 'g', 'u', 's' };
356 rwx
[3]= sbit
[(f
->mode
&(S_ISUID
|S_ISGID
))>>10];
357 if (ucase
&S_IXUSR
) rwx
[3] += 'A'-'a';
359 rwx
[3]= f
->mode
&(S_ISUID
|S_ISGID
) ? '=' : '-';
362 } else { /* Long form. */
367 p
[0] = (mode
& S_IRUSR
) ? 'r' : '-';
368 p
[1] = (mode
& S_IWUSR
) ? 'w' : '-';
369 p
[2] = (mode
& S_IXUSR
) ? 'x' : '-';
371 } while ((p
+=3) <= rwx
+7);
373 if (f
->mode
&S_ISUID
) rwx
[3]= f
->mode
&(S_IXUSR
>>0) ? 's' : '=';
374 if (f
->mode
&S_ISGID
) rwx
[6]= f
->mode
&(S_IXUSR
>>3) ? 's' : '=';
375 if (f
->mode
&S_ISVTX
) rwx
[9]= f
->mode
&(S_IXUSR
>>6) ? 't' : '=';
380 void numeral(int i
, char **pp
)
382 char itoa
[3*sizeof(int)], *a
=itoa
;
384 do *a
++ = i
%10 + '0'; while ((i
/=10) > 0);
386 do *(*pp
)++ = *--a
; while (a
>itoa
);
389 #define K 1024L /* A kilobyte counts in multiples of K */
390 #define T 1000L /* A megabyte in T*K, a gigabyte in T*T*K */
392 char *cxsize(struct file
*f
)
393 /* Try and fail to turn a 32 bit size into 4 readable characters. */
395 static char siz
[] = "1.2m";
399 siz
[1]= siz
[2]= siz
[3]= 0;
401 if (f
->size
<= 5*K
) { /* <= 5K prints as is. */
402 numeral((int) f
->size
, &p
);
405 z
= (f
->size
+ K
-1) / K
;
407 if (z
<= 999) { /* Print as 123k. */
408 numeral((int) z
, &p
);
409 *p
= 'k'; /* Can't use 'K', looks bad */
411 if (z
*10 <= 99*T
) { /* 1.2m (Try ls -X /dev/at0) */
412 z
= (z
*10 + T
-1) / T
; /* Force roundup */
413 numeral((int) z
/ 10, &p
);
415 numeral((int) z
% 10, &p
);
418 if (z
<= 999*T
) { /* 123m */
419 numeral((int) ((z
+ T
-1) / T
), &p
);
422 z
= (z
*10 + T
*T
-1) / (T
*T
);
423 numeral((int) z
/ 10, &p
);
425 numeral((int) z
% 10, &p
);
431 /* Transform size of file to number of blocks. This was once a function that
432 * guessed the number of indirect blocks, but that nonsense has been removed.
435 #define nblocks(f) ((f)->blocks)
437 #define nblocks(f) (((f)->size + BLOCK-1) / BLOCK)
440 /* From number of blocks to kilobytes. */
442 #define nblk2k(nb) (((nb) + (1024 / BLOCK - 1)) / (1024 / BLOCK))
444 #define nblk2k(nb) ((nb) * (BLOCK / 1024))
447 static int (*CMP
)(struct file
*f1
, struct file
*f2
);
448 static int (*rCMP
)(struct file
*f1
, struct file
*f2
);
450 static void mergesort(struct file
**al
)
451 /* This is either a stable mergesort, or thermal noise, I'm no longer sure.
452 * It must be called like this: if (L != nil && L->next != nil) mergesort(&L);
455 /* static */ struct file
*l1
, **mid
; /* Need not be local */
458 l1
= *(mid
= &(*al
)->next
);
460 if ((l1
= l1
->next
) == nil
) break;
462 } while ((l1
= l1
->next
) != nil
);
467 if ((*al
)->next
!= nil
) mergesort(al
);
468 if (l2
->next
!= nil
) mergesort(&l2
);
472 if ((*CMP
)(l1
, l2
) <= 0) {
473 if ((l1
= *(al
= &l1
->next
)) == nil
) {
479 l2
= *(al
= &l2
->next
);
481 if (l2
== nil
) break;
486 int namecmp(struct file
*f1
, struct file
*f2
)
488 return strcmp(f1
->name
, f2
->name
);
491 int mtimecmp(struct file
*f1
, struct file
*f2
)
493 return f1
->mtime
== f2
->mtime
? 0 : f1
->mtime
> f2
->mtime
? -1 : 1;
496 int atimecmp(struct file
*f1
, struct file
*f2
)
498 return f1
->atime
== f2
->atime
? 0 : f1
->atime
> f2
->atime
? -1 : 1;
501 int ctimecmp(struct file
*f1
, struct file
*f2
)
503 return f1
->ctime
== f2
->ctime
? 0 : f1
->ctime
> f2
->ctime
? -1 : 1;
506 int typecmp(struct file
*f1
, struct file
*f2
)
508 return ifmt(f1
->mode
) - ifmt(f2
->mode
);
511 int revcmp(struct file
*f1
, struct file
*f2
) { return (*rCMP
)(f2
, f1
); }
513 static void sort(struct file
**al
)
514 /* Sort the files according to the flags. */
516 if (!present('f') && *al
!= nil
&& (*al
)->next
!= nil
) {
519 if (!(field
& L_BYTIME
)) {
522 if (present('r')) { rCMP
= CMP
; CMP
= revcmp
; }
525 /* Sort on name first, then sort on time. */
528 if (field
& L_CTIME
) {
531 if (field
& L_ATIME
) {
537 if (present('r')) { rCMP
= CMP
; CMP
= revcmp
; }
540 /* Separate by file type if so desired. */
542 if (field
& L_TYPE
) {
549 struct file
*newfile(char *name
)
550 /* Create file structure for given name. */
554 new= (struct file
*) allocate(sizeof(*new));
555 new->name
= strcpy((char *) allocate(strlen(name
)+1), name
);
559 void pushfile(struct file
**flist
, struct file
*new)
560 /* Add file to the head of a list. */
566 void delfile(struct file
*old
)
567 /* Release old file structure. */
569 free((void *) old
->name
);
573 struct file
*popfile(struct file
**flist
)
574 /* Pop file off top of file list. */
583 int dotflag(char *name
)
584 /* Return flag that would make ls list this name: -a or -A. */
586 if (*name
++ != '.') return 0;
589 case 0: return 'a'; /* "." */
590 case '.': if (*name
== 0) return 'a'; /* ".." */
591 default: return 'A'; /* ".*" */
595 int adddir(struct file
**aflist
, char *name
)
596 /* Add directory entries of directory name to a file list. */
601 if (access(name
, 0) < 0) {
606 if ((d
= opendir(name
)) == nil
) {
610 while ((e
= readdir(d
)) != nil
) {
611 if (e
->d_ino
!= 0 && present(dotflag(e
->d_name
))) {
612 pushfile(aflist
, newfile(e
->d_name
));
613 aflist
= &(*aflist
)->next
;
620 off_t
countblocks(struct file
*flist
)
621 /* Compute total block count for a list of files. */
625 while (flist
!= nil
) {
626 switch (flist
->mode
& S_IFMT
) {
632 cb
+= nblocks(flist
);
639 void printname(char *name
)
640 /* Print a name with control characters as '?' (unless -q). The terminal is
641 * assumed to be eight bit clean.
644 int c
, q
= present('q');
646 while ((c
= (unsigned char) *name
++) != 0) {
647 if (q
&& (c
< ' ' || c
== 0177)) c
= '?';
652 int mark(struct file
*f
, int doit
)
658 if (field
& L_MARK
) {
659 switch (f
->mode
& S_IFMT
) {
660 case S_IFDIR
: c
= '/'; break;
662 case S_IFIFO
: c
= '|'; break;
665 case S_IFLNK
: c
= '@'; break;
668 case S_IFSOCK
: c
= '='; break;
671 if (f
->mode
& (S_IXUSR
| S_IXGRP
| S_IXOTH
)) c
= '*';
675 if (field
& L_MARKDIR
) {
676 if (S_ISDIR(f
->mode
)) c
= '/';
679 if (doit
&& c
!= 0) putchar(c
);
683 /* Width of entire column, and of several fields. */
684 enum { W_COL
, W_INO
, W_BLK
, W_NLINK
, W_UID
, W_GID
, W_SIZE
, W_NAME
, MAXFLDS
};
686 unsigned char fieldwidth
[MAXCOLS
][MAXFLDS
];
688 void maxise(unsigned char *aw
, int w
)
689 /* Set *aw to the larger of it and w. */
692 if (w
> UCHAR_MAX
) w
= UCHAR_MAX
;
697 int numwidth(unsigned long n
)
698 /* Compute width of 'n' when printed. */
702 do { width
++; } while ((n
/= 10) > 0);
707 int numxwidth(unsigned long n
)
708 /* Compute width of 'n' when printed in hex. */
712 do { width
++; } while ((n
/= 16) > 0);
717 static int nsp
= 0; /* This many spaces have not been printed yet. */
718 #define spaces(n) (nsp= (n))
719 #define terpri() (nsp= 0, putchar('\n')) /* No trailing spaces */
721 void print1(struct file
*f
, int col
, int doit
)
722 /* Either compute the number of spaces needed to print file f (doit == 0) or
723 * really print it (doit == 1).
728 unsigned char *f1width
= fieldwidth
[col
];
730 while (nsp
>0) { putchar(' '); nsp
--; }/* Fill gap between two columns */
732 if (field
& L_INODE
) {
734 printf("%*d ", f1width
[W_INO
], f
->ino
);
736 maxise(&f1width
[W_INO
], numwidth(f
->ino
));
740 if (field
& L_BLOCKS
) {
741 unsigned long nb
= nblk2k(nblocks(f
));
743 printf("%*lu ", f1width
[W_BLK
], nb
);
745 maxise(&f1width
[W_BLK
], numwidth(nb
));
749 if (field
& L_MODE
) {
751 printf("%s ", permissions(f
));
753 width
+= (field
& L_EXTRA
) ? 5 : 11;
756 if (field
& L_EXTRA
) {
761 n
= f1width
[W_SIZE
] - n
;
762 while (n
> 0) { putchar(' '); --n
; }
765 maxise(&f1width
[W_SIZE
], n
);
768 if (field
& L_LONG
) {
770 printf("%*u ", f1width
[W_NLINK
], (unsigned) f
->nlink
);
772 maxise(&f1width
[W_NLINK
], numwidth(f
->nlink
));
775 if (!(field
& L_GROUP
)) {
777 printf("%-*s ", f1width
[W_UID
],
780 maxise(&f1width
[W_UID
],
781 strlen(uidname(f
->uid
)));
786 printf("%-*s ", f1width
[W_GID
], gidname(f
->gid
));
788 maxise(&f1width
[W_GID
], strlen(gidname(f
->gid
)));
792 switch (f
->mode
& S_IFMT
) {
803 printf("%*d, %3d ", f1width
[W_SIZE
] - 5,
804 major(f
->rdev
), minor(f
->rdev
));
806 maxise(&f1width
[W_SIZE
],
807 numwidth(major(f
->rdev
)) + 5);
812 printf("%*lX ", f1width
[W_SIZE
],
813 (unsigned long) f
->rdev
);
815 maxise(&f1width
[W_SIZE
], numwidth(f
->rdev
));
818 #endif /* !__minix */
826 n
= f1width
[W_SIZE
] - n
;
827 while (n
> 0) { putchar(' '); --n
; }
830 maxise(&f1width
[W_SIZE
], n
);
834 printf("%*lu ", f1width
[W_SIZE
],
835 (unsigned long) f
->size
);
837 maxise(&f1width
[W_SIZE
],
845 printf("%s ", timestamp(f
));
847 width
+= (field
& L_LONGTIME
) ? 21 : 13;
854 if (mark(f
, 1) != 0) n
++;
856 if ((field
& L_LONG
) && (f
->mode
& S_IFMT
) == S_IFLNK
) {
860 buf
= (char *) allocate(((size_t) f
->size
+ 1)
862 addpath(&didx
, f
->name
);
863 r
= readlink(path
, buf
, (int) f
->size
);
865 if (r
> 0) buf
[r
] = 0; else r
=1, strcpy(buf
, "?");
872 spaces(f1width
[W_NAME
] - n
);
874 if (mark(f
, 0) != 0) n
++;
876 if ((field
& L_LONG
) && (f
->mode
& S_IFMT
) == S_IFLNK
) {
877 n
+= 4 + (int) f
->size
;
880 maxise(&f1width
[W_NAME
], n
+ NSEP
);
882 for (n
= 1; n
< MAXFLDS
; n
++) width
+= f1width
[n
];
883 maxise(&f1width
[W_COL
], width
);
887 int countfiles(struct file
*flist
)
888 /* Return number of files in the list. */
892 while (flist
!= nil
) { n
++; flist
= flist
->next
; }
897 struct file
*filecol
[MAXCOLS
]; /* filecol[i] is list of files for column i. */
898 int nfiles
, nlines
; /* # files to print, # of lines needed. */
900 void columnise(struct file
*flist
, int nplin
)
901 /* Chop list of files up in columns. Note that 3 columns are used for 5 files
902 * even though nplin may be 4, filecol[3] will simply be nil.
907 nlines
= (nfiles
+ nplin
- 1) / nplin
; /* nlines needed for nfiles */
911 for (i
=1; i
<nplin
; i
++) { /* Give nlines files to each column. */
912 for (j
=0; j
<nlines
&& flist
!= nil
; j
++) flist
= flist
->next
;
918 int print(struct file
*flist
, int nplin
, int doit
)
919 /* Try (doit == 0), or really print the list of files over nplin columns.
920 * Return true if it can be done in nplin columns or if nplin == 1.
923 register struct file
*f
;
924 register int col
, fld
, totlen
;
926 columnise(flist
, nplin
);
929 for (col
= 0; col
< nplin
; col
++) {
930 for (fld
= 0; fld
< MAXFLDS
; fld
++) {
931 fieldwidth
[col
][fld
]= 0;
936 while (--nlines
>= 0) {
939 for (col
= 0; col
< nplin
; col
++) {
940 if ((f
= filecol
[col
]) != nil
) {
941 filecol
[col
]= f
->next
;
942 print1(f
, col
, doit
);
944 if (!doit
&& nplin
> 1) {
945 /* See if this line is not too long. */
946 if (fieldwidth
[col
][W_COL
] == UCHAR_MAX
) {
949 totlen
+= fieldwidth
[col
][W_COL
];
950 if (totlen
> ncols
+NSEP
) return 0;
958 enum depth
{ SURFACE
, SURFACE1
, SUBMERGED
};
959 enum state
{ BOTTOM
, SINKING
, FLOATING
};
961 void listfiles(struct file
*flist
, enum depth depth
, enum state state
)
962 /* Main workhorse of ls, it sorts and prints the list of files. Flags:
963 * depth: working with the command line / just one file / listing dir.
964 * state: How "recursive" do we have to be.
967 struct file
*dlist
= nil
, **afl
= &flist
, **adl
= &dlist
;
969 static int white
= 1; /* Nothing printed yet. */
971 /* Flush everything previously printed, so new error output will
972 * not intermix with files listed earlier.
976 if (field
!= 0 || state
!= BOTTOM
) { /* Need stat(2) info. */
977 while (*afl
!= nil
) {
978 static struct stat st
;
981 addpath(&didx
, (*afl
)->name
);
983 if ((r
= status(path
, &st
)) < 0
985 && (status
== lstat
|| lstat(path
, &st
) < 0)
988 if (depth
!= SUBMERGED
|| errno
!= ENOENT
)
989 report((*afl
)->name
);
990 delfile(popfile(afl
));
1000 if (depth
== SUBMERGED
&& (field
& (L_BLOCKS
| L_LONG
))) {
1001 printf("total %ld\n", nblk2k(countblocks(flist
)));
1004 if (state
== SINKING
|| depth
== SURFACE1
) {
1005 /* Don't list directories themselves, list their contents later. */
1007 while (*afl
!= nil
) {
1008 if (((*afl
)->mode
& S_IFMT
) == S_IFDIR
) {
1009 pushfile(adl
, popfile(afl
));
1017 if ((nfiles
= countfiles(flist
)) > 0) {
1018 /* Print files in how many columns? */
1019 nplin
= !present('C') ? 1 : nfiles
< MAXCOLS
? nfiles
: MAXCOLS
;
1021 while (!print(flist
, nplin
, 0)) nplin
--; /* Try first */
1023 print(flist
, nplin
, 1); /* Then do it! */
1027 while (flist
!= nil
) { /* Destroy file list */
1028 if (state
== FLOATING
&& (flist
->mode
& S_IFMT
) == S_IFDIR
) {
1029 /* But keep these directories for ls -R. */
1030 pushfile(adl
, popfile(&flist
));
1033 delfile(popfile(&flist
));
1037 while (dlist
!= nil
) { /* List directories */
1038 if (dotflag(dlist
->name
) != 'a' || depth
!= SUBMERGED
) {
1041 addpath(&didx
, dlist
->name
);
1044 if (adddir(&flist
, path
)) {
1045 if (depth
!= SURFACE1
) {
1046 if (!white
) putchar('\n');
1047 printf("%s:\n", path
);
1050 listfiles(flist
, SUBMERGED
,
1051 state
== FLOATING
? FLOATING
: BOTTOM
);
1055 delfile(popfile(&dlist
));
1059 int main(int argc
, char **argv
)
1061 struct file
*flist
= nil
, **aflist
= &flist
;
1069 if ((arg0
= strrchr(argv
[0], '/')) == nil
) arg0
= argv
[0]; else arg0
++;
1072 if (strcmp(arg0
, "ls") != 0) {
1076 if (strchr(arg0flag
, *p
) != nil
) *p
+= 'A' - 'a';
1081 while (*argv
!= nil
&& (*argv
)[0] == '-') {
1082 if ((*argv
)[1] == '-' && (*argv
)[2] == 0) {
1086 setflags(*argv
++ + 1);
1091 if (istty
&& (lsflags
= getenv("LSOPTS")) != nil
) {
1092 if (*lsflags
== '-') lsflags
++;
1096 if (!present('1') && !present('C') && !present('l')
1097 && (istty
|| present('M') || present('X') || present('F'))
1100 if (istty
) setflags("q");
1102 if (SUPER_ID
== 0 || present('a')) setflags("A");
1104 if (present('i')) field
|= L_INODE
;
1105 if (present('s')) field
|= L_BLOCKS
;
1106 if (present('M')) field
|= L_MODE
;
1107 if (present('X')) field
|= L_EXTRA
| L_MODE
;
1108 if (present('t')) field
|= L_BYTIME
;
1109 if (present('u')) field
|= L_ATIME
;
1110 if (present('c')) field
|= L_CTIME
;
1111 if (present('l')) field
|= L_MODE
| L_LONG
;
1112 if (present('g')) field
|= L_MODE
| L_LONG
| L_GROUP
;
1113 if (present('F')) field
|= L_MARK
;
1114 if (present('p')) field
|= L_MARKDIR
;
1115 if (present('D')) field
|= L_TYPE
;
1116 if (present('T')) field
|= L_MODE
| L_LONG
| L_LONGTIME
;
1117 if (present('d')) field
|= L_DIR
;
1118 if (present('h')) field
|= L_KMG
;
1119 if (field
& L_LONG
) field
&= ~L_EXTRA
;
1122 status
= present('L') ? stat
: lstat
;
1126 int t
= istty
? 1 : open("/dev/tty", O_WRONLY
);
1128 if (t
>= 0 && ioctl(t
, TIOCGWINSZ
, &ws
) == 0 && ws
.ws_col
> 0)
1129 ncols
= ws
.ws_col
- 1;
1131 if (t
!= 1 && t
!= -1) close(t
);
1137 if (!(field
& L_DIR
)) depth
= SURFACE1
;
1138 pushfile(aflist
, newfile("."));
1140 if (argv
[1] == nil
&& !(field
& L_DIR
)) depth
= SURFACE1
;
1143 pushfile(aflist
, newfile(*argv
++));
1144 aflist
= &(*aflist
)->next
;
1145 } while (*argv
!=nil
);
1147 listfiles(flist
, depth
,
1148 (field
& L_DIR
) ? BOTTOM
: present('R') ? FLOATING
: SINKING
);