1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32#include "libbb.h"
33#include "unicode.h"
34
35
36
37
38
39#if ENABLE_FTPD
40
41
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
54enum {
55TERMINAL_WIDTH = 80,
56COLUMN_GAP = 2,
57
58
59STYLE_COLUMNS = 1 << 21,
60STYLE_LONG = 2 << 21,
61STYLE_SINGLE = 3 << 21,
62STYLE_MASK = STYLE_SINGLE,
63
64
65
66LIST_INO = 1 << 0,
67LIST_BLOCKS = 1 << 1,
68LIST_MODEBITS = 1 << 2,
69LIST_NLINKS = 1 << 3,
70LIST_ID_NAME = 1 << 4,
71LIST_ID_NUMERIC = 1 << 5,
72LIST_CONTEXT = 1 << 6,
73LIST_SIZE = 1 << 7,
74
75LIST_DATE_TIME = 1 << 9,
76LIST_FULLTIME = 1 << 10,
77LIST_FILENAME = 1 << 11,
78LIST_SYMLINK = 1 << 12,
79LIST_FILETYPE = 1 << 13,
80LIST_EXEC = 1 << 14,
81LIST_MASK = (LIST_EXEC << 1) - 1,
82
83
84DISP_DIRNAME = 1 << 15,
85DISP_HIDDEN = 1 << 16,
86DISP_DOT = 1 << 17,
87DISP_NOLIST = 1 << 18,
88DISP_RECURSIVE = 1 << 19,
89DISP_ROWS = 1 << 20,
90DISP_MASK = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1),
91
92
93SORT_FORWARD = 0,
94SORT_REVERSE = 1 << 27,
95
96SORT_NAME = 0,
97SORT_SIZE = 1 << 28,
98SORT_ATIME = 2 << 28,
99SORT_CTIME = 3 << 28,
100SORT_MTIME = 4 << 28,
101SORT_VERSION = 5 << 28,
102SORT_EXT = 6 << 28,
103SORT_DIR = 7 << 28,
104SORT_MASK = (7 << 28) * ENABLE_FEATURE_LS_SORTFILES,
105
106
107TIME_CHANGE = (1 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
108TIME_ACCESS = (1 << 24) * ENABLE_FEATURE_LS_TIMESTAMPS,
109TIME_MASK = (3 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
110
111FOLLOW_LINKS = (1 << 25) * ENABLE_FEATURE_LS_FOLLOWLINKS,
112
113LS_DISP_HR = (1 << 26) * ENABLE_FEATURE_HUMAN_READABLE,
114
115LIST_SHORT = LIST_FILENAME,
116LIST_LONG = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
117 LIST_DATE_TIME | LIST_FILENAME | LIST_SYMLINK,
118
119SPLIT_DIR = 1,
120SPLIT_FILE = 0,
121SPLIT_SUBDIR = 2,
122};
123
124
125
126
127
128
129
130
131
132
133static const char ls_options[] ALIGN1 =
134 "Cadil1gnsxQAk"
135 IF_FEATURE_LS_TIMESTAMPS("cetu")
136 IF_FEATURE_LS_SORTFILES("SXrv")
137 IF_FEATURE_LS_FILETYPES("Fp")
138 IF_FEATURE_LS_FOLLOWLINKS("L")
139 IF_FEATURE_LS_RECURSIVE("R")
140 IF_FEATURE_HUMAN_READABLE("h")
141 IF_SELINUX("KZ")
142 IF_FEATURE_AUTOWIDTH("T:w:")
143 ;
144enum {
145
146
147
148
149
150
151 OPT_g = (1 << 6),
152
153
154
155 OPT_Q = (1 << 10),
156
157
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
170enum {
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
178static const unsigned opt_flags[] = {
179 LIST_SHORT | STYLE_COLUMNS,
180 DISP_HIDDEN | DISP_DOT,
181 DISP_NOLIST,
182 LIST_INO,
183 LIST_LONG | STYLE_LONG,
184 LIST_SHORT | STYLE_SINGLE,
185 0,
186 LIST_ID_NUMERIC,
187 LIST_BLOCKS,
188 DISP_ROWS,
189 0,
190 DISP_HIDDEN,
191 ENABLE_SELINUX * LIST_CONTEXT,
192#if ENABLE_FEATURE_LS_TIMESTAMPS
193 TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME),
194 LIST_FULLTIME,
195 ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME,
196 TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME),
197#endif
198#if ENABLE_FEATURE_LS_SORTFILES
199 SORT_SIZE,
200 SORT_EXT,
201 SORT_REVERSE,
202 SORT_VERSION,
203#endif
204#if ENABLE_FEATURE_LS_FILETYPES
205 LIST_FILETYPE | LIST_EXEC,
206 LIST_FILETYPE,
207#endif
208#if ENABLE_FEATURE_LS_FOLLOWLINKS
209 FOLLOW_LINKS,
210#endif
211#if ENABLE_FEATURE_LS_RECURSIVE
212 DISP_RECURSIVE,
213#endif
214#if ENABLE_FEATURE_HUMAN_READABLE
215 LS_DISP_HR,
216#endif
217#if ENABLE_SELINUX
218 LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME,
219#endif
220#if ENABLE_SELINUX
221 LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT,
222#endif
223 (1U<<31)
224
225
226
227};
228
229
230
231
232
233struct dnode {
234 const char *name;
235 const char *fullname;
236 struct dnode *next;
237 smallint fname_allocated;
238 struct stat dstat;
239 IF_SELINUX(security_context_t sid;)
240};
241
242struct 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;
250 unsigned terminal_width;
251#endif
252#if ENABLE_FEATURE_LS_TIMESTAMPS
253
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
261enum { 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
269enum {
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 \
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(¤t_time_t);) \
281} while (0)
282
283
284static 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
323
324
325
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
330
331
332
333
334
335
336
337
338#define COLOR(mode) ( \
339 \
340 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
341 [TYPEINDEX(mode)])
342
343
344
345
346
347#define ATTR(mode) ( \
348 \
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
354static char fgcolor(mode_t mode)
355{
356 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
357 return COLOR(0xF000);
358 return COLOR(mode);
359}
360static char bold(mode_t mode)
361{
362 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
363 return ATTR(0xF000);
364 return ATTR(mode);
365}
366#endif
367
368#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
369static 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
383static 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
399
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
409static struct dnode **dnalloc(unsigned num)
410{
411 if (num < 1)
412 return NULL;
413
414 num++;
415 return xzalloc(num * sizeof(struct dnode *));
416}
417
418#if ENABLE_FEATURE_LS_RECURSIVE
419static 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
439static 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
448 dncnt = count_dirs(dn, which);
449
450
451 dnp = dnalloc(dncnt);
452
453
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
474static 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;
482
483
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
495
496 }
497 if (dif == 0) {
498
499 if (ENABLE_LOCALE_SUPPORT)
500 dif = strcoll(d1->name, d2->name);
501 else
502 dif = strcmp(d1->name, d2->name);
503 }
504
505
506 if (sizeof(dif) > sizeof(int)) {
507 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
508
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
517static 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
526static unsigned calc_name_len(const char *name)
527{
528 unsigned len;
529 uni_stat_t uni_stat;
530
531
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
550
551
552
553
554
555
556static unsigned print_name(const char *name)
557{
558 unsigned len;
559 uni_stat_t uni_stat;
560
561
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
584
585
586
587static NOINLINE unsigned list_single(const struct dnode *dn)
588{
589 unsigned column = 0;
590 char *lpath = lpath;
591#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
592 struct stat info;
593 char append;
594#endif
595
596
597
598
599
600
601#if ENABLE_FEATURE_LS_FILETYPES
602 append = append_char(dn->dstat.st_mode);
603#endif
604
605
606
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_groupname(dn->dstat.st_gid));
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_gid);
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 )) {
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
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
665 if (all_fmt & LIST_FULLTIME)
666 column += printf("%.24s ", filetime);
667 else {
668
669 time_t age = current_time_t - ttime;
670 printf("%.6s ", filetime + 4);
671 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
672
673 printf("%.5s ", filetime + 11);
674 } else {
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;
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;
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
737static 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;
743
744 if (all_fmt & STYLE_LONG) {
745 ncols = 1;
746 } else {
747
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++;
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
772 if (all_fmt & DISP_ROWS)
773 i = (row * ncols) + nc;
774 else
775 i = (nc * nrows) + row;
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
794
795
796
797
798
799
800
801
802
803static off_t calculate_blocks(struct dnode **dn)
804{
805 uoff_t blocks = 1;
806 if (dn) {
807 while (*dn) {
808
809 blocks += (*dn)->dstat.st_blocks;
810 dn++;
811 }
812 }
813
814
815
816
817 return blocks >> 1;
818}
819#endif
820
821
822static struct dnode **list_dir(const char *, unsigned *);
823
824static void showdirs(struct dnode **dn, int first)
825{
826 unsigned nfiles;
827 unsigned dndirs;
828 struct dnode **subdnp;
829 struct dnode **dnd;
830
831
832
833
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
851 dnsort(subdnp, nfiles);
852 showfiles(subdnp, nfiles);
853 if (ENABLE_FEATURE_LS_RECURSIVE
854 && (all_fmt & DISP_RECURSIVE)
855 ) {
856
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
863 free(dnd);
864 }
865 }
866
867 dfree(subdnp);
868 }
869 }
870}
871
872
873
874static 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
882
883
884
885
886 *nfiles_p = 0;
887 dir = warn_opendir(path);
888 if (dir == NULL) {
889 exit_code = EXIT_FAILURE;
890 return NULL;
891 }
892 dn = NULL;
893 nfiles = 0;
894 while ((entry = readdir(dir)) != NULL) {
895 char *fullname;
896
897
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
924
925
926 *nfiles_p = nfiles;
927 dnp = dnalloc(nfiles);
928 for (i = 0; ; i++) {
929 dnp[i] = dn;
930 dn = dn->next;
931 if (!dn)
932 break;
933 }
934
935 return dnp;
936}
937
938
939int 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
953
954
955
956
957
958
959
960
961
962 static const char ls_longopts[] ALIGN1 =
963 "color\0" Optional_argument "\xff";
964 static const char color_str[] ALIGN1 =
965 "always\0""yes\0""force\0"
966 "auto\0""tty\0""if-tty\0";
967
968 const char *color_opt = color_str;
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
980 get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);
981
982 terminal_width--;
983#endif
984
985
986 IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
987#if ENABLE_FEATURE_AUTOWIDTH
988 opt_complementary = "T+:w+";
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
1011
1012
1013 all_fmt |= flags;
1014 }
1015 }
1016
1017#if ENABLE_FEATURE_LS_COLOR
1018
1019 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1020 char *p = getenv("LS_COLORS");
1021
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
1043 if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST))
1044 all_fmt &= ~DISP_RECURSIVE;
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)
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;
1056
1057
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;
1067
1068
1069 dn = NULL;
1070 nfiles = 0;
1071 do {
1072
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
1084 if (nfiles == 0)
1085 return exit_code;
1086
1087
1088
1089
1090 dnp = dnalloc(nfiles);
1091 for (i = 0; ; i++) {
1092 dnp[i] = dn;
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}
1123