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
);
451 #define mergesort _ls_mergesort
454 static void mergesort(struct file
**al
)
455 /* This is either a stable mergesort, or thermal noise, I'm no longer sure.
456 * It must be called like this: if (L != nil && L->next != nil) mergesort(&L);
459 /* static */ struct file
*l1
, **mid
; /* Need not be local */
462 l1
= *(mid
= &(*al
)->next
);
464 if ((l1
= l1
->next
) == nil
) break;
466 } while ((l1
= l1
->next
) != nil
);
471 if ((*al
)->next
!= nil
) mergesort(al
);
472 if (l2
->next
!= nil
) mergesort(&l2
);
476 if ((*CMP
)(l1
, l2
) <= 0) {
477 if ((l1
= *(al
= &l1
->next
)) == nil
) {
483 l2
= *(al
= &l2
->next
);
485 if (l2
== nil
) break;
490 int namecmp(struct file
*f1
, struct file
*f2
)
492 return strcmp(f1
->name
, f2
->name
);
495 int mtimecmp(struct file
*f1
, struct file
*f2
)
497 return f1
->mtime
== f2
->mtime
? 0 : f1
->mtime
> f2
->mtime
? -1 : 1;
500 int atimecmp(struct file
*f1
, struct file
*f2
)
502 return f1
->atime
== f2
->atime
? 0 : f1
->atime
> f2
->atime
? -1 : 1;
505 int ctimecmp(struct file
*f1
, struct file
*f2
)
507 return f1
->ctime
== f2
->ctime
? 0 : f1
->ctime
> f2
->ctime
? -1 : 1;
510 int typecmp(struct file
*f1
, struct file
*f2
)
512 return ifmt(f1
->mode
) - ifmt(f2
->mode
);
515 int revcmp(struct file
*f1
, struct file
*f2
) { return (*rCMP
)(f2
, f1
); }
517 static void sort(struct file
**al
)
518 /* Sort the files according to the flags. */
520 if (!present('f') && *al
!= nil
&& (*al
)->next
!= nil
) {
523 if (!(field
& L_BYTIME
)) {
526 if (present('r')) { rCMP
= CMP
; CMP
= revcmp
; }
529 /* Sort on name first, then sort on time. */
532 if (field
& L_CTIME
) {
535 if (field
& L_ATIME
) {
541 if (present('r')) { rCMP
= CMP
; CMP
= revcmp
; }
544 /* Separate by file type if so desired. */
546 if (field
& L_TYPE
) {
553 struct file
*newfile(char *name
)
554 /* Create file structure for given name. */
558 new= (struct file
*) allocate(sizeof(*new));
559 new->name
= strcpy((char *) allocate(strlen(name
)+1), name
);
563 void pushfile(struct file
**flist
, struct file
*new)
564 /* Add file to the head of a list. */
570 void delfile(struct file
*old
)
571 /* Release old file structure. */
573 free((void *) old
->name
);
577 struct file
*popfile(struct file
**flist
)
578 /* Pop file off top of file list. */
587 int dotflag(char *name
)
588 /* Return flag that would make ls list this name: -a or -A. */
590 if (*name
++ != '.') return 0;
593 case 0: return 'a'; /* "." */
594 case '.': if (*name
== 0) return 'a'; /* ".." */
595 default: return 'A'; /* ".*" */
599 int adddir(struct file
**aflist
, char *name
)
600 /* Add directory entries of directory name to a file list. */
605 if (access(name
, 0) < 0) {
610 if ((d
= opendir(name
)) == nil
) {
614 while ((e
= readdir(d
)) != nil
) {
615 if (e
->d_ino
!= 0 && present(dotflag(e
->d_name
))) {
616 pushfile(aflist
, newfile(e
->d_name
));
617 aflist
= &(*aflist
)->next
;
624 off_t
countblocks(struct file
*flist
)
625 /* Compute total block count for a list of files. */
629 while (flist
!= nil
) {
630 switch (flist
->mode
& S_IFMT
) {
636 cb
+= nblocks(flist
);
643 void printname(char *name
)
644 /* Print a name with control characters as '?' (unless -q). The terminal is
645 * assumed to be eight bit clean.
648 int c
, q
= present('q');
650 while ((c
= (unsigned char) *name
++) != 0) {
651 if (q
&& (c
< ' ' || c
== 0177)) c
= '?';
656 int mark(struct file
*f
, int doit
)
662 if (field
& L_MARK
) {
663 switch (f
->mode
& S_IFMT
) {
664 case S_IFDIR
: c
= '/'; break;
666 case S_IFIFO
: c
= '|'; break;
669 case S_IFLNK
: c
= '@'; break;
672 case S_IFSOCK
: c
= '='; break;
675 if (f
->mode
& (S_IXUSR
| S_IXGRP
| S_IXOTH
)) c
= '*';
679 if (field
& L_MARKDIR
) {
680 if (S_ISDIR(f
->mode
)) c
= '/';
683 if (doit
&& c
!= 0) putchar(c
);
687 /* Width of entire column, and of several fields. */
688 enum { W_COL
, W_INO
, W_BLK
, W_NLINK
, W_UID
, W_GID
, W_SIZE
, W_NAME
, MAXFLDS
};
690 unsigned char fieldwidth
[MAXCOLS
][MAXFLDS
];
692 void maxise(unsigned char *aw
, int w
)
693 /* Set *aw to the larger of it and w. */
696 if (w
> UCHAR_MAX
) w
= UCHAR_MAX
;
701 int numwidth(unsigned long n
)
702 /* Compute width of 'n' when printed. */
706 do { width
++; } while ((n
/= 10) > 0);
711 int numxwidth(unsigned long n
)
712 /* Compute width of 'n' when printed in hex. */
716 do { width
++; } while ((n
/= 16) > 0);
721 static int nsp
= 0; /* This many spaces have not been printed yet. */
722 #define spaces(n) (nsp= (n))
723 #define terpri() (nsp= 0, putchar('\n')) /* No trailing spaces */
725 void print1(struct file
*f
, int col
, int doit
)
726 /* Either compute the number of spaces needed to print file f (doit == 0) or
727 * really print it (doit == 1).
732 unsigned char *f1width
= fieldwidth
[col
];
734 while (nsp
>0) { putchar(' '); nsp
--; }/* Fill gap between two columns */
736 if (field
& L_INODE
) {
738 printf("%*d ", f1width
[W_INO
], f
->ino
);
740 maxise(&f1width
[W_INO
], numwidth(f
->ino
));
744 if (field
& L_BLOCKS
) {
745 unsigned long nb
= nblk2k(nblocks(f
));
747 printf("%*lu ", f1width
[W_BLK
], nb
);
749 maxise(&f1width
[W_BLK
], numwidth(nb
));
753 if (field
& L_MODE
) {
755 printf("%s ", permissions(f
));
757 width
+= (field
& L_EXTRA
) ? 5 : 11;
760 if (field
& L_EXTRA
) {
765 n
= f1width
[W_SIZE
] - n
;
766 while (n
> 0) { putchar(' '); --n
; }
769 maxise(&f1width
[W_SIZE
], n
);
772 if (field
& L_LONG
) {
774 printf("%*u ", f1width
[W_NLINK
], (unsigned) f
->nlink
);
776 maxise(&f1width
[W_NLINK
], numwidth(f
->nlink
));
779 if (!(field
& L_GROUP
)) {
781 printf("%-*s ", f1width
[W_UID
],
784 maxise(&f1width
[W_UID
],
785 strlen(uidname(f
->uid
)));
790 printf("%-*s ", f1width
[W_GID
], gidname(f
->gid
));
792 maxise(&f1width
[W_GID
], strlen(gidname(f
->gid
)));
796 switch (f
->mode
& S_IFMT
) {
807 printf("%*d, %3d ", f1width
[W_SIZE
] - 5,
808 major(f
->rdev
), minor(f
->rdev
));
810 maxise(&f1width
[W_SIZE
],
811 numwidth(major(f
->rdev
)) + 5);
816 printf("%*lX ", f1width
[W_SIZE
],
817 (unsigned long) f
->rdev
);
819 maxise(&f1width
[W_SIZE
], numwidth(f
->rdev
));
822 #endif /* !__minix */
830 n
= f1width
[W_SIZE
] - n
;
831 while (n
> 0) { putchar(' '); --n
; }
834 maxise(&f1width
[W_SIZE
], n
);
838 printf("%*lu ", f1width
[W_SIZE
],
839 (unsigned long) f
->size
);
841 maxise(&f1width
[W_SIZE
],
849 printf("%s ", timestamp(f
));
851 width
+= (field
& L_LONGTIME
) ? 21 : 13;
858 if (mark(f
, 1) != 0) n
++;
860 if ((field
& L_LONG
) && (f
->mode
& S_IFMT
) == S_IFLNK
) {
864 buf
= (char *) allocate(((size_t) f
->size
+ 1)
866 addpath(&didx
, f
->name
);
867 r
= readlink(path
, buf
, (int) f
->size
);
869 if (r
> 0) buf
[r
] = 0; else r
=1, strcpy(buf
, "?");
876 spaces(f1width
[W_NAME
] - n
);
878 if (mark(f
, 0) != 0) n
++;
880 if ((field
& L_LONG
) && (f
->mode
& S_IFMT
) == S_IFLNK
) {
881 n
+= 4 + (int) f
->size
;
884 maxise(&f1width
[W_NAME
], n
+ NSEP
);
886 for (n
= 1; n
< MAXFLDS
; n
++) width
+= f1width
[n
];
887 maxise(&f1width
[W_COL
], width
);
891 int countfiles(struct file
*flist
)
892 /* Return number of files in the list. */
896 while (flist
!= nil
) { n
++; flist
= flist
->next
; }
901 struct file
*filecol
[MAXCOLS
]; /* filecol[i] is list of files for column i. */
902 int nfiles
, nlines
; /* # files to print, # of lines needed. */
904 void columnise(struct file
*flist
, int nplin
)
905 /* Chop list of files up in columns. Note that 3 columns are used for 5 files
906 * even though nplin may be 4, filecol[3] will simply be nil.
911 nlines
= (nfiles
+ nplin
- 1) / nplin
; /* nlines needed for nfiles */
915 for (i
=1; i
<nplin
; i
++) { /* Give nlines files to each column. */
916 for (j
=0; j
<nlines
&& flist
!= nil
; j
++) flist
= flist
->next
;
922 int print(struct file
*flist
, int nplin
, int doit
)
923 /* Try (doit == 0), or really print the list of files over nplin columns.
924 * Return true if it can be done in nplin columns or if nplin == 1.
927 register struct file
*f
;
928 register int col
, fld
, totlen
;
930 columnise(flist
, nplin
);
933 for (col
= 0; col
< nplin
; col
++) {
934 for (fld
= 0; fld
< MAXFLDS
; fld
++) {
935 fieldwidth
[col
][fld
]= 0;
940 while (--nlines
>= 0) {
943 for (col
= 0; col
< nplin
; col
++) {
944 if ((f
= filecol
[col
]) != nil
) {
945 filecol
[col
]= f
->next
;
946 print1(f
, col
, doit
);
948 if (!doit
&& nplin
> 1) {
949 /* See if this line is not too long. */
950 if (fieldwidth
[col
][W_COL
] == UCHAR_MAX
) {
953 totlen
+= fieldwidth
[col
][W_COL
];
954 if (totlen
> ncols
+NSEP
) return 0;
962 enum depth
{ SURFACE
, SURFACE1
, SUBMERGED
};
963 enum state
{ BOTTOM
, SINKING
, FLOATING
};
965 void listfiles(struct file
*flist
, enum depth depth
, enum state state
)
966 /* Main workhorse of ls, it sorts and prints the list of files. Flags:
967 * depth: working with the command line / just one file / listing dir.
968 * state: How "recursive" do we have to be.
971 struct file
*dlist
= nil
, **afl
= &flist
, **adl
= &dlist
;
973 static int white
= 1; /* Nothing printed yet. */
975 /* Flush everything previously printed, so new error output will
976 * not intermix with files listed earlier.
980 if (field
!= 0 || state
!= BOTTOM
) { /* Need stat(2) info. */
981 while (*afl
!= nil
) {
982 static struct stat st
;
985 addpath(&didx
, (*afl
)->name
);
987 if ((r
= status(path
, &st
)) < 0
989 && (status
== lstat
|| lstat(path
, &st
) < 0)
992 if (depth
!= SUBMERGED
|| errno
!= ENOENT
)
993 report((*afl
)->name
);
994 delfile(popfile(afl
));
1004 if (depth
== SUBMERGED
&& (field
& (L_BLOCKS
| L_LONG
))) {
1005 printf("total %ld\n", nblk2k(countblocks(flist
)));
1008 if (state
== SINKING
|| depth
== SURFACE1
) {
1009 /* Don't list directories themselves, list their contents later. */
1011 while (*afl
!= nil
) {
1012 if (((*afl
)->mode
& S_IFMT
) == S_IFDIR
) {
1013 pushfile(adl
, popfile(afl
));
1021 if ((nfiles
= countfiles(flist
)) > 0) {
1022 /* Print files in how many columns? */
1023 nplin
= !present('C') ? 1 : nfiles
< MAXCOLS
? nfiles
: MAXCOLS
;
1025 while (!print(flist
, nplin
, 0)) nplin
--; /* Try first */
1027 print(flist
, nplin
, 1); /* Then do it! */
1031 while (flist
!= nil
) { /* Destroy file list */
1032 if (state
== FLOATING
&& (flist
->mode
& S_IFMT
) == S_IFDIR
) {
1033 /* But keep these directories for ls -R. */
1034 pushfile(adl
, popfile(&flist
));
1037 delfile(popfile(&flist
));
1041 while (dlist
!= nil
) { /* List directories */
1042 if (dotflag(dlist
->name
) != 'a' || depth
!= SUBMERGED
) {
1045 addpath(&didx
, dlist
->name
);
1048 if (adddir(&flist
, path
)) {
1049 if (depth
!= SURFACE1
) {
1050 if (!white
) putchar('\n');
1051 printf("%s:\n", path
);
1054 listfiles(flist
, SUBMERGED
,
1055 state
== FLOATING
? FLOATING
: BOTTOM
);
1059 delfile(popfile(&dlist
));
1063 int main(int argc
, char **argv
)
1065 struct file
*flist
= nil
, **aflist
= &flist
;
1073 if ((arg0
= strrchr(argv
[0], '/')) == nil
) arg0
= argv
[0]; else arg0
++;
1076 if (strcmp(arg0
, "ls") != 0) {
1080 if (strchr(arg0flag
, *p
) != nil
) *p
+= 'A' - 'a';
1085 while (*argv
!= nil
&& (*argv
)[0] == '-') {
1086 if ((*argv
)[1] == '-' && (*argv
)[2] == 0) {
1090 setflags(*argv
++ + 1);
1095 if (istty
&& (lsflags
= getenv("LSOPTS")) != nil
) {
1096 if (*lsflags
== '-') lsflags
++;
1100 if (!present('1') && !present('C') && !present('l')
1101 && (istty
|| present('M') || present('X') || present('F'))
1104 if (istty
) setflags("q");
1106 if (SUPER_ID
== 0 || present('a')) setflags("A");
1108 if (present('i')) field
|= L_INODE
;
1109 if (present('s')) field
|= L_BLOCKS
;
1110 if (present('M')) field
|= L_MODE
;
1111 if (present('X')) field
|= L_EXTRA
| L_MODE
;
1112 if (present('t')) field
|= L_BYTIME
;
1113 if (present('u')) field
|= L_ATIME
;
1114 if (present('c')) field
|= L_CTIME
;
1115 if (present('l')) field
|= L_MODE
| L_LONG
;
1116 if (present('g')) field
|= L_MODE
| L_LONG
| L_GROUP
;
1117 if (present('F')) field
|= L_MARK
;
1118 if (present('p')) field
|= L_MARKDIR
;
1119 if (present('D')) field
|= L_TYPE
;
1120 if (present('T')) field
|= L_MODE
| L_LONG
| L_LONGTIME
;
1121 if (present('d')) field
|= L_DIR
;
1122 if (present('h')) field
|= L_KMG
;
1123 if (field
& L_LONG
) field
&= ~L_EXTRA
;
1126 status
= present('L') ? stat
: lstat
;
1130 int t
= istty
? 1 : open("/dev/tty", O_WRONLY
);
1132 if (t
>= 0 && ioctl(t
, TIOCGWINSZ
, &ws
) == 0 && ws
.ws_col
> 0)
1133 ncols
= ws
.ws_col
- 1;
1135 if (t
!= 1 && t
!= -1) close(t
);
1141 if (!(field
& L_DIR
)) depth
= SURFACE1
;
1142 pushfile(aflist
, newfile("."));
1144 if (argv
[1] == nil
&& !(field
& L_DIR
)) depth
= SURFACE1
;
1147 pushfile(aflist
, newfile(*argv
++));
1148 aflist
= &(*aflist
)->next
;
1149 } while (*argv
!=nil
);
1151 listfiles(flist
, depth
,
1152 (field
& L_DIR
) ? BOTTOM
: present('R') ? FLOATING
: SINKING
);