mv: implement -n option
[oweals/busybox.git] / coreutils / ls.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * tiny-ls.c version 0.1.0: A minimalist 'ls'
4  * Copyright (C) 1996 Brian Candler <B.Candler@pobox.com>
5  *
6  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
7  */
8
9 /* [date unknown. Perhaps before year 2000]
10  * To achieve a small memory footprint, this version of 'ls' doesn't do any
11  * file sorting, and only has the most essential command line switches
12  * (i.e., the ones I couldn't live without :-) All features which involve
13  * linking in substantial chunks of libc can be disabled.
14  *
15  * Although I don't really want to add new features to this program to
16  * keep it small, I *am* interested to receive bug fixes and ways to make
17  * it more portable.
18  *
19  * KNOWN BUGS:
20  * 1. hidden files can make column width too large
21  *
22  * NON-OPTIMAL BEHAVIOUR:
23  * 1. autowidth reads directories twice
24  * 2. if you do a short directory listing without filetype characters
25  *    appended, there's no need to stat each one
26  * PORTABILITY:
27  * 1. requires lstat (BSD) - how do you do it without?
28  *
29  * [2009-03]
30  * ls sorts listing now, and supports almost all options.
31  */
32 #include "libbb.h"
33 #include "unicode.h"
34
35
36 /* This is a NOEXEC applet. Be very careful! */
37
38
39 #if ENABLE_FTPD
40 /* ftpd uses ls, and without timestamps Mozilla won't understand
41  * ftpd's LIST output.
42  */
43 # undef CONFIG_FEATURE_LS_TIMESTAMPS
44 # undef ENABLE_FEATURE_LS_TIMESTAMPS
45 # undef IF_FEATURE_LS_TIMESTAMPS
46 # undef IF_NOT_FEATURE_LS_TIMESTAMPS
47 # define CONFIG_FEATURE_LS_TIMESTAMPS 1
48 # define ENABLE_FEATURE_LS_TIMESTAMPS 1
49 # define IF_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__
50 # define IF_NOT_FEATURE_LS_TIMESTAMPS(...)
51 #endif
52
53
54 enum {
55 TERMINAL_WIDTH  = 80,           /* use 79 if terminal has linefold bug */
56 COLUMN_GAP      = 2,            /* includes the file type char */
57
58 /* what is the overall style of the listing */
59 STYLE_COLUMNS   = 1 << 21,      /* fill columns */
60 STYLE_LONG      = 2 << 21,      /* one record per line, extended info */
61 STYLE_SINGLE    = 3 << 21,      /* one record per line */
62 STYLE_MASK      = STYLE_SINGLE,
63
64 /* 51306 lrwxrwxrwx  1 root     root         2 May 11 01:43 /bin/view -> vi* */
65 /* what file information will be listed */
66 LIST_INO        = 1 << 0,
67 LIST_BLOCKS     = 1 << 1,
68 LIST_MODEBITS   = 1 << 2,
69 LIST_NLINKS     = 1 << 3,
70 LIST_ID_NAME    = 1 << 4,
71 LIST_ID_NUMERIC = 1 << 5,
72 LIST_CONTEXT    = 1 << 6,
73 LIST_SIZE       = 1 << 7,
74 //LIST_DEV        = 1 << 8, - unused, synonym to LIST_SIZE
75 LIST_DATE_TIME  = 1 << 9,
76 LIST_FULLTIME   = 1 << 10,
77 LIST_FILENAME   = 1 << 11,
78 LIST_SYMLINK    = 1 << 12,
79 LIST_FILETYPE   = 1 << 13,
80 LIST_EXEC       = 1 << 14,
81 LIST_MASK       = (LIST_EXEC << 1) - 1,
82
83 /* what files will be displayed */
84 DISP_DIRNAME    = 1 << 15,      /* 2 or more items? label directories */
85 DISP_HIDDEN     = 1 << 16,      /* show filenames starting with . */
86 DISP_DOT        = 1 << 17,      /* show . and .. */
87 DISP_NOLIST     = 1 << 18,      /* show directory as itself, not contents */
88 DISP_RECURSIVE  = 1 << 19,      /* show directory and everything below it */
89 DISP_ROWS       = 1 << 20,      /* print across rows */
90 DISP_MASK       = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1),
91
92 /* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */
93 SORT_FORWARD    = 0,            /* sort in reverse order */
94 SORT_REVERSE    = 1 << 27,      /* sort in reverse order */
95
96 SORT_NAME       = 0,            /* sort by file name */
97 SORT_SIZE       = 1 << 28,      /* sort by file size */
98 SORT_ATIME      = 2 << 28,      /* sort by last access time */
99 SORT_CTIME      = 3 << 28,      /* sort by last change time */
100 SORT_MTIME      = 4 << 28,      /* sort by last modification time */
101 SORT_VERSION    = 5 << 28,      /* sort by version */
102 SORT_EXT        = 6 << 28,      /* sort by file name extension */
103 SORT_DIR        = 7 << 28,      /* sort by file or directory */
104 SORT_MASK       = (7 << 28) * ENABLE_FEATURE_LS_SORTFILES,
105
106 /* which of the three times will be used */
107 TIME_CHANGE     = (1 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
108 TIME_ACCESS     = (1 << 24) * ENABLE_FEATURE_LS_TIMESTAMPS,
109 TIME_MASK       = (3 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
110
111 FOLLOW_LINKS    = (1 << 25) * ENABLE_FEATURE_LS_FOLLOWLINKS,
112
113 LS_DISP_HR      = (1 << 26) * ENABLE_FEATURE_HUMAN_READABLE,
114
115 LIST_SHORT      = LIST_FILENAME,
116 LIST_LONG       = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
117                   LIST_DATE_TIME | LIST_FILENAME | LIST_SYMLINK,
118
119 SPLIT_DIR       = 1,
120 SPLIT_FILE      = 0,
121 SPLIT_SUBDIR    = 2,
122 };
123
124 /* "[-]Cadil1", POSIX mandated options, busybox always supports */
125 /* "[-]gnsx", POSIX non-mandated options, busybox always supports */
126 /* "[-]Q" GNU option? busybox always supports */
127 /* "[-]Ak" GNU options, busybox always supports */
128 /* "[-]FLRctur", POSIX mandated options, busybox optionally supports */
129 /* "[-]p", POSIX non-mandated options, busybox optionally supports */
130 /* "[-]SXvThw", GNU options, busybox optionally supports */
131 /* "[-]K", SELinux mandated options, busybox optionally supports */
132 /* "[-]e", I think we made this one up */
133 static const char ls_options[] ALIGN1 =
134         "Cadil1gnsxQAk" /* 13 opts, total 13 */
135         IF_FEATURE_LS_TIMESTAMPS("cetu") /* 4, 17 */
136         IF_FEATURE_LS_SORTFILES("SXrv")  /* 4, 21 */
137         IF_FEATURE_LS_FILETYPES("Fp")    /* 2, 23 */
138         IF_FEATURE_LS_FOLLOWLINKS("L")   /* 1, 24 */
139         IF_FEATURE_LS_RECURSIVE("R")     /* 1, 25 */
140         IF_FEATURE_HUMAN_READABLE("h")   /* 1, 26 */
141         IF_SELINUX("KZ") /* 2, 28 */
142         IF_FEATURE_AUTOWIDTH("T:w:") /* 2, 30 */
143         ;
144 enum {
145         //OPT_C = (1 << 0),
146         //OPT_a = (1 << 1),
147         //OPT_d = (1 << 2),
148         //OPT_i = (1 << 3),
149         //OPT_l = (1 << 4),
150         //OPT_1 = (1 << 5),
151         OPT_g = (1 << 6),
152         //OPT_n = (1 << 7),
153         //OPT_s = (1 << 8),
154         //OPT_x = (1 << 9),
155         OPT_Q = (1 << 10),
156         //OPT_A = (1 << 11),
157         //OPT_k = (1 << 12),
158         OPTBIT_color = 13
159                 + 4 * ENABLE_FEATURE_LS_TIMESTAMPS
160                 + 4 * ENABLE_FEATURE_LS_SORTFILES
161                 + 2 * ENABLE_FEATURE_LS_FILETYPES
162                 + 1 * ENABLE_FEATURE_LS_FOLLOWLINKS
163                 + 1 * ENABLE_FEATURE_LS_RECURSIVE
164                 + 1 * ENABLE_FEATURE_HUMAN_READABLE
165                 + 2 * ENABLE_SELINUX
166                 + 2 * ENABLE_FEATURE_AUTOWIDTH,
167         OPT_color = 1 << OPTBIT_color,
168 };
169
170 enum {
171         LIST_MASK_TRIGGER       = 0,
172         STYLE_MASK_TRIGGER      = STYLE_MASK,
173         DISP_MASK_TRIGGER       = DISP_ROWS,
174         SORT_MASK_TRIGGER       = SORT_MASK,
175 };
176
177 /* TODO: simple toggles may be stored as OPT_xxx bits instead */
178 static const unsigned opt_flags[] = {
179         LIST_SHORT | STYLE_COLUMNS, /* C */
180         DISP_HIDDEN | DISP_DOT,     /* a */
181         DISP_NOLIST,                /* d */
182         LIST_INO,                   /* i */
183         LIST_LONG | STYLE_LONG,     /* l - remember LS_DISP_HR in mask! */
184         LIST_SHORT | STYLE_SINGLE,  /* 1 */
185         0,                          /* g (don't show group) - handled via OPT_g */
186         LIST_ID_NUMERIC,            /* n */
187         LIST_BLOCKS,                /* s */
188         DISP_ROWS,                  /* x */
189         0,                          /* Q (quote filename) - handled via OPT_Q */
190         DISP_HIDDEN,                /* A */
191         ENABLE_SELINUX * LIST_CONTEXT, /* k (ignored if !SELINUX) */
192 #if ENABLE_FEATURE_LS_TIMESTAMPS
193         TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME),   /* c */
194         LIST_FULLTIME,              /* e */
195         ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME,   /* t */
196         TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME),   /* u */
197 #endif
198 #if ENABLE_FEATURE_LS_SORTFILES
199         SORT_SIZE,                  /* S */
200         SORT_EXT,                   /* X */
201         SORT_REVERSE,               /* r */
202         SORT_VERSION,               /* v */
203 #endif
204 #if ENABLE_FEATURE_LS_FILETYPES
205         LIST_FILETYPE | LIST_EXEC,  /* F */
206         LIST_FILETYPE,              /* p */
207 #endif
208 #if ENABLE_FEATURE_LS_FOLLOWLINKS
209         FOLLOW_LINKS,               /* L */
210 #endif
211 #if ENABLE_FEATURE_LS_RECURSIVE
212         DISP_RECURSIVE,             /* R */
213 #endif
214 #if ENABLE_FEATURE_HUMAN_READABLE
215         LS_DISP_HR,                 /* h */
216 #endif
217 #if ENABLE_SELINUX
218         LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */
219 #endif
220 #if ENABLE_SELINUX
221         LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT, /* Z */
222 #endif
223         (1U<<31)
224         /* options after Z are not processed through opt_flags:
225          * T, w - ignored
226          */
227 };
228
229
230 /*
231  * a directory entry and its stat info are stored here
232  */
233 struct dnode {
234         const char *name;       /* the dir entry name */
235         const char *fullname;   /* the dir entry name */
236         struct dnode *next;     /* point at the next node */
237         smallint fname_allocated;
238         struct stat dstat;      /* the file stat info */
239         IF_SELINUX(security_context_t sid;)
240 };
241
242 struct globals {
243 #if ENABLE_FEATURE_LS_COLOR
244         smallint show_color;
245 #endif
246         smallint exit_code;
247         unsigned all_fmt;
248 #if ENABLE_FEATURE_AUTOWIDTH
249         unsigned tabstops; // = COLUMN_GAP;
250         unsigned terminal_width; // = TERMINAL_WIDTH;
251 #endif
252 #if ENABLE_FEATURE_LS_TIMESTAMPS
253         /* Do time() just once. Saves one syscall per file for "ls -l" */
254         time_t current_time_t;
255 #endif
256 } FIX_ALIASING;
257 #define G (*(struct globals*)&bb_common_bufsiz1)
258 #if ENABLE_FEATURE_LS_COLOR
259 # define show_color     (G.show_color    )
260 #else
261 enum { show_color = 0 };
262 #endif
263 #define exit_code       (G.exit_code     )
264 #define all_fmt         (G.all_fmt       )
265 #if ENABLE_FEATURE_AUTOWIDTH
266 # define tabstops       (G.tabstops      )
267 # define terminal_width (G.terminal_width)
268 #else
269 enum {
270         tabstops = COLUMN_GAP,
271         terminal_width = TERMINAL_WIDTH,
272 };
273 #endif
274 #define current_time_t (G.current_time_t)
275 #define INIT_G() do { \
276         /* we have to zero it out because of NOEXEC */ \
277         memset(&G, 0, sizeof(G)); \
278         IF_FEATURE_AUTOWIDTH(tabstops = COLUMN_GAP;) \
279         IF_FEATURE_AUTOWIDTH(terminal_width = TERMINAL_WIDTH;) \
280         IF_FEATURE_LS_TIMESTAMPS(time(&current_time_t);) \
281 } while (0)
282
283
284 static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
285 {
286         struct stat dstat;
287         struct dnode *cur;
288         IF_SELINUX(security_context_t sid = NULL;)
289
290         if ((all_fmt & FOLLOW_LINKS) || force_follow) {
291 #if ENABLE_SELINUX
292                 if (is_selinux_enabled())  {
293                          getfilecon(fullname, &sid);
294                 }
295 #endif
296                 if (stat(fullname, &dstat)) {
297                         bb_simple_perror_msg(fullname);
298                         exit_code = EXIT_FAILURE;
299                         return 0;
300                 }
301         } else {
302 #if ENABLE_SELINUX
303                 if (is_selinux_enabled()) {
304                         lgetfilecon(fullname, &sid);
305                 }
306 #endif
307                 if (lstat(fullname, &dstat)) {
308                         bb_simple_perror_msg(fullname);
309                         exit_code = EXIT_FAILURE;
310                         return 0;
311                 }
312         }
313
314         cur = xmalloc(sizeof(*cur));
315         cur->fullname = fullname;
316         cur->name = name;
317         cur->dstat = dstat;
318         IF_SELINUX(cur->sid = sid;)
319         return cur;
320 }
321
322 /* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
323  * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
324  *  3/7:multiplexed char/block device)
325  * and we use 0 for unknown and 15 for executables (see below) */
326 #define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
327 #define TYPECHAR(mode)  ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
328 #define APPCHAR(mode)   ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)])
329 /* 036 black foreground              050 black background
330    037 red foreground                051 red background
331    040 green foreground              052 green background
332    041 brown foreground              053 brown background
333    042 blue foreground               054 blue background
334    043 magenta (purple) foreground   055 magenta background
335    044 cyan (light blue) foreground  056 cyan background
336    045 gray foreground               057 white background
337 */
338 #define COLOR(mode) ( \
339         /*un  fi  chr     dir     blk     file    link    sock        exe */ \
340         "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
341         [TYPEINDEX(mode)])
342 /* Select normal (0) [actually "reset all"] or bold (1)
343  * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
344  *  let's use 7 for "impossible" types, just for fun)
345  * Note: coreutils 6.9 uses inverted red for setuid binaries.
346  */
347 #define ATTR(mode) ( \
348         /*un fi chr   dir   blk   file  link  sock     exe */ \
349         "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
350         [TYPEINDEX(mode)])
351
352 #if ENABLE_FEATURE_LS_COLOR
353 /* mode of zero is interpreted as "unknown" (stat failed) */
354 static char fgcolor(mode_t mode)
355 {
356         if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
357                 return COLOR(0xF000);   /* File is executable ... */
358         return COLOR(mode);
359 }
360 static char bold(mode_t mode)
361 {
362         if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
363                 return ATTR(0xF000);    /* File is executable ... */
364         return ATTR(mode);
365 }
366 #endif
367
368 #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
369 static char append_char(mode_t mode)
370 {
371         if (!(all_fmt & LIST_FILETYPE))
372                 return '\0';
373         if (S_ISDIR(mode))
374                 return '/';
375         if (!(all_fmt & LIST_EXEC))
376                 return '\0';
377         if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
378                 return '*';
379         return APPCHAR(mode);
380 }
381 #endif
382
383 static unsigned count_dirs(struct dnode **dn, int which)
384 {
385         unsigned dirs, all;
386
387         if (!dn)
388                 return 0;
389
390         dirs = all = 0;
391         for (; *dn; dn++) {
392                 const char *name;
393
394                 all++;
395                 if (!S_ISDIR((*dn)->dstat.st_mode))
396                         continue;
397                 name = (*dn)->name;
398                 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
399                  /* or if it's not . or .. */
400                  || name[0] != '.' || (name[1] && (name[1] != '.' || name[2]))
401                 ) {
402                         dirs++;
403                 }
404         }
405         return which != SPLIT_FILE ? dirs : all - dirs;
406 }
407
408 /* get memory to hold an array of pointers */
409 static struct dnode **dnalloc(unsigned num)
410 {
411         if (num < 1)
412                 return NULL;
413
414         num++; /* so that we have terminating NULL */
415         return xzalloc(num * sizeof(struct dnode *));
416 }
417
418 #if ENABLE_FEATURE_LS_RECURSIVE
419 static void dfree(struct dnode **dnp)
420 {
421         unsigned i;
422
423         if (dnp == NULL)
424                 return;
425
426         for (i = 0; dnp[i]; i++) {
427                 struct dnode *cur = dnp[i];
428                 if (cur->fname_allocated)
429                         free((char*)cur->fullname);
430                 free(cur);
431         }
432         free(dnp);
433 }
434 #else
435 #define dfree(...) ((void)0)
436 #endif
437
438 /* Returns NULL-terminated malloced vector of pointers (or NULL) */
439 static struct dnode **splitdnarray(struct dnode **dn, int which)
440 {
441         unsigned dncnt, d;
442         struct dnode **dnp;
443
444         if (dn == NULL)
445                 return NULL;
446
447         /* count how many dirs or files there are */
448         dncnt = count_dirs(dn, which);
449
450         /* allocate a file array and a dir array */
451         dnp = dnalloc(dncnt);
452
453         /* copy the entrys into the file or dir array */
454         for (d = 0; *dn; dn++) {
455                 if (S_ISDIR((*dn)->dstat.st_mode)) {
456                         const char *name;
457
458                         if (!(which & (SPLIT_DIR|SPLIT_SUBDIR)))
459                                 continue;
460                         name = (*dn)->name;
461                         if ((which & SPLIT_DIR)
462                          || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
463                         ) {
464                                 dnp[d++] = *dn;
465                         }
466                 } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {
467                         dnp[d++] = *dn;
468                 }
469         }
470         return dnp;
471 }
472
473 #if ENABLE_FEATURE_LS_SORTFILES
474 static int sortcmp(const void *a, const void *b)
475 {
476         struct dnode *d1 = *(struct dnode **)a;
477         struct dnode *d2 = *(struct dnode **)b;
478         unsigned sort_opts = all_fmt & SORT_MASK;
479         off_t dif;
480
481         dif = 0; /* assume SORT_NAME */
482         // TODO: use pre-initialized function pointer
483         // instead of branch forest
484         if (sort_opts == SORT_SIZE) {
485                 dif = (d2->dstat.st_size - d1->dstat.st_size);
486         } else if (sort_opts == SORT_ATIME) {
487                 dif = (d2->dstat.st_atime - d1->dstat.st_atime);
488         } else if (sort_opts == SORT_CTIME) {
489                 dif = (d2->dstat.st_ctime - d1->dstat.st_ctime);
490         } else if (sort_opts == SORT_MTIME) {
491                 dif = (d2->dstat.st_mtime - d1->dstat.st_mtime);
492         } else if (sort_opts == SORT_DIR) {
493                 dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);
494                 /* } else if (sort_opts == SORT_VERSION) { */
495                 /* } else if (sort_opts == SORT_EXT) { */
496         }
497         if (dif == 0) {
498                 /* sort by name, or tie_breaker for other sorts */
499                 if (ENABLE_LOCALE_SUPPORT)
500                         dif = strcoll(d1->name, d2->name);
501                 else
502                         dif = strcmp(d1->name, d2->name);
503         }
504
505         /* Make dif fit into an int */
506         if (sizeof(dif) > sizeof(int)) {
507                 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
508                 /* shift leaving only "int" worth of bits */
509                 if (dif != 0) {
510                         dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
511                 }
512         }
513
514         return (all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif;
515 }
516
517 static void dnsort(struct dnode **dn, int size)
518 {
519         qsort(dn, size, sizeof(*dn), sortcmp);
520 }
521 #else
522 #define dnsort(dn, size) ((void)0)
523 #endif
524
525
526 static unsigned calc_name_len(const char *name)
527 {
528         unsigned len;
529         uni_stat_t uni_stat;
530
531         // TODO: quote tab as \t, etc, if -Q
532         name = printable_string(&uni_stat, name);
533
534         if (!(option_mask32 & OPT_Q)) {
535                 return uni_stat.unicode_width;
536         }
537
538         len = 2 + uni_stat.unicode_width;
539         while (*name) {
540                 if (*name == '"' || *name == '\\') {
541                         len++;
542                 }
543                 name++;
544         }
545         return len;
546 }
547
548
549 /* Return the number of used columns.
550  * Note that only STYLE_COLUMNS uses return value.
551  * STYLE_SINGLE and STYLE_LONG don't care.
552  * coreutils 7.2 also supports:
553  * ls -b (--escape) = octal escapes (although it doesn't look like working)
554  * ls -N (--literal) = not escape at all
555  */
556 static unsigned print_name(const char *name)
557 {
558         unsigned len;
559         uni_stat_t uni_stat;
560
561         // TODO: quote tab as \t, etc, if -Q
562         name = printable_string(&uni_stat, name);
563
564         if (!(option_mask32 & OPT_Q)) {
565                 fputs(name, stdout);
566                 return uni_stat.unicode_width;
567         }
568
569         len = 2 + uni_stat.unicode_width;
570         putchar('"');
571         while (*name) {
572                 if (*name == '"' || *name == '\\') {
573                         putchar('\\');
574                         len++;
575                 }
576                 putchar(*name);
577                 name++;
578         }
579         putchar('"');
580         return len;
581 }
582
583 /* Return the number of used columns.
584  * Note that only STYLE_COLUMNS uses return value,
585  * STYLE_SINGLE and STYLE_LONG don't care.
586  */
587 static NOINLINE unsigned list_single(const struct dnode *dn)
588 {
589         unsigned column = 0;
590         char *lpath = lpath; /* for compiler */
591 #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
592         struct stat info;
593         char append;
594 #endif
595
596         /* Never happens:
597         if (dn->fullname == NULL)
598                 return 0;
599         */
600
601 #if ENABLE_FEATURE_LS_FILETYPES
602         append = append_char(dn->dstat.st_mode);
603 #endif
604
605         /* Do readlink early, so that if it fails, error message
606          * does not appear *inside* the "ls -l" line */
607         if (all_fmt & LIST_SYMLINK)
608                 if (S_ISLNK(dn->dstat.st_mode))
609                         lpath = xmalloc_readlink_or_warn(dn->fullname);
610
611         if (all_fmt & LIST_INO)
612                 column += printf("%7llu ", (long long) dn->dstat.st_ino);
613         if (all_fmt & LIST_BLOCKS)
614                 column += printf("%4"OFF_FMT"u ", (off_t) (dn->dstat.st_blocks >> 1));
615         if (all_fmt & LIST_MODEBITS)
616                 column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
617         if (all_fmt & LIST_NLINKS)
618                 column += printf("%4lu ", (long) dn->dstat.st_nlink);
619 #if ENABLE_FEATURE_LS_USERNAME
620         if (all_fmt & LIST_ID_NAME) {
621                 if (option_mask32 & OPT_g) {
622                         column += printf("%-8.8s ",
623                                 get_cached_username(dn->dstat.st_uid));
624                 } else {
625                         column += printf("%-8.8s %-8.8s ",
626                                 get_cached_username(dn->dstat.st_uid),
627                                 get_cached_groupname(dn->dstat.st_gid));
628                 }
629         }
630 #endif
631         if (all_fmt & LIST_ID_NUMERIC) {
632                 if (option_mask32 & OPT_g)
633                         column += printf("%-8u ", (int) dn->dstat.st_uid);
634                 else
635                         column += printf("%-8u %-8u ",
636                                         (int) dn->dstat.st_uid,
637                                         (int) dn->dstat.st_gid);
638         }
639         if (all_fmt & (LIST_SIZE /*|LIST_DEV*/ )) {
640                 if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
641                         column += printf("%4u, %3u ",
642                                         (int) major(dn->dstat.st_rdev),
643                                         (int) minor(dn->dstat.st_rdev));
644                 } else {
645                         if (all_fmt & LS_DISP_HR) {
646                                 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
647                                         /* print st_size, show one fractional, use suffixes */
648                                         make_human_readable_str(dn->dstat.st_size, 1, 0)
649                                 );
650                         } else {
651                                 column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size);
652                         }
653                 }
654         }
655 #if ENABLE_FEATURE_LS_TIMESTAMPS
656         if (all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
657                 char *filetime;
658                 time_t ttime = dn->dstat.st_mtime;
659                 if (all_fmt & TIME_ACCESS)
660                         ttime = dn->dstat.st_atime;
661                 if (all_fmt & TIME_CHANGE)
662                         ttime = dn->dstat.st_ctime;
663                 filetime = ctime(&ttime);
664                 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
665                 if (all_fmt & LIST_FULLTIME)
666                         column += printf("%.24s ", filetime);
667                 else { /* LIST_DATE_TIME */
668                         /* current_time_t ~== time(NULL) */
669                         time_t age = current_time_t - ttime;
670                         printf("%.6s ", filetime + 4); /* "Jun 30" */
671                         if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
672                                 /* hh:mm if less than 6 months old */
673                                 printf("%.5s ", filetime + 11);
674                         } else { /* year. buggy if year > 9999 ;) */
675                                 printf(" %.4s ", filetime + 20);
676                         }
677                         column += 13;
678                 }
679         }
680 #endif
681 #if ENABLE_SELINUX
682         if (all_fmt & LIST_CONTEXT) {
683                 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
684                 freecon(dn->sid);
685         }
686 #endif
687         if (all_fmt & LIST_FILENAME) {
688 #if ENABLE_FEATURE_LS_COLOR
689                 if (show_color) {
690                         info.st_mode = 0; /* for fgcolor() */
691                         lstat(dn->fullname, &info);
692                         printf("\033[%u;%um", bold(info.st_mode),
693                                         fgcolor(info.st_mode));
694                 }
695 #endif
696                 column += print_name(dn->name);
697                 if (show_color) {
698                         printf("\033[0m");
699                 }
700         }
701         if (all_fmt & LIST_SYMLINK) {
702                 if (S_ISLNK(dn->dstat.st_mode) && lpath) {
703                         printf(" -> ");
704 #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
705 #if ENABLE_FEATURE_LS_COLOR
706                         info.st_mode = 0; /* for fgcolor() */
707 #endif
708                         if (stat(dn->fullname, &info) == 0) {
709                                 append = append_char(info.st_mode);
710                         }
711 #endif
712 #if ENABLE_FEATURE_LS_COLOR
713                         if (show_color) {
714                                 printf("\033[%u;%um", bold(info.st_mode),
715                                            fgcolor(info.st_mode));
716                         }
717 #endif
718                         column += print_name(lpath) + 4;
719                         if (show_color) {
720                                 printf("\033[0m");
721                         }
722                         free(lpath);
723                 }
724         }
725 #if ENABLE_FEATURE_LS_FILETYPES
726         if (all_fmt & LIST_FILETYPE) {
727                 if (append) {
728                         putchar(append);
729                         column++;
730                 }
731         }
732 #endif
733
734         return column;
735 }
736
737 static void showfiles(struct dnode **dn, unsigned nfiles)
738 {
739         unsigned i, ncols, nrows, row, nc;
740         unsigned column = 0;
741         unsigned nexttab = 0;
742         unsigned column_width = 0; /* used only by STYLE_COLUMNS */
743
744         if (all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */
745                 ncols = 1;
746         } else {
747                 /* find the longest file name, use that as the column width */
748                 for (i = 0; dn[i]; i++) {
749                         int len = calc_name_len(dn[i]->name);
750                         if (column_width < len)
751                                 column_width = len;
752                 }
753                 column_width += tabstops +
754                         IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
755                                 ((all_fmt & LIST_INO) ? 8 : 0) +
756                                 ((all_fmt & LIST_BLOCKS) ? 5 : 0);
757                 ncols = (int) (terminal_width / column_width);
758         }
759
760         if (ncols > 1) {
761                 nrows = nfiles / ncols;
762                 if (nrows * ncols < nfiles)
763                         nrows++;                /* round up fractionals */
764         } else {
765                 nrows = nfiles;
766                 ncols = 1;
767         }
768
769         for (row = 0; row < nrows; row++) {
770                 for (nc = 0; nc < ncols; nc++) {
771                         /* reach into the array based on the column and row */
772                         if (all_fmt & DISP_ROWS)
773                                 i = (row * ncols) + nc; /* display across row */
774                         else
775                                 i = (nc * nrows) + row; /* display by column */
776                         if (i < nfiles) {
777                                 if (column > 0) {
778                                         nexttab -= column;
779                                         printf("%*s", nexttab, "");
780                                         column += nexttab;
781                                 }
782                                 nexttab = column + column_width;
783                                 column += list_single(dn[i]);
784                         }
785                 }
786                 putchar('\n');
787                 column = 0;
788         }
789 }
790
791
792 #if ENABLE_DESKTOP
793 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
794  * If any of the -l, -n, -s options is specified, each list
795  * of files within the directory shall be preceded by a
796  * status line indicating the number of file system blocks
797  * occupied by files in the directory in 512-byte units if
798  * the -k option is not specified, or 1024-byte units if the
799  * -k option is specified, rounded up to the next integral
800  * number of units.
801  */
802 /* by Jorgen Overgaard (jorgen AT antistaten.se) */
803 static off_t calculate_blocks(struct dnode **dn)
804 {
805         uoff_t blocks = 1;
806         if (dn) {
807                 while (*dn) {
808                         /* st_blocks is in 512 byte blocks */
809                         blocks += (*dn)->dstat.st_blocks;
810                         dn++;
811                 }
812         }
813
814         /* Even though standard says use 512 byte blocks, coreutils use 1k */
815         /* Actually, we round up by calculating (blocks + 1) / 2,
816          * "+ 1" was done when we initialized blocks to 1 */
817         return blocks >> 1;
818 }
819 #endif
820
821
822 static struct dnode **list_dir(const char *, unsigned *);
823
824 static void showdirs(struct dnode **dn, int first)
825 {
826         unsigned nfiles;
827         unsigned dndirs;
828         struct dnode **subdnp;
829         struct dnode **dnd;
830
831         /* Never happens:
832         if (dn == NULL || ndirs < 1) {
833                 return;
834         }
835         */
836
837         for (; *dn; dn++) {
838                 if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
839                         if (!first)
840                                 bb_putchar('\n');
841                         first = 0;
842                         printf("%s:\n", (*dn)->fullname);
843                 }
844                 subdnp = list_dir((*dn)->fullname, &nfiles);
845 #if ENABLE_DESKTOP
846                 if ((all_fmt & STYLE_MASK) == STYLE_LONG)
847                         printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
848 #endif
849                 if (nfiles > 0) {
850                         /* list all files at this level */
851                         dnsort(subdnp, nfiles);
852                         showfiles(subdnp, nfiles);
853                         if (ENABLE_FEATURE_LS_RECURSIVE
854                          && (all_fmt & DISP_RECURSIVE)
855                         ) {
856                                 /* recursive - list the sub-dirs */
857                                 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
858                                 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
859                                 if (dndirs > 0) {
860                                         dnsort(dnd, dndirs);
861                                         showdirs(dnd, 0);
862                                         /* free the array of dnode pointers to the dirs */
863                                         free(dnd);
864                                 }
865                         }
866                         /* free the dnodes and the fullname mem */
867                         dfree(subdnp);
868                 }
869         }
870 }
871
872
873 /* Returns NULL-terminated malloced vector of pointers (or NULL) */
874 static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
875 {
876         struct dnode *dn, *cur, **dnp;
877         struct dirent *entry;
878         DIR *dir;
879         unsigned i, nfiles;
880
881         /* Never happens:
882         if (path == NULL)
883                 return NULL;
884         */
885
886         *nfiles_p = 0;
887         dir = warn_opendir(path);
888         if (dir == NULL) {
889                 exit_code = EXIT_FAILURE;
890                 return NULL;    /* could not open the dir */
891         }
892         dn = NULL;
893         nfiles = 0;
894         while ((entry = readdir(dir)) != NULL) {
895                 char *fullname;
896
897                 /* are we going to list the file- it may be . or .. or a hidden file */
898                 if (entry->d_name[0] == '.') {
899                         if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
900                          && !(all_fmt & DISP_DOT)
901                         ) {
902                                 continue;
903                         }
904                         if (!(all_fmt & DISP_HIDDEN))
905                                 continue;
906                 }
907                 fullname = concat_path_file(path, entry->d_name);
908                 cur = my_stat(fullname, bb_basename(fullname), 0);
909                 if (!cur) {
910                         free(fullname);
911                         continue;
912                 }
913                 cur->fname_allocated = 1;
914                 cur->next = dn;
915                 dn = cur;
916                 nfiles++;
917         }
918         closedir(dir);
919
920         if (dn == NULL)
921                 return NULL;
922
923         /* now that we know how many files there are
924          * allocate memory for an array to hold dnode pointers
925          */
926         *nfiles_p = nfiles;
927         dnp = dnalloc(nfiles);
928         for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
929                 dnp[i] = dn;    /* save pointer to node in array */
930                 dn = dn->next;
931                 if (!dn)
932                         break;
933         }
934
935         return dnp;
936 }
937
938
939 int ls_main(int argc UNUSED_PARAM, char **argv)
940 {
941         struct dnode **dnd;
942         struct dnode **dnf;
943         struct dnode **dnp;
944         struct dnode *dn;
945         struct dnode *cur;
946         unsigned opt;
947         unsigned nfiles;
948         unsigned dnfiles;
949         unsigned dndirs;
950         unsigned i;
951 #if ENABLE_FEATURE_LS_COLOR
952         /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
953         /* coreutils 6.10:
954          * # ls --color=BOGUS
955          * ls: invalid argument 'BOGUS' for '--color'
956          * Valid arguments are:
957          * 'always', 'yes', 'force'
958          * 'never', 'no', 'none'
959          * 'auto', 'tty', 'if-tty'
960          * (and substrings: "--color=alwa" work too)
961          */
962         static const char ls_longopts[] ALIGN1 =
963                 "color\0" Optional_argument "\xff"; /* no short equivalent */
964         static const char color_str[] ALIGN1 =
965                 "always\0""yes\0""force\0"
966                 "auto\0""tty\0""if-tty\0";
967         /* need to initialize since --color has _an optional_ argument */
968         const char *color_opt = color_str; /* "always" */
969 #endif
970
971         INIT_G();
972
973         init_unicode();
974
975         all_fmt = LIST_SHORT |
976                 (ENABLE_FEATURE_LS_SORTFILES * (SORT_NAME | SORT_FORWARD));
977
978 #if ENABLE_FEATURE_AUTOWIDTH
979         /* obtain the terminal width */
980         get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);
981         /* go one less... */
982         terminal_width--;
983 #endif
984
985         /* process options */
986         IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
987 #if ENABLE_FEATURE_AUTOWIDTH
988         opt_complementary = "T+:w+"; /* -T N, -w N */
989         opt = getopt32(argv, ls_options, &tabstops, &terminal_width
990                                 IF_FEATURE_LS_COLOR(, &color_opt));
991 #else
992         opt = getopt32(argv, ls_options IF_FEATURE_LS_COLOR(, &color_opt));
993 #endif
994         for (i = 0; opt_flags[i] != (1U<<31); i++) {
995                 if (opt & (1 << i)) {
996                         unsigned flags = opt_flags[i];
997
998                         if (flags & LIST_MASK_TRIGGER)
999                                 all_fmt &= ~LIST_MASK;
1000                         if (flags & STYLE_MASK_TRIGGER)
1001                                 all_fmt &= ~STYLE_MASK;
1002                         if (flags & SORT_MASK_TRIGGER)
1003                                 all_fmt &= ~SORT_MASK;
1004                         if (flags & DISP_MASK_TRIGGER)
1005                                 all_fmt &= ~DISP_MASK;
1006                         if (flags & TIME_MASK)
1007                                 all_fmt &= ~TIME_MASK;
1008                         if (flags & LIST_CONTEXT)
1009                                 all_fmt |= STYLE_SINGLE;
1010                         /* huh?? opt cannot be 'l' */
1011                         //if (LS_DISP_HR && opt == 'l')
1012                         //      all_fmt &= ~LS_DISP_HR;
1013                         all_fmt |= flags;
1014                 }
1015         }
1016
1017 #if ENABLE_FEATURE_LS_COLOR
1018         /* find color bit value - last position for short getopt */
1019         if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1020                 char *p = getenv("LS_COLORS");
1021                 /* LS_COLORS is unset, or (not empty && not "none") ? */
1022                 if (!p || (p[0] && strcmp(p, "none") != 0))
1023                         show_color = 1;
1024         }
1025         if (opt & OPT_color) {
1026                 if (color_opt[0] == 'n')
1027                         show_color = 0;
1028                 else switch (index_in_substrings(color_str, color_opt)) {
1029                 case 3:
1030                 case 4:
1031                 case 5:
1032                         if (isatty(STDOUT_FILENO)) {
1033                 case 0:
1034                 case 1:
1035                 case 2:
1036                                 show_color = 1;
1037                         }
1038                 }
1039         }
1040 #endif
1041
1042         /* sort out which command line options take precedence */
1043         if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST))
1044                 all_fmt &= ~DISP_RECURSIVE;     /* no recurse if listing only dir */
1045         if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1046                 if (all_fmt & TIME_CHANGE)
1047                         all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME;
1048                 if (all_fmt & TIME_ACCESS)
1049                         all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME;
1050         }
1051         if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* only for long list */
1052                 all_fmt &= ~(LIST_ID_NUMERIC|LIST_FULLTIME|LIST_ID_NAME|LIST_ID_NUMERIC);
1053         if (ENABLE_FEATURE_LS_USERNAME)
1054                 if ((all_fmt & STYLE_MASK) == STYLE_LONG && (all_fmt & LIST_ID_NUMERIC))
1055                         all_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */
1056
1057         /* choose a display format */
1058         if (!(all_fmt & STYLE_MASK))
1059                 all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE);
1060
1061         argv += optind;
1062         if (!argv[0])
1063                 *--argv = (char*)".";
1064
1065         if (argv[1])
1066                 all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
1067
1068         /* stuff the command line file names into a dnode array */
1069         dn = NULL;
1070         nfiles = 0;
1071         do {
1072                 /* NB: follow links on command line unless -l or -s */
1073                 cur = my_stat(*argv, *argv, !(all_fmt & (STYLE_LONG|LIST_BLOCKS)));
1074                 argv++;
1075                 if (!cur)
1076                         continue;
1077                 cur->fname_allocated = 0;
1078                 cur->next = dn;
1079                 dn = cur;
1080                 nfiles++;
1081         } while (*argv);
1082
1083         /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1084         if (nfiles == 0)
1085                 return exit_code;
1086
1087         /* now that we know how many files there are
1088          * allocate memory for an array to hold dnode pointers
1089          */
1090         dnp = dnalloc(nfiles);
1091         for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1092                 dnp[i] = dn;    /* save pointer to node in array */
1093                 dn = dn->next;
1094                 if (!dn)
1095                         break;
1096         }
1097
1098         if (all_fmt & DISP_NOLIST) {
1099                 dnsort(dnp, nfiles);
1100                 showfiles(dnp, nfiles);
1101         } else {
1102                 dnd = splitdnarray(dnp, SPLIT_DIR);
1103                 dnf = splitdnarray(dnp, SPLIT_FILE);
1104                 dndirs = count_dirs(dnp, SPLIT_DIR);
1105                 dnfiles = nfiles - dndirs;
1106                 if (dnfiles > 0) {
1107                         dnsort(dnf, dnfiles);
1108                         showfiles(dnf, dnfiles);
1109                         if (ENABLE_FEATURE_CLEAN_UP)
1110                                 free(dnf);
1111                 }
1112                 if (dndirs > 0) {
1113                         dnsort(dnd, dndirs);
1114                         showdirs(dnd, dnfiles == 0);
1115                         if (ENABLE_FEATURE_CLEAN_UP)
1116                                 free(dnd);
1117                 }
1118         }
1119         if (ENABLE_FEATURE_CLEAN_UP)
1120                 dfree(dnp);
1121         return exit_code;
1122 }