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