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