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