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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95#include "libbb.h"
96#include "unicode.h"
97
98
99
100
101
102#if ENABLE_FTPD
103
104
105
106# undef CONFIG_FEATURE_LS_TIMESTAMPS
107# undef ENABLE_FEATURE_LS_TIMESTAMPS
108# undef IF_FEATURE_LS_TIMESTAMPS
109# undef IF_NOT_FEATURE_LS_TIMESTAMPS
110# define CONFIG_FEATURE_LS_TIMESTAMPS 1
111# define ENABLE_FEATURE_LS_TIMESTAMPS 1
112# define IF_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__
113# define IF_NOT_FEATURE_LS_TIMESTAMPS(...)
114#endif
115
116
117enum {
118TERMINAL_WIDTH = 80,
119
120SPLIT_FILE = 0,
121SPLIT_DIR = 1,
122SPLIT_SUBDIR = 2,
123
124
125
126
127
128LIST_INO = 1 << 0,
129LIST_BLOCKS = 1 << 1,
130LIST_MODEBITS = 1 << 2,
131LIST_NLINKS = 1 << 3,
132LIST_ID_NAME = 1 << 4,
133LIST_ID_NUMERIC = 1 << 5,
134LIST_CONTEXT = 1 << 6,
135LIST_SIZE = 1 << 7,
136LIST_DATE_TIME = 1 << 8,
137LIST_FULLTIME = 1 << 9,
138LIST_SYMLINK = 1 << 10,
139LIST_FILETYPE = 1 << 11,
140LIST_CLASSIFY = 1 << 12,
141LIST_MASK = (LIST_CLASSIFY << 1) - 1,
142
143
144DISP_DIRNAME = 1 << 13,
145DISP_HIDDEN = 1 << 14,
146DISP_DOT = 1 << 15,
147DISP_NOLIST = 1 << 16,
148DISP_RECURSIVE = 1 << 17,
149DISP_ROWS = 1 << 18,
150DISP_MASK = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1),
151
152
153STYLE_COLUMNAR = 1 << 19,
154STYLE_LONG = 2 << 19,
155STYLE_SINGLE = 3 << 19,
156STYLE_MASK = STYLE_SINGLE,
157
158
159TIME_CHANGE = (1 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
160TIME_ACCESS = (2 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
161TIME_MASK = (3 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
162
163
164SORT_REVERSE = 1 << 23,
165
166SORT_NAME = 0,
167SORT_SIZE = 1 << 24,
168SORT_ATIME = 2 << 24,
169SORT_CTIME = 3 << 24,
170SORT_MTIME = 4 << 24,
171SORT_VERSION = 5 << 24,
172SORT_EXT = 6 << 24,
173SORT_DIR = 7 << 24,
174SORT_MASK = (7 << 24) * ENABLE_FEATURE_LS_SORTFILES,
175
176LIST_LONG = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
177 LIST_DATE_TIME | LIST_SYMLINK,
178};
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194static const char ls_options[] ALIGN1 =
195 "Cadil1gnsxQAk"
196 IF_FEATURE_LS_TIMESTAMPS("cetu")
197 IF_FEATURE_LS_SORTFILES("SXrv")
198 IF_FEATURE_LS_FILETYPES("Fp")
199 IF_FEATURE_LS_RECURSIVE("R")
200 IF_SELINUX("KZ")
201 IF_FEATURE_LS_FOLLOWLINKS("LH")
202 IF_FEATURE_HUMAN_READABLE("h")
203 IF_FEATURE_AUTOWIDTH("T:w:")
204 ;
205enum {
206
207
208
209
210
211
212 OPT_g = (1 << 6),
213
214
215
216 OPT_Q = (1 << 10),
217
218
219
220 OPTBIT_c = 13,
221 OPTBIT_e,
222 OPTBIT_t,
223 OPTBIT_u,
224 OPTBIT_S = OPTBIT_c + 4 * ENABLE_FEATURE_LS_TIMESTAMPS,
225 OPTBIT_X,
226 OPTBIT_r,
227 OPTBIT_v,
228 OPTBIT_F = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES,
229 OPTBIT_p,
230 OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES,
231 OPTBIT_K = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE,
232 OPTBIT_Z,
233 OPTBIT_L = OPTBIT_K + 2 * ENABLE_SELINUX,
234 OPTBIT_H,
235 OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS,
236 OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE,
237 OPTBIT_w,
238 OPTBIT_color = OPTBIT_T + 2 * ENABLE_FEATURE_AUTOWIDTH,
239
240 OPT_c = (1 << OPTBIT_c) * ENABLE_FEATURE_LS_TIMESTAMPS,
241 OPT_e = (1 << OPTBIT_e) * ENABLE_FEATURE_LS_TIMESTAMPS,
242 OPT_t = (1 << OPTBIT_t) * ENABLE_FEATURE_LS_TIMESTAMPS,
243 OPT_u = (1 << OPTBIT_u) * ENABLE_FEATURE_LS_TIMESTAMPS,
244 OPT_S = (1 << OPTBIT_S) * ENABLE_FEATURE_LS_SORTFILES,
245 OPT_X = (1 << OPTBIT_X) * ENABLE_FEATURE_LS_SORTFILES,
246 OPT_r = (1 << OPTBIT_r) * ENABLE_FEATURE_LS_SORTFILES,
247 OPT_v = (1 << OPTBIT_v) * ENABLE_FEATURE_LS_SORTFILES,
248 OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES,
249 OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES,
250 OPT_R = (1 << OPTBIT_R) * ENABLE_FEATURE_LS_RECURSIVE,
251 OPT_K = (1 << OPTBIT_K) * ENABLE_SELINUX,
252 OPT_Z = (1 << OPTBIT_Z) * ENABLE_SELINUX,
253 OPT_L = (1 << OPTBIT_L) * ENABLE_FEATURE_LS_FOLLOWLINKS,
254 OPT_H = (1 << OPTBIT_H) * ENABLE_FEATURE_LS_FOLLOWLINKS,
255 OPT_h = (1 << OPTBIT_h) * ENABLE_FEATURE_HUMAN_READABLE,
256 OPT_T = (1 << OPTBIT_T) * ENABLE_FEATURE_AUTOWIDTH,
257 OPT_w = (1 << OPTBIT_w) * ENABLE_FEATURE_AUTOWIDTH,
258 OPT_color = (1 << OPTBIT_color) * ENABLE_FEATURE_LS_COLOR,
259};
260
261
262static const uint32_t opt_flags[] = {
263 STYLE_COLUMNAR,
264 DISP_HIDDEN | DISP_DOT,
265 DISP_NOLIST,
266 LIST_INO,
267 LIST_LONG | STYLE_LONG,
268 STYLE_SINGLE,
269 LIST_LONG | STYLE_LONG,
270 LIST_ID_NUMERIC | LIST_LONG | STYLE_LONG,
271 LIST_BLOCKS,
272 DISP_ROWS | STYLE_COLUMNAR,
273 0,
274 DISP_HIDDEN,
275 ENABLE_SELINUX * (LIST_CONTEXT|STYLE_SINGLE),
276#if ENABLE_FEATURE_LS_TIMESTAMPS
277 TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME),
278 LIST_FULLTIME,
279 ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME,
280 TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME),
281#endif
282#if ENABLE_FEATURE_LS_SORTFILES
283 SORT_SIZE,
284 SORT_EXT,
285 SORT_REVERSE,
286 SORT_VERSION,
287#endif
288#if ENABLE_FEATURE_LS_FILETYPES
289 LIST_FILETYPE | LIST_CLASSIFY,
290 LIST_FILETYPE,
291#endif
292#if ENABLE_FEATURE_LS_RECURSIVE
293 DISP_RECURSIVE,
294#endif
295#if ENABLE_SELINUX
296 LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME|STYLE_SINGLE,
297 LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT|STYLE_SINGLE,
298#endif
299 (1U << 31)
300
301};
302
303
304
305
306
307struct dnode {
308 const char *name;
309 const char *fullname;
310 struct dnode *dn_next;
311 IF_SELINUX(security_context_t sid;)
312 smallint fname_allocated;
313
314
315
316
317 mode_t dn_mode_lstat;
318 mode_t dn_mode_stat;
319
320
321
322
323
324
325
326
327
328
329
330 mode_t dn_mode;
331 off_t dn_size;
332#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
333 time_t dn_atime;
334 time_t dn_mtime;
335 time_t dn_ctime;
336#endif
337 ino_t dn_ino;
338 blkcnt_t dn_blocks;
339 nlink_t dn_nlink;
340 uid_t dn_uid;
341 gid_t dn_gid;
342 int dn_rdev_maj;
343 int dn_rdev_min;
344
345
346};
347
348struct globals {
349#if ENABLE_FEATURE_LS_COLOR
350 smallint show_color;
351# define G_show_color (G.show_color)
352#else
353# define G_show_color 0
354#endif
355 smallint exit_code;
356 unsigned all_fmt;
357#if ENABLE_FEATURE_AUTOWIDTH
358 unsigned terminal_width;
359# define G_terminal_width (G.terminal_width)
360#else
361# define G_terminal_width TERMINAL_WIDTH
362#endif
363#if ENABLE_FEATURE_LS_TIMESTAMPS
364
365 time_t current_time_t;
366#endif
367} FIX_ALIASING;
368#define G (*(struct globals*)&bb_common_bufsiz1)
369#define INIT_G() do { \
370 \
371 memset(&G, 0, sizeof(G)); \
372 IF_FEATURE_AUTOWIDTH(G_terminal_width = TERMINAL_WIDTH;) \
373 IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \
374} while (0)
375
376
377
378
379
380
381
382
383
384#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
385
386#define APPCHAR(mode) ("\0""|""\0""\0""/""\0""\0""\0""\0""\0""@""\0""=""\0""\0""\0" [TYPEINDEX(mode)])
387
388
389
390
391
392
393
394
395
396#define COLOR(mode) ( \
397 \
398 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
399 [TYPEINDEX(mode)])
400
401
402
403
404
405#define ATTR(mode) ( \
406 \
407 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
408 [TYPEINDEX(mode)])
409
410#if ENABLE_FEATURE_LS_COLOR
411
412static char fgcolor(mode_t mode)
413{
414 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
415 return COLOR(0xF000);
416 return COLOR(mode);
417}
418static char bold(mode_t mode)
419{
420 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
421 return ATTR(0xF000);
422 return ATTR(mode);
423}
424#endif
425
426#if ENABLE_FEATURE_LS_FILETYPES
427static char append_char(mode_t mode)
428{
429 if (!(G.all_fmt & LIST_FILETYPE))
430 return '\0';
431 if (S_ISDIR(mode))
432 return '/';
433 if (!(G.all_fmt & LIST_CLASSIFY))
434 return '\0';
435 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
436 return '*';
437 return APPCHAR(mode);
438}
439#endif
440
441static unsigned calc_name_len(const char *name)
442{
443 unsigned len;
444 uni_stat_t uni_stat;
445
446
447 name = printable_string(&uni_stat, name);
448
449 if (!(option_mask32 & OPT_Q)) {
450 return uni_stat.unicode_width;
451 }
452
453 len = 2 + uni_stat.unicode_width;
454 while (*name) {
455 if (*name == '"' || *name == '\\') {
456 len++;
457 }
458 name++;
459 }
460 return len;
461}
462
463
464
465
466
467
468
469
470static unsigned print_name(const char *name)
471{
472 unsigned len;
473 uni_stat_t uni_stat;
474
475
476 name = printable_string(&uni_stat, name);
477
478 if (!(option_mask32 & OPT_Q)) {
479 fputs(name, stdout);
480 return uni_stat.unicode_width;
481 }
482
483 len = 2 + uni_stat.unicode_width;
484 putchar('"');
485 while (*name) {
486 if (*name == '"' || *name == '\\') {
487 putchar('\\');
488 len++;
489 }
490 putchar(*name);
491 name++;
492 }
493 putchar('"');
494 return len;
495}
496
497
498
499
500
501static NOINLINE unsigned display_single(const struct dnode *dn)
502{
503 unsigned column = 0;
504 char *lpath;
505#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
506 struct stat statbuf;
507 char append;
508#endif
509
510#if ENABLE_FEATURE_LS_FILETYPES
511 append = append_char(dn->dn_mode);
512#endif
513
514
515
516 lpath = NULL;
517 if (G.all_fmt & LIST_SYMLINK)
518 if (S_ISLNK(dn->dn_mode))
519 lpath = xmalloc_readlink_or_warn(dn->fullname);
520
521 if (G.all_fmt & LIST_INO)
522 column += printf("%7llu ", (long long) dn->dn_ino);
523
524 if (G.all_fmt & LIST_BLOCKS)
525 column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1));
526 if (G.all_fmt & LIST_MODEBITS)
527 column += printf("%-10s ", (char *) bb_mode_string(dn->dn_mode));
528 if (G.all_fmt & LIST_NLINKS)
529 column += printf("%4lu ", (long) dn->dn_nlink);
530 if (G.all_fmt & LIST_ID_NUMERIC) {
531 if (option_mask32 & OPT_g)
532 column += printf("%-8u ", (int) dn->dn_gid);
533 else
534 column += printf("%-8u %-8u ",
535 (int) dn->dn_uid,
536 (int) dn->dn_gid);
537 }
538#if ENABLE_FEATURE_LS_USERNAME
539 else if (G.all_fmt & LIST_ID_NAME) {
540 if (option_mask32 & OPT_g) {
541 column += printf("%-8.8s ",
542 get_cached_groupname(dn->dn_gid));
543 } else {
544 column += printf("%-8.8s %-8.8s ",
545 get_cached_username(dn->dn_uid),
546 get_cached_groupname(dn->dn_gid));
547 }
548 }
549#endif
550 if (G.all_fmt & LIST_SIZE) {
551 if (S_ISBLK(dn->dn_mode) || S_ISCHR(dn->dn_mode)) {
552 column += printf("%4u, %3u ",
553 dn->dn_rdev_maj,
554 dn->dn_rdev_min);
555 } else {
556 if (option_mask32 & OPT_h) {
557 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
558
559 make_human_readable_str(dn->dn_size, 1, 0)
560 );
561 } else {
562 column += printf("%9"OFF_FMT"u ", dn->dn_size);
563 }
564 }
565 }
566#if ENABLE_FEATURE_LS_TIMESTAMPS
567 if (G.all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
568 char *filetime;
569 time_t ttime = dn->dn_mtime;
570 if (G.all_fmt & TIME_ACCESS)
571 ttime = dn->dn_atime;
572 if (G.all_fmt & TIME_CHANGE)
573 ttime = dn->dn_ctime;
574 filetime = ctime(&ttime);
575
576 if (G.all_fmt & LIST_FULLTIME) {
577
578
579
580 column += printf("%.24s ", filetime);
581 } else {
582
583 time_t age = G.current_time_t - ttime;
584 printf("%.6s ", filetime + 4);
585 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
586
587 printf("%.5s ", filetime + 11);
588 } else {
589 printf(" %.4s ", filetime + 20);
590 }
591 column += 13;
592 }
593 }
594#endif
595#if ENABLE_SELINUX
596 if (G.all_fmt & LIST_CONTEXT) {
597 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
598 freecon(dn->sid);
599 }
600#endif
601
602#if ENABLE_FEATURE_LS_COLOR
603 if (G_show_color) {
604 mode_t mode = dn->dn_mode_lstat;
605 if (!mode)
606 if (lstat(dn->fullname, &statbuf) == 0)
607 mode = statbuf.st_mode;
608 printf("\033[%u;%um", bold(mode), fgcolor(mode));
609 }
610#endif
611 column += print_name(dn->name);
612 if (G_show_color) {
613 printf("\033[0m");
614 }
615
616 if (lpath) {
617 printf(" -> ");
618#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
619 if ((G.all_fmt & LIST_FILETYPE) || G_show_color) {
620 mode_t mode = dn->dn_mode_stat;
621 if (!mode)
622 if (stat(dn->fullname, &statbuf) == 0)
623 mode = statbuf.st_mode;
624# if ENABLE_FEATURE_LS_FILETYPES
625 append = append_char(mode);
626# endif
627# if ENABLE_FEATURE_LS_COLOR
628 if (G_show_color) {
629 printf("\033[%u;%um", bold(mode), fgcolor(mode));
630 }
631# endif
632 }
633#endif
634 column += print_name(lpath) + 4;
635 free(lpath);
636 if (G_show_color) {
637 printf("\033[0m");
638 }
639 }
640#if ENABLE_FEATURE_LS_FILETYPES
641 if (G.all_fmt & LIST_FILETYPE) {
642 if (append) {
643 putchar(append);
644 column++;
645 }
646 }
647#endif
648
649 return column;
650}
651
652static void display_files(struct dnode **dn, unsigned nfiles)
653{
654 unsigned i, ncols, nrows, row, nc;
655 unsigned column;
656 unsigned nexttab;
657 unsigned column_width = 0;
658
659 if (G.all_fmt & STYLE_LONG) {
660 ncols = 1;
661 } else {
662
663 for (i = 0; dn[i]; i++) {
664 int len = calc_name_len(dn[i]->name);
665 if (column_width < len)
666 column_width = len;
667 }
668 column_width += 1 +
669 IF_SELINUX( ((G.all_fmt & LIST_CONTEXT) ? 33 : 0) + )
670 ((G.all_fmt & LIST_INO) ? 8 : 0) +
671 ((G.all_fmt & LIST_BLOCKS) ? 5 : 0);
672 ncols = (unsigned)G_terminal_width / column_width;
673 }
674
675 if (ncols > 1) {
676 nrows = nfiles / ncols;
677 if (nrows * ncols < nfiles)
678 nrows++;
679 } else {
680 nrows = nfiles;
681 ncols = 1;
682 }
683
684 column = 0;
685 nexttab = 0;
686 for (row = 0; row < nrows; row++) {
687 for (nc = 0; nc < ncols; nc++) {
688
689 if (G.all_fmt & DISP_ROWS)
690 i = (row * ncols) + nc;
691 else
692 i = (nc * nrows) + row;
693 if (i < nfiles) {
694 if (column > 0) {
695 nexttab -= column;
696 printf("%*s ", nexttab, "");
697 column += nexttab + 1;
698 }
699 nexttab = column + column_width;
700 column += display_single(dn[i]);
701 }
702 }
703 putchar('\n');
704 column = 0;
705 }
706}
707
708
709
710
711static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
712{
713 struct stat statbuf;
714 struct dnode *cur;
715
716 cur = xzalloc(sizeof(*cur));
717 cur->fullname = fullname;
718 cur->name = name;
719
720 if ((option_mask32 & OPT_L) || force_follow) {
721#if ENABLE_SELINUX
722 if (is_selinux_enabled()) {
723 getfilecon(fullname, &cur->sid);
724 }
725#endif
726 if (stat(fullname, &statbuf)) {
727 bb_simple_perror_msg(fullname);
728 G.exit_code = EXIT_FAILURE;
729 free(cur);
730 return NULL;
731 }
732 cur->dn_mode_stat = statbuf.st_mode;
733 } else {
734#if ENABLE_SELINUX
735 if (is_selinux_enabled()) {
736 lgetfilecon(fullname, &cur->sid);
737 }
738#endif
739 if (lstat(fullname, &statbuf)) {
740 bb_simple_perror_msg(fullname);
741 G.exit_code = EXIT_FAILURE;
742 free(cur);
743 return NULL;
744 }
745 cur->dn_mode_lstat = statbuf.st_mode;
746 }
747
748
749 cur->dn_mode = statbuf.st_mode ;
750 cur->dn_size = statbuf.st_size ;
751#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
752 cur->dn_atime = statbuf.st_atime ;
753 cur->dn_mtime = statbuf.st_mtime ;
754 cur->dn_ctime = statbuf.st_ctime ;
755#endif
756 cur->dn_ino = statbuf.st_ino ;
757 cur->dn_blocks = statbuf.st_blocks;
758 cur->dn_nlink = statbuf.st_nlink ;
759 cur->dn_uid = statbuf.st_uid ;
760 cur->dn_gid = statbuf.st_gid ;
761 cur->dn_rdev_maj = major(statbuf.st_rdev);
762 cur->dn_rdev_min = minor(statbuf.st_rdev);
763
764 return cur;
765}
766
767static unsigned count_dirs(struct dnode **dn, int which)
768{
769 unsigned dirs, all;
770
771 if (!dn)
772 return 0;
773
774 dirs = all = 0;
775 for (; *dn; dn++) {
776 const char *name;
777
778 all++;
779 if (!S_ISDIR((*dn)->dn_mode))
780 continue;
781
782 name = (*dn)->name;
783 if (which != SPLIT_SUBDIR
784
785 || name[0] != '.'
786 || (name[1] && (name[1] != '.' || name[2]))
787 ) {
788 dirs++;
789 }
790 }
791 return which != SPLIT_FILE ? dirs : all - dirs;
792}
793
794
795static struct dnode **dnalloc(unsigned num)
796{
797 if (num < 1)
798 return NULL;
799
800 num++;
801 return xzalloc(num * sizeof(struct dnode *));
802}
803
804#if ENABLE_FEATURE_LS_RECURSIVE
805static void dfree(struct dnode **dnp)
806{
807 unsigned i;
808
809 if (dnp == NULL)
810 return;
811
812 for (i = 0; dnp[i]; i++) {
813 struct dnode *cur = dnp[i];
814 if (cur->fname_allocated)
815 free((char*)cur->fullname);
816 free(cur);
817 }
818 free(dnp);
819}
820#else
821#define dfree(...) ((void)0)
822#endif
823
824
825static struct dnode **splitdnarray(struct dnode **dn, int which)
826{
827 unsigned dncnt, d;
828 struct dnode **dnp;
829
830 if (dn == NULL)
831 return NULL;
832
833
834 dncnt = count_dirs(dn, which);
835
836
837 dnp = dnalloc(dncnt);
838
839
840 for (d = 0; *dn; dn++) {
841 if (S_ISDIR((*dn)->dn_mode)) {
842 const char *name;
843
844 if (which == SPLIT_FILE)
845 continue;
846
847 name = (*dn)->name;
848 if ((which & SPLIT_DIR)
849
850 || name[0] != '.'
851 || (name[1] && (name[1] != '.' || name[2]))
852 ) {
853 dnp[d++] = *dn;
854 }
855 } else
856 if (which == SPLIT_FILE) {
857 dnp[d++] = *dn;
858 }
859 }
860 return dnp;
861}
862
863#if ENABLE_FEATURE_LS_SORTFILES
864static int sortcmp(const void *a, const void *b)
865{
866 struct dnode *d1 = *(struct dnode **)a;
867 struct dnode *d2 = *(struct dnode **)b;
868 unsigned sort_opts = G.all_fmt & SORT_MASK;
869 off_t dif;
870
871 dif = 0;
872
873
874 if (sort_opts == SORT_SIZE) {
875 dif = (d2->dn_size - d1->dn_size);
876 } else
877 if (sort_opts == SORT_ATIME) {
878 dif = (d2->dn_atime - d1->dn_atime);
879 } else
880 if (sort_opts == SORT_CTIME) {
881 dif = (d2->dn_ctime - d1->dn_ctime);
882 } else
883 if (sort_opts == SORT_MTIME) {
884 dif = (d2->dn_mtime - d1->dn_mtime);
885 } else
886 if (sort_opts == SORT_DIR) {
887 dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode);
888 } else
889#if defined(HAVE_STRVERSCMP) && HAVE_STRVERSCMP == 1
890 if (sort_opts == SORT_VERSION) {
891 dif = strverscmp(d1->name, d2->name);
892 } else
893#endif
894 if (sort_opts == SORT_EXT) {
895 dif = strcmp(strchrnul(d1->name, '.'), strchrnul(d2->name, '.'));
896 }
897 if (dif == 0) {
898
899 if (ENABLE_LOCALE_SUPPORT)
900 dif = strcoll(d1->name, d2->name);
901 else
902 dif = strcmp(d1->name, d2->name);
903 }
904
905
906 if (sizeof(dif) > sizeof(int)) {
907 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
908
909 if (dif != 0) {
910 dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
911 }
912 }
913
914 return (G.all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif;
915}
916
917static void dnsort(struct dnode **dn, int size)
918{
919 qsort(dn, size, sizeof(*dn), sortcmp);
920}
921
922static void sort_and_display_files(struct dnode **dn, unsigned nfiles)
923{
924 dnsort(dn, nfiles);
925 display_files(dn, nfiles);
926}
927#else
928# define dnsort(dn, size) ((void)0)
929# define sort_and_display_files(dn, nfiles) display_files(dn, nfiles)
930#endif
931
932
933static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p)
934{
935 struct dnode *dn, *cur, **dnp;
936 struct dirent *entry;
937 DIR *dir;
938 unsigned i, nfiles;
939
940 *nfiles_p = 0;
941 dir = warn_opendir(path);
942 if (dir == NULL) {
943 G.exit_code = EXIT_FAILURE;
944 return NULL;
945 }
946 dn = NULL;
947 nfiles = 0;
948 while ((entry = readdir(dir)) != NULL) {
949 char *fullname;
950
951
952 if (entry->d_name[0] == '.') {
953 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
954 && !(G.all_fmt & DISP_DOT)
955 ) {
956 continue;
957 }
958 if (!(G.all_fmt & DISP_HIDDEN))
959 continue;
960 }
961 fullname = concat_path_file(path, entry->d_name);
962 cur = my_stat(fullname, bb_basename(fullname), 0);
963 if (!cur) {
964 free(fullname);
965 continue;
966 }
967 cur->fname_allocated = 1;
968 cur->dn_next = dn;
969 dn = cur;
970 nfiles++;
971 }
972 closedir(dir);
973
974 if (dn == NULL)
975 return NULL;
976
977
978
979
980 *nfiles_p = nfiles;
981 dnp = dnalloc(nfiles);
982 for (i = 0; ; i++) {
983 dnp[i] = dn;
984 dn = dn->dn_next;
985 if (!dn)
986 break;
987 }
988
989 return dnp;
990}
991
992#if ENABLE_DESKTOP
993
994
995
996
997
998
999
1000
1001
1002
1003static off_t calculate_blocks(struct dnode **dn)
1004{
1005 uoff_t blocks = 1;
1006 if (dn) {
1007 while (*dn) {
1008
1009 blocks += (*dn)->dn_blocks;
1010 dn++;
1011 }
1012 }
1013
1014
1015
1016
1017 return blocks >> 1;
1018}
1019#endif
1020
1021static void scan_and_display_dirs_recur(struct dnode **dn, int first)
1022{
1023 unsigned nfiles;
1024 struct dnode **subdnp;
1025
1026 for (; *dn; dn++) {
1027 if (G.all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
1028 if (!first)
1029 bb_putchar('\n');
1030 first = 0;
1031 printf("%s:\n", (*dn)->fullname);
1032 }
1033 subdnp = scan_one_dir((*dn)->fullname, &nfiles);
1034#if ENABLE_DESKTOP
1035 if ((G.all_fmt & STYLE_MASK) == STYLE_LONG)
1036 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
1037#endif
1038 if (nfiles > 0) {
1039
1040 sort_and_display_files(subdnp, nfiles);
1041
1042 if (ENABLE_FEATURE_LS_RECURSIVE
1043 && (G.all_fmt & DISP_RECURSIVE)
1044 ) {
1045 struct dnode **dnd;
1046 unsigned dndirs;
1047
1048 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
1049 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
1050 if (dndirs > 0) {
1051 dnsort(dnd, dndirs);
1052 scan_and_display_dirs_recur(dnd, 0);
1053
1054 free(dnd);
1055 }
1056 }
1057
1058 dfree(subdnp);
1059 }
1060 }
1061}
1062
1063
1064int ls_main(int argc UNUSED_PARAM, char **argv)
1065{
1066 struct dnode **dnd;
1067 struct dnode **dnf;
1068 struct dnode **dnp;
1069 struct dnode *dn;
1070 struct dnode *cur;
1071 unsigned opt;
1072 unsigned nfiles;
1073 unsigned dnfiles;
1074 unsigned dndirs;
1075 unsigned i;
1076#if ENABLE_FEATURE_LS_COLOR
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087 static const char ls_longopts[] ALIGN1 =
1088 "color\0" Optional_argument "\xff";
1089 static const char color_str[] ALIGN1 =
1090 "always\0""yes\0""force\0"
1091 "auto\0""tty\0""if-tty\0";
1092
1093 const char *color_opt = color_str;
1094#endif
1095
1096 INIT_G();
1097
1098 init_unicode();
1099
1100 if (ENABLE_FEATURE_LS_SORTFILES)
1101 G.all_fmt = SORT_NAME;
1102
1103#if ENABLE_FEATURE_AUTOWIDTH
1104
1105 get_terminal_width_height(STDIN_FILENO, &G_terminal_width, NULL);
1106
1107 G_terminal_width--;
1108#endif
1109
1110
1111 IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
1112 opt_complementary =
1113
1114 IF_FEATURE_LS_TIMESTAMPS("el")
1115
1116
1117
1118 IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t"))
1119
1120 IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H")
1121 ":C-xl:x-Cl:l-xC"
1122 ":C-1:1-C"
1123 ":x-1:1-x"
1124 IF_FEATURE_LS_TIMESTAMPS(":c-u:u-c")
1125
1126 IF_FEATURE_AUTOWIDTH(":w+");
1127 opt = getopt32(argv, ls_options
1128 IF_FEATURE_AUTOWIDTH(, NULL, &G_terminal_width)
1129 IF_FEATURE_LS_COLOR(, &color_opt)
1130 );
1131 for (i = 0; opt_flags[i] != (1U << 31); i++) {
1132 if (opt & (1 << i)) {
1133 uint32_t flags = opt_flags[i];
1134
1135 if (flags & STYLE_MASK)
1136 G.all_fmt &= ~STYLE_MASK;
1137 if (flags & SORT_MASK)
1138 G.all_fmt &= ~SORT_MASK;
1139 if (flags & TIME_MASK)
1140 G.all_fmt &= ~TIME_MASK;
1141
1142 G.all_fmt |= flags;
1143 }
1144 }
1145
1146#if ENABLE_FEATURE_LS_COLOR
1147
1148 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1149 char *p = getenv("LS_COLORS");
1150
1151 if (!p || (p[0] && strcmp(p, "none") != 0))
1152 G_show_color = 1;
1153 }
1154 if (opt & OPT_color) {
1155 if (color_opt[0] == 'n')
1156 G_show_color = 0;
1157 else switch (index_in_substrings(color_str, color_opt)) {
1158 case 3:
1159 case 4:
1160 case 5:
1161 if (isatty(STDOUT_FILENO)) {
1162 case 0:
1163 case 1:
1164 case 2:
1165 G_show_color = 1;
1166 }
1167 }
1168 }
1169#endif
1170
1171
1172 if (ENABLE_FEATURE_LS_RECURSIVE && (G.all_fmt & DISP_NOLIST))
1173 G.all_fmt &= ~DISP_RECURSIVE;
1174 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1175 if (G.all_fmt & TIME_CHANGE)
1176 G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_CTIME;
1177 if (G.all_fmt & TIME_ACCESS)
1178 G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_ATIME;
1179 }
1180 if ((G.all_fmt & STYLE_MASK) != STYLE_LONG)
1181 G.all_fmt &= ~(LIST_ID_NUMERIC|LIST_ID_NAME|LIST_FULLTIME);
1182
1183
1184 if (!(G.all_fmt & STYLE_MASK))
1185 G.all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE);
1186
1187 argv += optind;
1188 if (!argv[0])
1189 *--argv = (char*)".";
1190
1191 if (argv[1])
1192 G.all_fmt |= DISP_DIRNAME;
1193
1194
1195 dn = NULL;
1196 nfiles = 0;
1197 do {
1198 cur = my_stat(*argv, *argv,
1199
1200 !((G.all_fmt & STYLE_MASK) == STYLE_LONG
1201 || (G.all_fmt & LIST_BLOCKS)
1202 || (option_mask32 & OPT_F)
1203 )
1204
1205 || (option_mask32 & OPT_H)
1206
1207 );
1208 argv++;
1209 if (!cur)
1210 continue;
1211
1212 cur->dn_next = dn;
1213 dn = cur;
1214 nfiles++;
1215 } while (*argv);
1216
1217
1218 if (nfiles == 0)
1219 return G.exit_code;
1220
1221
1222
1223
1224 dnp = dnalloc(nfiles);
1225 for (i = 0; ; i++) {
1226 dnp[i] = dn;
1227 dn = dn->dn_next;
1228 if (!dn)
1229 break;
1230 }
1231
1232 if (G.all_fmt & DISP_NOLIST) {
1233 sort_and_display_files(dnp, nfiles);
1234 } else {
1235 dnd = splitdnarray(dnp, SPLIT_DIR);
1236 dnf = splitdnarray(dnp, SPLIT_FILE);
1237 dndirs = count_dirs(dnp, SPLIT_DIR);
1238 dnfiles = nfiles - dndirs;
1239 if (dnfiles > 0) {
1240 sort_and_display_files(dnf, dnfiles);
1241 if (ENABLE_FEATURE_CLEAN_UP)
1242 free(dnf);
1243 }
1244 if (dndirs > 0) {
1245 dnsort(dnd, dndirs);
1246 scan_and_display_dirs_recur(dnd, dnfiles == 0);
1247 if (ENABLE_FEATURE_CLEAN_UP)
1248 free(dnd);
1249 }
1250 }
1251
1252 if (ENABLE_FEATURE_CLEAN_UP)
1253 dfree(dnp);
1254 return G.exit_code;
1255}
1256