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#include "libbb.h"
35
36#if ENABLE_FEATURE_ASSUME_UNICODE
37#include <wchar.h>
38#endif
39
40
41
42
43#if ENABLE_FTPD
44
45
46
47# undef CONFIG_FEATURE_LS_TIMESTAMPS
48# undef ENABLE_FEATURE_LS_TIMESTAMPS
49# undef USE_FEATURE_LS_TIMESTAMPS
50# undef SKIP_FEATURE_LS_TIMESTAMPS
51# define CONFIG_FEATURE_LS_TIMESTAMPS 1
52# define ENABLE_FEATURE_LS_TIMESTAMPS 1
53# define USE_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__
54# define SKIP_FEATURE_LS_TIMESTAMPS(...)
55#endif
56
57
58enum {
59
60TERMINAL_WIDTH = 80,
61COLUMN_GAP = 2,
62
63
64STYLE_COLUMNS = 1 << 21,
65STYLE_LONG = 2 << 21,
66STYLE_SINGLE = 3 << 21,
67STYLE_MASK = STYLE_SINGLE,
68
69
70
71LIST_INO = 1 << 0,
72LIST_BLOCKS = 1 << 1,
73LIST_MODEBITS = 1 << 2,
74LIST_NLINKS = 1 << 3,
75LIST_ID_NAME = 1 << 4,
76LIST_ID_NUMERIC = 1 << 5,
77LIST_CONTEXT = 1 << 6,
78LIST_SIZE = 1 << 7,
79
80LIST_DATE_TIME = 1 << 9,
81LIST_FULLTIME = 1 << 10,
82LIST_FILENAME = 1 << 11,
83LIST_SYMLINK = 1 << 12,
84LIST_FILETYPE = 1 << 13,
85LIST_EXEC = 1 << 14,
86LIST_MASK = (LIST_EXEC << 1) - 1,
87
88
89DISP_DIRNAME = 1 << 15,
90DISP_HIDDEN = 1 << 16,
91DISP_DOT = 1 << 17,
92DISP_NOLIST = 1 << 18,
93DISP_RECURSIVE = 1 << 19,
94DISP_ROWS = 1 << 20,
95DISP_MASK = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1),
96
97
98SORT_FORWARD = 0,
99SORT_REVERSE = 1 << 27,
100
101SORT_NAME = 0,
102SORT_SIZE = 1 << 28,
103SORT_ATIME = 2 << 28,
104SORT_CTIME = 3 << 28,
105SORT_MTIME = 4 << 28,
106SORT_VERSION = 5 << 28,
107SORT_EXT = 6 << 28,
108SORT_DIR = 7 << 28,
109SORT_MASK = (7 << 28) * ENABLE_FEATURE_LS_SORTFILES,
110
111
112TIME_CHANGE = (1 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
113TIME_ACCESS = (1 << 24) * ENABLE_FEATURE_LS_TIMESTAMPS,
114TIME_MASK = (3 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
115
116FOLLOW_LINKS = (1 << 25) * ENABLE_FEATURE_LS_FOLLOWLINKS,
117
118LS_DISP_HR = (1 << 26) * ENABLE_FEATURE_HUMAN_READABLE,
119
120LIST_SHORT = LIST_FILENAME,
121LIST_LONG = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
122 LIST_DATE_TIME | LIST_FILENAME | LIST_SYMLINK,
123
124SPLIT_DIR = 1,
125SPLIT_FILE = 0,
126SPLIT_SUBDIR = 2,
127
128};
129
130
131
132
133
134
135
136
137
138
139static const char ls_options[] ALIGN1 =
140 "Cadil1gnsxQAk"
141 USE_FEATURE_LS_TIMESTAMPS("cetu")
142 USE_FEATURE_LS_SORTFILES("SXrv")
143 USE_FEATURE_LS_FILETYPES("Fp")
144 USE_FEATURE_LS_FOLLOWLINKS("L")
145 USE_FEATURE_LS_RECURSIVE("R")
146 USE_FEATURE_HUMAN_READABLE("h")
147 USE_SELINUX("K")
148 USE_SELINUX("Z")
149 USE_FEATURE_AUTOWIDTH("T:w:")
150 ;
151enum {
152
153
154
155
156
157
158 OPT_g = (1 << 6),
159
160
161
162 OPT_Q = (1 << 10),
163
164
165};
166
167enum {
168 LIST_MASK_TRIGGER = 0,
169 STYLE_MASK_TRIGGER = STYLE_MASK,
170 DISP_MASK_TRIGGER = DISP_ROWS,
171 SORT_MASK_TRIGGER = SORT_MASK,
172};
173
174
175static const unsigned opt_flags[] = {
176 LIST_SHORT | STYLE_COLUMNS,
177 DISP_HIDDEN | DISP_DOT,
178 DISP_NOLIST,
179 LIST_INO,
180 LIST_LONG | STYLE_LONG,
181 LIST_SHORT | STYLE_SINGLE,
182 0,
183 LIST_ID_NUMERIC,
184 LIST_BLOCKS,
185 DISP_ROWS,
186 0,
187 DISP_HIDDEN,
188 ENABLE_SELINUX * LIST_CONTEXT,
189#if ENABLE_FEATURE_LS_TIMESTAMPS
190 TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME),
191 LIST_FULLTIME,
192 ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME,
193 TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME),
194#endif
195#if ENABLE_FEATURE_LS_SORTFILES
196 SORT_SIZE,
197 SORT_EXT,
198 SORT_REVERSE,
199 SORT_VERSION,
200#endif
201#if ENABLE_FEATURE_LS_FILETYPES
202 LIST_FILETYPE | LIST_EXEC,
203 LIST_FILETYPE,
204#endif
205#if ENABLE_FEATURE_LS_FOLLOWLINKS
206 FOLLOW_LINKS,
207#endif
208#if ENABLE_FEATURE_LS_RECURSIVE
209 DISP_RECURSIVE,
210#endif
211#if ENABLE_FEATURE_HUMAN_READABLE
212 LS_DISP_HR,
213#endif
214#if ENABLE_SELINUX
215 LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME,
216#endif
217#if ENABLE_SELINUX
218 LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT,
219#endif
220 (1U<<31)
221
222
223
224};
225
226
227
228
229
230struct dnode {
231 const char *name;
232 const char *fullname;
233 int allocated;
234 struct stat dstat;
235 USE_SELINUX(security_context_t sid;)
236 struct dnode *next;
237};
238
239static struct dnode **list_dir(const char *);
240static struct dnode **dnalloc(int);
241static int list_single(const struct dnode *);
242
243
244struct globals {
245#if ENABLE_FEATURE_LS_COLOR
246 smallint show_color;
247#endif
248 smallint exit_code;
249 unsigned all_fmt;
250#if ENABLE_FEATURE_AUTOWIDTH
251 unsigned tabstops;
252 unsigned terminal_width;
253#endif
254#if ENABLE_FEATURE_LS_TIMESTAMPS
255
256 time_t current_time_t;
257#endif
258};
259#define G (*(struct globals*)&bb_common_bufsiz1)
260#if ENABLE_FEATURE_LS_COLOR
261#define show_color (G.show_color )
262#else
263enum { show_color = 0 };
264#endif
265#define exit_code (G.exit_code )
266#define all_fmt (G.all_fmt )
267#if ENABLE_FEATURE_AUTOWIDTH
268#define tabstops (G.tabstops )
269#define terminal_width (G.terminal_width)
270#else
271enum {
272 tabstops = COLUMN_GAP,
273 terminal_width = TERMINAL_WIDTH,
274};
275#endif
276#define current_time_t (G.current_time_t)
277
278#define INIT_G() do { \
279 memset(&G, 0, sizeof(G)); \
280 USE_FEATURE_AUTOWIDTH(tabstops = COLUMN_GAP;) \
281 USE_FEATURE_AUTOWIDTH(terminal_width = TERMINAL_WIDTH;) \
282 USE_FEATURE_LS_TIMESTAMPS(time(¤t_time_t);) \
283} while (0)
284
285
286#if ENABLE_FEATURE_ASSUME_UNICODE
287
288static size_t mbstrlen(const char *string)
289{
290 size_t width = mbsrtowcs(NULL , &string,
291 MAXINT(size_t) , NULL );
292 if (width == (size_t)-1)
293 return strlen(string);
294 return width;
295}
296#else
297#define mbstrlen(string) strlen(string)
298#endif
299
300
301static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
302{
303 struct stat dstat;
304 struct dnode *cur;
305 USE_SELINUX(security_context_t sid = NULL;)
306
307 if ((all_fmt & FOLLOW_LINKS) || force_follow) {
308#if ENABLE_SELINUX
309 if (is_selinux_enabled()) {
310 getfilecon(fullname, &sid);
311 }
312#endif
313 if (stat(fullname, &dstat)) {
314 bb_simple_perror_msg(fullname);
315 exit_code = EXIT_FAILURE;
316 return 0;
317 }
318 } else {
319#if ENABLE_SELINUX
320 if (is_selinux_enabled()) {
321 lgetfilecon(fullname, &sid);
322 }
323#endif
324 if (lstat(fullname, &dstat)) {
325 bb_simple_perror_msg(fullname);
326 exit_code = EXIT_FAILURE;
327 return 0;
328 }
329 }
330
331 cur = xmalloc(sizeof(struct dnode));
332 cur->fullname = fullname;
333 cur->name = name;
334 cur->dstat = dstat;
335 USE_SELINUX(cur->sid = sid;)
336 return cur;
337}
338
339
340
341
342
343
344#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
345#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
346#define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)])
347
348
349
350
351
352
353
354
355
356#define COLOR(mode) ( \
357 \
358 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
359 [TYPEINDEX(mode)])
360
361
362
363
364
365#define ATTR(mode) ( \
366 \
367 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
368 [TYPEINDEX(mode)])
369
370#if ENABLE_FEATURE_LS_COLOR
371
372static char fgcolor(mode_t mode)
373{
374 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
375 return COLOR(0xF000);
376 return COLOR(mode);
377}
378static char bold(mode_t mode)
379{
380 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
381 return ATTR(0xF000);
382 return ATTR(mode);
383}
384#endif
385
386#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
387static char append_char(mode_t mode)
388{
389 if (!(all_fmt & LIST_FILETYPE))
390 return '\0';
391 if (S_ISDIR(mode))
392 return '/';
393 if (!(all_fmt & LIST_EXEC))
394 return '\0';
395 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
396 return '*';
397 return APPCHAR(mode);
398}
399#endif
400
401
402#define countdirs(A, B) count_dirs((A), (B), 1)
403#define countsubdirs(A, B) count_dirs((A), (B), 0)
404static int count_dirs(struct dnode **dn, int nfiles, int notsubdirs)
405{
406 int i, dirs;
407
408 if (!dn)
409 return 0;
410 dirs = 0;
411 for (i = 0; i < nfiles; i++) {
412 const char *name;
413 if (!S_ISDIR(dn[i]->dstat.st_mode))
414 continue;
415 name = dn[i]->name;
416 if (notsubdirs
417 || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
418 ) {
419 dirs++;
420 }
421 }
422 return dirs;
423}
424
425static int countfiles(struct dnode **dnp)
426{
427 int nfiles;
428 struct dnode *cur;
429
430 if (dnp == NULL)
431 return 0;
432 nfiles = 0;
433 for (cur = dnp[0]; cur->next; cur = cur->next)
434 nfiles++;
435 nfiles++;
436 return nfiles;
437}
438
439
440static struct dnode **dnalloc(int num)
441{
442 if (num < 1)
443 return NULL;
444
445 return xzalloc(num * sizeof(struct dnode *));
446}
447
448#if ENABLE_FEATURE_LS_RECURSIVE
449static void dfree(struct dnode **dnp, int nfiles)
450{
451 int i;
452
453 if (dnp == NULL)
454 return;
455
456 for (i = 0; i < nfiles; i++) {
457 struct dnode *cur = dnp[i];
458 if (cur->allocated)
459 free((char*)cur->fullname);
460 free(cur);
461 }
462 free(dnp);
463}
464#else
465#define dfree(...) ((void)0)
466#endif
467
468static struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which)
469{
470 int dncnt, i, d;
471 struct dnode **dnp;
472
473 if (dn == NULL || nfiles < 1)
474 return NULL;
475
476
477 if (which == SPLIT_SUBDIR)
478 dncnt = countsubdirs(dn, nfiles);
479 else {
480 dncnt = countdirs(dn, nfiles);
481 if (which == SPLIT_FILE)
482 dncnt = nfiles - dncnt;
483 }
484
485
486 dnp = dnalloc(dncnt);
487
488
489 for (d = i = 0; i < nfiles; i++) {
490 if (S_ISDIR(dn[i]->dstat.st_mode)) {
491 const char *name;
492 if (!(which & (SPLIT_DIR|SPLIT_SUBDIR)))
493 continue;
494 name = dn[i]->name;
495 if ((which & SPLIT_DIR)
496 || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
497 ) {
498 dnp[d++] = dn[i];
499 }
500 } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {
501 dnp[d++] = dn[i];
502 }
503 }
504 return dnp;
505}
506
507#if ENABLE_FEATURE_LS_SORTFILES
508static int sortcmp(const void *a, const void *b)
509{
510 struct dnode *d1 = *(struct dnode **)a;
511 struct dnode *d2 = *(struct dnode **)b;
512 unsigned sort_opts = all_fmt & SORT_MASK;
513 int dif;
514
515 dif = 0;
516
517
518 if (sort_opts == SORT_SIZE) {
519 dif = (int) (d2->dstat.st_size - d1->dstat.st_size);
520 } else if (sort_opts == SORT_ATIME) {
521 dif = (int) (d2->dstat.st_atime - d1->dstat.st_atime);
522 } else if (sort_opts == SORT_CTIME) {
523 dif = (int) (d2->dstat.st_ctime - d1->dstat.st_ctime);
524 } else if (sort_opts == SORT_MTIME) {
525 dif = (int) (d2->dstat.st_mtime - d1->dstat.st_mtime);
526 } else if (sort_opts == SORT_DIR) {
527 dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);
528
529
530 }
531
532 if (dif == 0) {
533
534 if (ENABLE_LOCALE_SUPPORT) dif = strcoll(d1->name, d2->name);
535 else dif = strcmp(d1->name, d2->name);
536 }
537
538 if (all_fmt & SORT_REVERSE) {
539 dif = -dif;
540 }
541 return dif;
542}
543
544static void dnsort(struct dnode **dn, int size)
545{
546 qsort(dn, size, sizeof(*dn), sortcmp);
547}
548#else
549#define dnsort(dn, size) ((void)0)
550#endif
551
552
553static void showfiles(struct dnode **dn, int nfiles)
554{
555 int i, ncols, nrows, row, nc;
556 int column = 0;
557 int nexttab = 0;
558 int column_width = 0;
559
560 if (dn == NULL || nfiles < 1)
561 return;
562
563 if (all_fmt & STYLE_LONG) {
564 ncols = 1;
565 } else {
566
567 for (i = 0; i < nfiles; i++) {
568 int len = mbstrlen(dn[i]->name);
569 if (column_width < len)
570 column_width = len;
571 }
572 column_width += tabstops +
573 USE_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
574 ((all_fmt & LIST_INO) ? 8 : 0) +
575 ((all_fmt & LIST_BLOCKS) ? 5 : 0);
576 ncols = (int) (terminal_width / column_width);
577 }
578
579 if (ncols > 1) {
580 nrows = nfiles / ncols;
581 if (nrows * ncols < nfiles)
582 nrows++;
583 } else {
584 nrows = nfiles;
585 ncols = 1;
586 }
587
588 for (row = 0; row < nrows; row++) {
589 for (nc = 0; nc < ncols; nc++) {
590
591 i = (nc * nrows) + row;
592 if (all_fmt & DISP_ROWS)
593 i = (row * ncols) + nc;
594 if (i < nfiles) {
595 if (column > 0) {
596 nexttab -= column;
597 printf("%*s", nexttab, "");
598 column += nexttab;
599 }
600 nexttab = column + column_width;
601 column += list_single(dn[i]);
602 }
603 }
604 putchar('\n');
605 column = 0;
606 }
607}
608
609
610static void showdirs(struct dnode **dn, int ndirs, int first)
611{
612 int i, nfiles;
613 struct dnode **subdnp;
614 int dndirs;
615 struct dnode **dnd;
616
617 if (dn == NULL || ndirs < 1)
618 return;
619
620 for (i = 0; i < ndirs; i++) {
621 if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
622 if (!first)
623 bb_putchar('\n');
624 first = 0;
625 printf("%s:\n", dn[i]->fullname);
626 }
627 subdnp = list_dir(dn[i]->fullname);
628 nfiles = countfiles(subdnp);
629 if (nfiles > 0) {
630
631 dnsort(subdnp, nfiles);
632 showfiles(subdnp, nfiles);
633 if (ENABLE_FEATURE_LS_RECURSIVE) {
634 if (all_fmt & DISP_RECURSIVE) {
635
636 dnd = splitdnarray(subdnp, nfiles, SPLIT_SUBDIR);
637 dndirs = countsubdirs(subdnp, nfiles);
638 if (dndirs > 0) {
639 dnsort(dnd, dndirs);
640 showdirs(dnd, dndirs, 0);
641
642 free(dnd);
643 }
644 }
645
646 dfree(subdnp, nfiles);
647 }
648 }
649 }
650}
651
652
653static struct dnode **list_dir(const char *path)
654{
655 struct dnode *dn, *cur, **dnp;
656 struct dirent *entry;
657 DIR *dir;
658 int i, nfiles;
659
660 if (path == NULL)
661 return NULL;
662
663 dn = NULL;
664 nfiles = 0;
665 dir = warn_opendir(path);
666 if (dir == NULL) {
667 exit_code = EXIT_FAILURE;
668 return NULL;
669 }
670 while ((entry = readdir(dir)) != NULL) {
671 char *fullname;
672
673
674 if (entry->d_name[0] == '.') {
675 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
676 && !(all_fmt & DISP_DOT)
677 ) {
678 continue;
679 }
680 if (!(all_fmt & DISP_HIDDEN))
681 continue;
682 }
683 fullname = concat_path_file(path, entry->d_name);
684 cur = my_stat(fullname, bb_basename(fullname), 0);
685 if (!cur) {
686 free(fullname);
687 continue;
688 }
689 cur->allocated = 1;
690 cur->next = dn;
691 dn = cur;
692 nfiles++;
693 }
694 closedir(dir);
695
696
697
698
699 if (dn == NULL)
700 return NULL;
701 dnp = dnalloc(nfiles);
702 for (i = 0, cur = dn; i < nfiles; i++) {
703 dnp[i] = cur;
704 cur = cur->next;
705 }
706
707 return dnp;
708}
709
710
711static int print_name(const char *name)
712{
713 if (option_mask32 & OPT_Q) {
714#if ENABLE_FEATURE_ASSUME_UNICODE
715 int len = 2 + mbstrlen(name);
716#else
717 int len = 2;
718#endif
719 putchar('"');
720 while (*name) {
721 if (*name == '"') {
722 putchar('\\');
723 len++;
724 }
725 putchar(*name++);
726 if (!ENABLE_FEATURE_ASSUME_UNICODE)
727 len++;
728 }
729 putchar('"');
730 return len;
731 }
732
733#if ENABLE_FEATURE_ASSUME_UNICODE
734 fputs(name, stdout);
735 return mbstrlen(name);
736#else
737 return printf("%s", name);
738#endif
739}
740
741
742static int list_single(const struct dnode *dn)
743{
744 int column = 0;
745 char *lpath = lpath;
746#if ENABLE_FEATURE_LS_TIMESTAMPS
747 char *filetime;
748 time_t ttime, age;
749#endif
750#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
751 struct stat info;
752 char append;
753#endif
754
755 if (dn->fullname == NULL)
756 return 0;
757
758#if ENABLE_FEATURE_LS_TIMESTAMPS
759 ttime = dn->dstat.st_mtime;
760 if (all_fmt & TIME_ACCESS)
761 ttime = dn->dstat.st_atime;
762 if (all_fmt & TIME_CHANGE)
763 ttime = dn->dstat.st_ctime;
764 filetime = ctime(&ttime);
765#endif
766#if ENABLE_FEATURE_LS_FILETYPES
767 append = append_char(dn->dstat.st_mode);
768#endif
769
770
771
772 if (all_fmt & LIST_SYMLINK)
773 if (S_ISLNK(dn->dstat.st_mode))
774 lpath = xmalloc_readlink_or_warn(dn->fullname);
775
776 if (all_fmt & LIST_INO)
777 column += printf("%7lu ", (long) dn->dstat.st_ino);
778 if (all_fmt & LIST_BLOCKS)
779 column += printf("%4"OFF_FMT"u ", (off_t) dn->dstat.st_blocks >> 1);
780 if (all_fmt & LIST_MODEBITS)
781 column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
782 if (all_fmt & LIST_NLINKS)
783 column += printf("%4lu ", (long) dn->dstat.st_nlink);
784#if ENABLE_FEATURE_LS_USERNAME
785 if (all_fmt & LIST_ID_NAME) {
786 if (option_mask32 & OPT_g) {
787 column += printf("%-8.8s",
788 get_cached_username(dn->dstat.st_uid));
789 } else {
790 column += printf("%-8.8s %-8.8s",
791 get_cached_username(dn->dstat.st_uid),
792 get_cached_groupname(dn->dstat.st_gid));
793 }
794 }
795#endif
796 if (all_fmt & LIST_ID_NUMERIC) {
797 if (option_mask32 & OPT_g)
798 column += printf("%-8u", (int) dn->dstat.st_uid);
799 else
800 column += printf("%-8u %-8u",
801 (int) dn->dstat.st_uid,
802 (int) dn->dstat.st_gid);
803 }
804 if (all_fmt & (LIST_SIZE )) {
805 if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
806 column += printf("%4u, %3u ",
807 (int) major(dn->dstat.st_rdev),
808 (int) minor(dn->dstat.st_rdev));
809 } else {
810 if (all_fmt & LS_DISP_HR) {
811 column += printf("%9s ",
812 make_human_readable_str(dn->dstat.st_size, 1, 0));
813 } else {
814 column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size);
815 }
816 }
817 }
818#if ENABLE_FEATURE_LS_TIMESTAMPS
819 if (all_fmt & LIST_FULLTIME)
820 column += printf("%24.24s ", filetime);
821 if (all_fmt & LIST_DATE_TIME)
822 if ((all_fmt & LIST_FULLTIME) == 0) {
823
824 age = current_time_t - ttime;
825 printf("%6.6s ", filetime + 4);
826 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
827
828 printf("%5.5s ", filetime + 11);
829 } else {
830 printf(" %4.4s ", filetime + 20);
831 }
832 column += 13;
833 }
834#endif
835#if ENABLE_SELINUX
836 if (all_fmt & LIST_CONTEXT) {
837 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
838 freecon(dn->sid);
839 }
840#endif
841 if (all_fmt & LIST_FILENAME) {
842#if ENABLE_FEATURE_LS_COLOR
843 if (show_color) {
844 info.st_mode = 0;
845 lstat(dn->fullname, &info);
846 printf("\033[%u;%um", bold(info.st_mode),
847 fgcolor(info.st_mode));
848 }
849#endif
850 column += print_name(dn->name);
851 if (show_color) {
852 printf("\033[0m");
853 }
854 }
855 if (all_fmt & LIST_SYMLINK) {
856 if (S_ISLNK(dn->dstat.st_mode) && lpath) {
857 printf(" -> ");
858#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
859#if ENABLE_FEATURE_LS_COLOR
860 info.st_mode = 0;
861#endif
862 if (stat(dn->fullname, &info) == 0) {
863 append = append_char(info.st_mode);
864 }
865#endif
866#if ENABLE_FEATURE_LS_COLOR
867 if (show_color) {
868 printf("\033[%u;%um", bold(info.st_mode),
869 fgcolor(info.st_mode));
870 }
871#endif
872 column += print_name(lpath) + 4;
873 if (show_color) {
874 printf("\033[0m");
875 }
876 free(lpath);
877 }
878 }
879#if ENABLE_FEATURE_LS_FILETYPES
880 if (all_fmt & LIST_FILETYPE) {
881 if (append) {
882 putchar(append);
883 column++;
884 }
885 }
886#endif
887
888 return column;
889}
890
891
892
893#if ENABLE_FEATURE_LS_COLOR
894
895
896static const char ls_color_opt[] ALIGN1 =
897 "color\0" Optional_argument "\xff"
898 ;
899#endif
900
901
902int ls_main(int argc UNUSED_PARAM, char **argv)
903{
904 struct dnode **dnd;
905 struct dnode **dnf;
906 struct dnode **dnp;
907 struct dnode *dn;
908 struct dnode *cur;
909 unsigned opt;
910 int nfiles;
911 int dnfiles;
912 int dndirs;
913 int i;
914
915 USE_FEATURE_LS_COLOR(const char *color_opt = "always";)
916
917 INIT_G();
918
919 all_fmt = LIST_SHORT |
920 (ENABLE_FEATURE_LS_SORTFILES * (SORT_NAME | SORT_FORWARD));
921
922#if ENABLE_FEATURE_AUTOWIDTH
923
924 get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);
925
926 terminal_width--;
927#endif
928
929
930 USE_FEATURE_LS_COLOR(applet_long_options = ls_color_opt;)
931#if ENABLE_FEATURE_AUTOWIDTH
932 opt_complementary = "T+:w+";
933 opt = getopt32(argv, ls_options, &tabstops, &terminal_width
934 USE_FEATURE_LS_COLOR(, &color_opt));
935#else
936 opt = getopt32(argv, ls_options USE_FEATURE_LS_COLOR(, &color_opt));
937#endif
938 for (i = 0; opt_flags[i] != (1U<<31); i++) {
939 if (opt & (1 << i)) {
940 unsigned flags = opt_flags[i];
941
942 if (flags & LIST_MASK_TRIGGER)
943 all_fmt &= ~LIST_MASK;
944 if (flags & STYLE_MASK_TRIGGER)
945 all_fmt &= ~STYLE_MASK;
946 if (flags & SORT_MASK_TRIGGER)
947 all_fmt &= ~SORT_MASK;
948 if (flags & DISP_MASK_TRIGGER)
949 all_fmt &= ~DISP_MASK;
950 if (flags & TIME_MASK)
951 all_fmt &= ~TIME_MASK;
952 if (flags & LIST_CONTEXT)
953 all_fmt |= STYLE_SINGLE;
954
955
956
957 all_fmt |= flags;
958 }
959 }
960
961#if ENABLE_FEATURE_LS_COLOR
962
963 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
964 char *p = getenv("LS_COLORS");
965
966 if (!p || (p[0] && strcmp(p, "none") != 0))
967 show_color = 1;
968 }
969 if (opt & (1 << i)) {
970 if (strcmp("always", color_opt) == 0)
971 show_color = 1;
972 else if (strcmp("never", color_opt) == 0)
973 show_color = 0;
974 else if (strcmp("auto", color_opt) == 0 && isatty(STDOUT_FILENO))
975 show_color = 1;
976 }
977#endif
978
979
980 if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST))
981 all_fmt &= ~DISP_RECURSIVE;
982 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
983 if (all_fmt & TIME_CHANGE)
984 all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME;
985 if (all_fmt & TIME_ACCESS)
986 all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME;
987 }
988 if ((all_fmt & STYLE_MASK) != STYLE_LONG)
989 all_fmt &= ~(LIST_ID_NUMERIC|LIST_FULLTIME|LIST_ID_NAME|LIST_ID_NUMERIC);
990 if (ENABLE_FEATURE_LS_USERNAME)
991 if ((all_fmt & STYLE_MASK) == STYLE_LONG && (all_fmt & LIST_ID_NUMERIC))
992 all_fmt &= ~LIST_ID_NAME;
993
994
995 if (!(all_fmt & STYLE_MASK))
996 all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE);
997
998 argv += optind;
999 if (!argv[0])
1000 *--argv = (char*)".";
1001
1002 if (argv[1])
1003 all_fmt |= DISP_DIRNAME;
1004
1005
1006 dn = NULL;
1007 nfiles = 0;
1008 do {
1009
1010 cur = my_stat(*argv, *argv, !(all_fmt & (STYLE_LONG|LIST_BLOCKS)));
1011 argv++;
1012 if (!cur)
1013 continue;
1014 cur->allocated = 0;
1015 cur->next = dn;
1016 dn = cur;
1017 nfiles++;
1018 } while (*argv);
1019
1020
1021
1022
1023 dnp = dnalloc(nfiles);
1024 for (i = 0, cur = dn; i < nfiles; i++) {
1025 dnp[i] = cur;
1026 cur = cur->next;
1027 }
1028
1029 if (all_fmt & DISP_NOLIST) {
1030 dnsort(dnp, nfiles);
1031 if (nfiles > 0)
1032 showfiles(dnp, nfiles);
1033 } else {
1034 dnd = splitdnarray(dnp, nfiles, SPLIT_DIR);
1035 dnf = splitdnarray(dnp, nfiles, SPLIT_FILE);
1036 dndirs = countdirs(dnp, nfiles);
1037 dnfiles = nfiles - dndirs;
1038 if (dnfiles > 0) {
1039 dnsort(dnf, dnfiles);
1040 showfiles(dnf, dnfiles);
1041 if (ENABLE_FEATURE_CLEAN_UP)
1042 free(dnf);
1043 }
1044 if (dndirs > 0) {
1045 dnsort(dnd, dndirs);
1046 showdirs(dnd, dndirs, dnfiles == 0);
1047 if (ENABLE_FEATURE_CLEAN_UP)
1048 free(dnd);
1049 }
1050 }
1051 if (ENABLE_FEATURE_CLEAN_UP)
1052 dfree(dnp, nfiles);
1053 return exit_code;
1054}
1055