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_string(&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_string(&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 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
1020 }
1021#endif
1022 if (nfiles > 0) {
1023
1024 sort_and_display_files(subdnp, nfiles);
1025
1026 if (ENABLE_FEATURE_LS_RECURSIVE
1027 && (option_mask32 & OPT_R)
1028 ) {
1029 struct dnode **dnd;
1030 unsigned dndirs;
1031
1032 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
1033 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
1034 if (dndirs > 0) {
1035 dnsort(dnd, dndirs);
1036 scan_and_display_dirs_recur(dnd, 0);
1037
1038 free(dnd);
1039 }
1040 }
1041
1042 dfree(subdnp);
1043 }
1044 }
1045}
1046
1047
1048int ls_main(int argc UNUSED_PARAM, char **argv)
1049{
1050 struct dnode **dnd;
1051 struct dnode **dnf;
1052 struct dnode **dnp;
1053 struct dnode *dn;
1054 struct dnode *cur;
1055 unsigned opt;
1056 unsigned nfiles;
1057 unsigned dnfiles;
1058 unsigned dndirs;
1059 unsigned i;
1060#if ENABLE_FEATURE_LS_COLOR
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071 static const char color_str[] ALIGN1 =
1072 "always\0""yes\0""force\0"
1073 "auto\0""tty\0""if-tty\0";
1074
1075 const char *color_opt = color_str;
1076#endif
1077#if ENABLE_LONG_OPTS
1078 static const char ls_longopts[] ALIGN1 =
1079 "full-time\0" No_argument "\xff"
1080 "group-directories-first\0" No_argument "\xfe"
1081 "color\0" Optional_argument "\xfd"
1082 ;
1083#endif
1084
1085 INIT_G();
1086
1087 init_unicode();
1088
1089#if ENABLE_FEATURE_LS_WIDTH
1090
1091 G_terminal_width = get_terminal_width(STDIN_FILENO);
1092
1093 G_terminal_width--;
1094#endif
1095
1096
1097 opt = getopt32long(argv, "^"
1098 ls_options
1099 "\0"
1100
1101 "nl:gl"
1102
1103 IF_FEATURE_LS_TIMESTAMPS(IF_LONG_OPTS(":\xff""l"))
1104
1105
1106
1107 IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t"))
1108
1109 IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H")
1110 ":C-xl:x-Cl:l-xC"
1111 ":C-1:1-C"
1112 ":x-1:1-x"
1113 IF_FEATURE_LS_TIMESTAMPS(":c-u:u-c")
1114
1115 IF_FEATURE_LS_WIDTH(":w+")
1116 , ls_longopts
1117 IF_FEATURE_LS_WIDTH(, NULL, &G_terminal_width)
1118 IF_FEATURE_LS_COLOR(, &color_opt)
1119 );
1120#if 0
1121 bb_error_msg("opt:0x%08x l:%x H:%x color:%x dirs:%x", opt, OPT_l, OPT_H, OPT_color, OPT_dirs_first);
1122 if (opt & OPT_c ) bb_error_msg("-c");
1123 if (opt & OPT_l ) bb_error_msg("-l");
1124 if (opt & OPT_H ) bb_error_msg("-H");
1125 if (opt & OPT_color ) bb_error_msg("--color");
1126 if (opt & OPT_dirs_first) bb_error_msg("--group-directories-first");
1127 if (opt & OPT_full_time ) bb_error_msg("--full-time");
1128 exit(0);
1129#endif
1130
1131#if ENABLE_SELINUX
1132 if (opt & OPT_Z) {
1133 if (!is_selinux_enabled())
1134 option_mask32 &= ~OPT_Z;
1135 }
1136#endif
1137
1138#if ENABLE_FEATURE_LS_COLOR
1139
1140 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1141 char *p = getenv("LS_COLORS");
1142
1143 if (!p || (p[0] && strcmp(p, "none") != 0))
1144 G_show_color = 1;
1145 }
1146 if (opt & OPT_color) {
1147 if (color_opt[0] == 'n')
1148 G_show_color = 0;
1149 else switch (index_in_substrings(color_str, color_opt)) {
1150 case 3:
1151 case 4:
1152 case 5:
1153 if (isatty(STDOUT_FILENO)) {
1154 case 0:
1155 case 1:
1156 case 2:
1157 G_show_color = 1;
1158 }
1159 }
1160 }
1161#endif
1162
1163
1164 if (ENABLE_FEATURE_LS_RECURSIVE && (opt & OPT_d))
1165 option_mask32 &= ~OPT_R;
1166 if (!(opt & OPT_l)) {
1167 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1168
1169
1170
1171
1172 if (opt & (OPT_c|OPT_u)) {
1173 option_mask32 |= OPT_t;
1174 }
1175 }
1176 }
1177
1178
1179 if (!(option_mask32 & (OPT_l|OPT_1|OPT_x|OPT_C)))
1180 option_mask32 |= (isatty(STDOUT_FILENO) ? OPT_C : OPT_1);
1181
1182 if (ENABLE_FTPD && applet_name[0] == 'f') {
1183
1184 option_mask32 |= OPT_dirs_first;
1185 }
1186
1187 argv += optind;
1188 if (!argv[0])
1189 *--argv = (char*)".";
1190
1191 if (argv[1])
1192 G.show_dirname = 1;
1193
1194
1195 dn = NULL;
1196 nfiles = 0;
1197 do {
1198 cur = my_stat(*argv, *argv,
1199
1200 !(option_mask32 & (OPT_l|OPT_s|OPT_F))
1201
1202 || (option_mask32 & OPT_H)
1203
1204 );
1205 argv++;
1206 if (!cur)
1207 continue;
1208
1209 cur->dn_next = dn;
1210 dn = cur;
1211 nfiles++;
1212 } while (*argv);
1213
1214
1215 if (nfiles == 0)
1216 return G.exit_code;
1217
1218
1219
1220
1221 dnp = dnalloc(nfiles);
1222 for (i = 0; ; i++) {
1223 dnp[i] = dn;
1224 dn = dn->dn_next;
1225 if (!dn)
1226 break;
1227 }
1228
1229 if (option_mask32 & OPT_d) {
1230 sort_and_display_files(dnp, nfiles);
1231 } else {
1232 dnd = splitdnarray(dnp, SPLIT_DIR);
1233 dnf = splitdnarray(dnp, SPLIT_FILE);
1234 dndirs = count_dirs(dnp, SPLIT_DIR);
1235 dnfiles = nfiles - dndirs;
1236 if (dnfiles > 0) {
1237 sort_and_display_files(dnf, dnfiles);
1238 if (ENABLE_FEATURE_CLEAN_UP)
1239 free(dnf);
1240 }
1241 if (dndirs > 0) {
1242 dnsort(dnd, dndirs);
1243 scan_and_display_dirs_recur(dnd, dnfiles == 0);
1244 if (ENABLE_FEATURE_CLEAN_UP)
1245 free(dnd);
1246 }
1247 }
1248
1249 if (ENABLE_FEATURE_CLEAN_UP)
1250 dfree(dnp);
1251 return G.exit_code;
1252}
1253