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(name, stdout);
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
502 if (opt & OPT_s)
503 column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1));
504 if (opt & OPT_l) {
505
506 column += printf("%-10s ", (char *) bb_mode_string(dn->dn_mode));
507
508 column += printf("%4lu ", (long) dn->dn_nlink);
509
510 if (opt & OPT_n) {
511 if (opt & OPT_g)
512 column += printf("%-8u ", (int) dn->dn_gid);
513 else
514 column += printf("%-8u %-8u ",
515 (int) dn->dn_uid,
516 (int) dn->dn_gid);
517 }
518#if ENABLE_FEATURE_LS_USERNAME
519 else {
520 if (opt & OPT_g) {
521 column += printf("%-8.8s ",
522 get_cached_groupname(dn->dn_gid));
523 } else {
524 column += printf("%-8.8s %-8.8s ",
525 get_cached_username(dn->dn_uid),
526 get_cached_groupname(dn->dn_gid));
527 }
528 }
529#endif
530#if ENABLE_SELINUX
531 }
532 if (opt & OPT_Z) {
533 column += printf("%-32s ", dn->sid ? dn->sid : "?");
534 freecon(dn->sid);
535 }
536 if (opt & OPT_l) {
537#endif
538
539 if (S_ISBLK(dn->dn_mode) || S_ISCHR(dn->dn_mode)) {
540 column += printf("%4u, %3u ",
541 dn->dn_rdev_maj,
542 dn->dn_rdev_min);
543 } else {
544 if (opt & OPT_h) {
545 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
546
547 make_human_readable_str(dn->dn_size, 1, 0)
548 );
549 } else {
550 column += printf("%9"OFF_FMT"u ", dn->dn_size);
551 }
552 }
553#if ENABLE_FEATURE_LS_TIMESTAMPS
554
555 if (opt & OPT_full_time) {
556
557
558
559
560 char buf[sizeof("YYYY-mm-dd HH:MM:SS TIMEZONE")];
561 strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %z",
562 localtime(&dn->dn_time));
563 column += printf("%s ", buf);
564 } else {
565
566 char *filetime = ctime(&dn->dn_time);
567
568 time_t age = G.current_time_t - dn->dn_time;
569 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
570
571
572 printf("%.12s ", filetime + 4);
573 } else {
574
575
576 strchr(filetime + 20, '\n')[0] = ' ';
577 printf("%.7s%6s", filetime + 4, filetime + 20);
578 }
579 column += 13;
580 }
581#endif
582 }
583
584#if ENABLE_FEATURE_LS_COLOR
585 if (G_show_color) {
586 mode_t mode = dn->dn_mode_lstat;
587 if (!mode)
588 if (lstat(dn->fullname, &statbuf) == 0)
589 mode = statbuf.st_mode;
590 printf(ESC"[%u;%um", bold(mode), fgcolor(mode));
591 }
592#endif
593 column += print_name(dn->name);
594 if (G_show_color) {
595 printf(ESC"[m");
596 }
597
598 if (lpath) {
599 printf(" -> ");
600#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
601 if ((opt & (OPT_F|OPT_p))
602 || G_show_color
603 ) {
604 mode_t mode = dn->dn_mode_stat;
605 if (!mode)
606 if (stat(dn->fullname, &statbuf) == 0)
607 mode = statbuf.st_mode;
608# if ENABLE_FEATURE_LS_FILETYPES
609 append = append_char(mode);
610# endif
611# if ENABLE_FEATURE_LS_COLOR
612 if (G_show_color) {
613 printf(ESC"[%u;%um", bold(mode), fgcolor(mode));
614 }
615# endif
616 }
617#endif
618 column += print_name(lpath) + 4;
619 free(lpath);
620 if (G_show_color) {
621 printf(ESC"[m");
622 }
623 }
624#if ENABLE_FEATURE_LS_FILETYPES
625 if (opt & (OPT_F|OPT_p)) {
626 if (append) {
627 putchar(append);
628 column++;
629 }
630 }
631#endif
632
633 return column;
634}
635
636static void display_files(struct dnode **dn, unsigned nfiles)
637{
638 unsigned i, ncols, nrows, row, nc;
639 unsigned column;
640 unsigned nexttab;
641 unsigned column_width = 0;
642
643 if (option_mask32 & (OPT_l|OPT_1)) {
644 ncols = 1;
645 } else {
646
647 for (i = 0; dn[i]; i++) {
648 int len = calc_name_len(dn[i]->name);
649 if (column_width < len)
650 column_width = len;
651 }
652 column_width += 2
653 + ((option_mask32 & OPT_Z) ? 33 : 0)
654 + ((option_mask32 & OPT_i) ? 8 : 0)
655 + ((option_mask32 & OPT_s) ? 5 : 0)
656 ;
657 ncols = (unsigned)G_terminal_width / column_width;
658 }
659
660 if (ncols > 1) {
661 nrows = nfiles / ncols;
662 if (nrows * ncols < nfiles)
663 nrows++;
664 } else {
665 nrows = nfiles;
666 ncols = 1;
667 }
668
669 column = 0;
670 nexttab = 0;
671 for (row = 0; row < nrows; row++) {
672 for (nc = 0; nc < ncols; nc++) {
673
674 if (option_mask32 & OPT_x)
675 i = (row * ncols) + nc;
676 else
677 i = (nc * nrows) + row;
678 if (i < nfiles) {
679 if (column > 0) {
680 nexttab -= column;
681 printf("%*s", nexttab, "");
682 column += nexttab;
683 }
684 nexttab = column + column_width;
685 column += display_single(dn[i]);
686 }
687 }
688 putchar('\n');
689 column = 0;
690 }
691}
692
693
694
695
696static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
697{
698 struct stat statbuf;
699 struct dnode *cur;
700
701 cur = xzalloc(sizeof(*cur));
702 cur->fullname = fullname;
703 cur->name = name;
704
705 if ((option_mask32 & OPT_L) || force_follow) {
706#if ENABLE_SELINUX
707 if (option_mask32 & OPT_Z) {
708 getfilecon(fullname, &cur->sid);
709 }
710#endif
711 if (stat(fullname, &statbuf)) {
712 bb_simple_perror_msg(fullname);
713 G.exit_code = EXIT_FAILURE;
714 free(cur);
715 return NULL;
716 }
717 cur->dn_mode_stat = statbuf.st_mode;
718 } else {
719#if ENABLE_SELINUX
720 if (option_mask32 & OPT_Z) {
721 lgetfilecon(fullname, &cur->sid);
722 }
723#endif
724 if (lstat(fullname, &statbuf)) {
725 bb_simple_perror_msg(fullname);
726 G.exit_code = EXIT_FAILURE;
727 free(cur);
728 return NULL;
729 }
730 cur->dn_mode_lstat = statbuf.st_mode;
731 }
732
733
734 cur->dn_mode = statbuf.st_mode ;
735 cur->dn_size = statbuf.st_size ;
736#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
737 cur->dn_time = statbuf.st_mtime ;
738 if (option_mask32 & OPT_u)
739 cur->dn_time = statbuf.st_atime;
740 if (option_mask32 & OPT_c)
741 cur->dn_time = statbuf.st_ctime;
742#endif
743 cur->dn_ino = statbuf.st_ino ;
744 cur->dn_blocks = statbuf.st_blocks;
745 cur->dn_nlink = statbuf.st_nlink ;
746 cur->dn_uid = statbuf.st_uid ;
747 cur->dn_gid = statbuf.st_gid ;
748 cur->dn_rdev_maj = major(statbuf.st_rdev);
749 cur->dn_rdev_min = minor(statbuf.st_rdev);
750
751 return cur;
752}
753
754static unsigned count_dirs(struct dnode **dn, int which)
755{
756 unsigned dirs, all;
757
758 if (!dn)
759 return 0;
760
761 dirs = all = 0;
762 for (; *dn; dn++) {
763 const char *name;
764
765 all++;
766 if (!S_ISDIR((*dn)->dn_mode))
767 continue;
768
769 name = (*dn)->name;
770 if (which != SPLIT_SUBDIR
771
772 || name[0] != '.'
773 || (name[1] && (name[1] != '.' || name[2]))
774 ) {
775 dirs++;
776 }
777 }
778 return which != SPLIT_FILE ? dirs : all - dirs;
779}
780
781
782static struct dnode **dnalloc(unsigned num)
783{
784 if (num < 1)
785 return NULL;
786
787 num++;
788 return xzalloc(num * sizeof(struct dnode *));
789}
790
791#if ENABLE_FEATURE_LS_RECURSIVE
792static void dfree(struct dnode **dnp)
793{
794 unsigned i;
795
796 if (dnp == NULL)
797 return;
798
799 for (i = 0; dnp[i]; i++) {
800 struct dnode *cur = dnp[i];
801 if (cur->fname_allocated)
802 free((char*)cur->fullname);
803 free(cur);
804 }
805 free(dnp);
806}
807#else
808#define dfree(...) ((void)0)
809#endif
810
811
812static struct dnode **splitdnarray(struct dnode **dn, int which)
813{
814 unsigned dncnt, d;
815 struct dnode **dnp;
816
817 if (dn == NULL)
818 return NULL;
819
820
821 dncnt = count_dirs(dn, which);
822
823
824 dnp = dnalloc(dncnt);
825
826
827 for (d = 0; *dn; dn++) {
828 if (S_ISDIR((*dn)->dn_mode)) {
829 const char *name;
830
831 if (which == SPLIT_FILE)
832 continue;
833
834 name = (*dn)->name;
835 if ((which & SPLIT_DIR)
836
837 || name[0] != '.'
838 || (name[1] && (name[1] != '.' || name[2]))
839 ) {
840 dnp[d++] = *dn;
841 }
842 } else
843 if (which == SPLIT_FILE) {
844 dnp[d++] = *dn;
845 }
846 }
847 return dnp;
848}
849
850#if ENABLE_FEATURE_LS_SORTFILES
851static int sortcmp(const void *a, const void *b)
852{
853 struct dnode *d1 = *(struct dnode **)a;
854 struct dnode *d2 = *(struct dnode **)b;
855 unsigned opt = option_mask32;
856 off_t dif;
857
858 dif = 0;
859
860
861 if (opt & OPT_dirs_first) {
862 dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode);
863 if (dif != 0)
864 goto maybe_invert_and_ret;
865 }
866
867 if (opt & OPT_S) {
868 dif = (d2->dn_size - d1->dn_size);
869 } else
870 if (opt & OPT_t) {
871 dif = (d2->dn_time - d1->dn_time);
872 } else
873#if defined(HAVE_STRVERSCMP) && HAVE_STRVERSCMP == 1
874 if (opt & OPT_v) {
875 dif = strverscmp(d1->name, d2->name);
876 } else
877#endif
878 if (opt & OPT_X) {
879 dif = strcmp(strchrnul(d1->name, '.'), strchrnul(d2->name, '.'));
880 }
881 if (dif == 0) {
882
883 if (ENABLE_LOCALE_SUPPORT)
884 dif = strcoll(d1->name, d2->name);
885 else
886 dif = strcmp(d1->name, d2->name);
887 } else {
888
889 if (sizeof(dif) > sizeof(int)) {
890 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
891
892
893 dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
894 }
895 }
896 maybe_invert_and_ret:
897 return (opt & OPT_r) ? -(int)dif : (int)dif;
898}
899
900static void dnsort(struct dnode **dn, int size)
901{
902 qsort(dn, size, sizeof(*dn), sortcmp);
903}
904
905static void sort_and_display_files(struct dnode **dn, unsigned nfiles)
906{
907 dnsort(dn, nfiles);
908 display_files(dn, nfiles);
909}
910#else
911# define dnsort(dn, size) ((void)0)
912# define sort_and_display_files(dn, nfiles) display_files(dn, nfiles)
913#endif
914
915
916static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p)
917{
918 struct dnode *dn, *cur, **dnp;
919 struct dirent *entry;
920 DIR *dir;
921 unsigned i, nfiles;
922
923 *nfiles_p = 0;
924 dir = warn_opendir(path);
925 if (dir == NULL) {
926 G.exit_code = EXIT_FAILURE;
927 return NULL;
928 }
929 dn = NULL;
930 nfiles = 0;
931 while ((entry = readdir(dir)) != NULL) {
932 char *fullname;
933
934
935 if (entry->d_name[0] == '.') {
936 if (!(option_mask32 & (OPT_a|OPT_A)))
937 continue;
938 if (!(option_mask32 & OPT_a)
939 && (!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
940 ) {
941 continue;
942 }
943 }
944 fullname = concat_path_file(path, entry->d_name);
945 cur = my_stat(fullname, bb_basename(fullname), 0);
946 if (!cur) {
947 free(fullname);
948 continue;
949 }
950 cur->fname_allocated = 1;
951 cur->dn_next = dn;
952 dn = cur;
953 nfiles++;
954 }
955 closedir(dir);
956
957 if (dn == NULL)
958 return NULL;
959
960
961
962
963 *nfiles_p = nfiles;
964 dnp = dnalloc(nfiles);
965 for (i = 0; ; i++) {
966 dnp[i] = dn;
967 dn = dn->dn_next;
968 if (!dn)
969 break;
970 }
971
972 return dnp;
973}
974
975#if ENABLE_DESKTOP
976
977
978
979
980
981
982
983
984
985
986static off_t calculate_blocks(struct dnode **dn)
987{
988 uoff_t blocks = 1;
989 if (dn) {
990 while (*dn) {
991
992 blocks += (*dn)->dn_blocks;
993 dn++;
994 }
995 }
996
997
998
999
1000 return blocks >> 1;
1001}
1002#endif
1003
1004static void scan_and_display_dirs_recur(struct dnode **dn, int first)
1005{
1006 unsigned nfiles;
1007 struct dnode **subdnp;
1008
1009 for (; *dn; dn++) {
1010 if (G.show_dirname || (option_mask32 & OPT_R)) {
1011 if (!first)
1012 bb_putchar('\n');
1013 first = 0;
1014 printf("%s:\n", (*dn)->fullname);
1015 }
1016 subdnp = scan_one_dir((*dn)->fullname, &nfiles);
1017#if ENABLE_DESKTOP
1018 if (option_mask32 & (OPT_s|OPT_l)) {
1019 if (option_mask32 & OPT_h) {
1020 printf("total %-"HUMAN_READABLE_MAX_WIDTH_STR"s\n",
1021
1022 make_human_readable_str(calculate_blocks(subdnp) * 1024,
1023 0, 0)
1024 );
1025 } else {
1026 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
1027 }
1028 }
1029#endif
1030 if (nfiles > 0) {
1031
1032 sort_and_display_files(subdnp, nfiles);
1033
1034 if (ENABLE_FEATURE_LS_RECURSIVE
1035 && (option_mask32 & OPT_R)
1036 ) {
1037 struct dnode **dnd;
1038 unsigned dndirs;
1039
1040 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
1041 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
1042 if (dndirs > 0) {
1043 dnsort(dnd, dndirs);
1044 scan_and_display_dirs_recur(dnd, 0);
1045
1046 free(dnd);
1047 }
1048 }
1049
1050 dfree(subdnp);
1051 }
1052 }
1053}
1054
1055
1056int ls_main(int argc UNUSED_PARAM, char **argv)
1057{
1058 struct dnode **dnd;
1059 struct dnode **dnf;
1060 struct dnode **dnp;
1061 struct dnode *dn;
1062 struct dnode *cur;
1063 unsigned opt;
1064 unsigned nfiles;
1065 unsigned dnfiles;
1066 unsigned dndirs;
1067 unsigned i;
1068#if ENABLE_FEATURE_LS_COLOR
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079 static const char color_str[] ALIGN1 =
1080 "always\0""yes\0""force\0"
1081 "auto\0""tty\0""if-tty\0";
1082
1083 const char *color_opt = color_str;
1084#endif
1085#if ENABLE_LONG_OPTS
1086 static const char ls_longopts[] ALIGN1 =
1087 "full-time\0" No_argument "\xff"
1088 "group-directories-first\0" No_argument "\xfe"
1089 "color\0" Optional_argument "\xfd"
1090 ;
1091#endif
1092
1093 INIT_G();
1094
1095 init_unicode();
1096
1097#if ENABLE_FEATURE_LS_WIDTH
1098
1099 G_terminal_width = get_terminal_width(STDIN_FILENO);
1100
1101 G_terminal_width--;
1102#endif
1103
1104
1105 opt = getopt32long(argv, "^"
1106 ls_options
1107 "\0"
1108
1109 "nl:gl"
1110
1111 IF_FEATURE_LS_TIMESTAMPS(IF_LONG_OPTS(":\xff""l"))
1112
1113
1114
1115 IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t"))
1116
1117 IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H")
1118 ":C-xl:x-Cl:l-xC"
1119 ":C-1:1-C"
1120 ":x-1:1-x"
1121 IF_FEATURE_LS_TIMESTAMPS(":c-u:u-c")
1122
1123 IF_FEATURE_LS_WIDTH(":w+")
1124 , ls_longopts
1125 IF_FEATURE_LS_WIDTH(, NULL, &G_terminal_width)
1126 IF_FEATURE_LS_COLOR(, &color_opt)
1127 );
1128#if 0
1129 bb_error_msg("opt:0x%08x l:%x H:%x color:%x dirs:%x", opt, OPT_l, OPT_H, OPT_color, OPT_dirs_first);
1130 if (opt & OPT_c ) bb_error_msg("-c");
1131 if (opt & OPT_l ) bb_error_msg("-l");
1132 if (opt & OPT_H ) bb_error_msg("-H");
1133 if (opt & OPT_color ) bb_error_msg("--color");
1134 if (opt & OPT_dirs_first) bb_error_msg("--group-directories-first");
1135 if (opt & OPT_full_time ) bb_error_msg("--full-time");
1136 exit(0);
1137#endif
1138
1139#if ENABLE_SELINUX
1140 if (opt & OPT_Z) {
1141 if (!is_selinux_enabled())
1142 option_mask32 &= ~OPT_Z;
1143 }
1144#endif
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 && (opt & OPT_d))
1173 option_mask32 &= ~OPT_R;
1174 if (!(opt & OPT_l)) {
1175 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1176
1177
1178
1179
1180 if (opt & (OPT_c|OPT_u)) {
1181 option_mask32 |= OPT_t;
1182 }
1183 }
1184 }
1185
1186
1187 if (!(option_mask32 & (OPT_l|OPT_1|OPT_x|OPT_C)))
1188 option_mask32 |= (isatty(STDOUT_FILENO) ? OPT_C : OPT_1);
1189
1190 if (ENABLE_FTPD && applet_name[0] == 'f') {
1191
1192 option_mask32 |= OPT_dirs_first;
1193 }
1194
1195 argv += optind;
1196 if (!argv[0])
1197 *--argv = (char*)".";
1198
1199 if (argv[1])
1200 G.show_dirname = 1;
1201
1202
1203 dn = NULL;
1204 nfiles = 0;
1205 do {
1206 cur = my_stat(*argv, *argv,
1207
1208 !(option_mask32 & (OPT_l|OPT_i|OPT_s|OPT_F))
1209
1210 || (option_mask32 & OPT_H)
1211
1212 );
1213 argv++;
1214 if (!cur)
1215 continue;
1216
1217 cur->dn_next = dn;
1218 dn = cur;
1219 nfiles++;
1220 } while (*argv);
1221
1222
1223 if (nfiles == 0)
1224 return G.exit_code;
1225
1226
1227
1228
1229 dnp = dnalloc(nfiles);
1230 for (i = 0; ; i++) {
1231 dnp[i] = dn;
1232 dn = dn->dn_next;
1233 if (!dn)
1234 break;
1235 }
1236
1237 if (option_mask32 & OPT_d) {
1238 sort_and_display_files(dnp, nfiles);
1239 } else {
1240 dnd = splitdnarray(dnp, SPLIT_DIR);
1241 dnf = splitdnarray(dnp, SPLIT_FILE);
1242 dndirs = count_dirs(dnp, SPLIT_DIR);
1243 dnfiles = nfiles - dndirs;
1244 if (dnfiles > 0) {
1245 sort_and_display_files(dnf, dnfiles);
1246 if (ENABLE_FEATURE_CLEAN_UP)
1247 free(dnf);
1248 }
1249 if (dndirs > 0) {
1250 dnsort(dnd, dndirs);
1251 scan_and_display_dirs_recur(dnd, dnfiles == 0);
1252 if (ENABLE_FEATURE_CLEAN_UP)
1253 free(dnd);
1254 }
1255 }
1256
1257 if (ENABLE_FEATURE_CLEAN_UP)
1258 dfree(dnp);
1259 return G.exit_code;
1260}
1261