aboutsummaryrefslogtreecommitdiff
path: root/coreutils/ls.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--coreutils/ls.c331
1 files changed, 250 insertions, 81 deletions
diff --git a/coreutils/ls.c b/coreutils/ls.c
index cc809b797..2153554e8 100644
--- a/coreutils/ls.c
+++ b/coreutils/ls.c
@@ -109,8 +109,11 @@
109//usage:#define ls_full_usage "\n\n" 109//usage:#define ls_full_usage "\n\n"
110//usage: "List directory contents\n" 110//usage: "List directory contents\n"
111//usage: "\n -1 One column output" 111//usage: "\n -1 One column output"
112//usage: "\n -a Include names starting with ." 112//usage: "\n -a Include names starting with ." IF_PLATFORM_MINGW32(" and hidden files")
113//usage: "\n -A Like -a, but exclude . and .." 113//usage: "\n -A Like -a, but exclude . and .."
114//usage: IF_PLATFORM_MINGW32(
115//usage: "\n -aa,-AA Like -a,-A but omit hidden system files"
116//usage: )
114////usage: "\n -C List by columns" - don't show, this is a default anyway 117////usage: "\n -C List by columns" - don't show, this is a default anyway
115//usage: "\n -x List by lines" 118//usage: "\n -x List by lines"
116//usage: "\n -d List directory names, not contents" 119//usage: "\n -d List directory names, not contents"
@@ -126,6 +129,8 @@
126//usage: "\n -F Append indicator (one of */=@|) to names" 129//usage: "\n -F Append indicator (one of */=@|) to names"
127//usage: ) 130//usage: )
128//usage: "\n -l Long format" 131//usage: "\n -l Long format"
132////usage: "\n -g Long format without group column"
133////TODO: support -G too ("suppress owner column", GNUism)
129//usage: "\n -i List inode numbers" 134//usage: "\n -i List inode numbers"
130//usage: "\n -n List numeric UIDs and GIDs instead of names" 135//usage: "\n -n List numeric UIDs and GIDs instead of names"
131//usage: "\n -s List allocated blocks" 136//usage: "\n -s List allocated blocks"
@@ -159,6 +164,8 @@
159//usage: IF_FEATURE_LS_WIDTH( 164//usage: IF_FEATURE_LS_WIDTH(
160//usage: "\n -w N Format N columns wide" 165//usage: "\n -w N Format N columns wide"
161//usage: ) 166//usage: )
167////usage: "\n -Q Double-quote names"
168////usage: "\n -q Replace unprintable chars with '?'"
162//usage: IF_FEATURE_LS_COLOR( 169//usage: IF_FEATURE_LS_COLOR(
163//usage: "\n --color[={always,never,auto}]" 170//usage: "\n --color[={always,never,auto}]"
164//usage: ) 171//usage: )
@@ -196,27 +203,47 @@ SPLIT_SUBDIR = 2,
196 203
197/* -Cadi1l Std options, busybox always supports */ 204/* -Cadi1l Std options, busybox always supports */
198/* -gnsxA Std options, busybox always supports */ 205/* -gnsxA Std options, busybox always supports */
199/* -Q GNU option, busybox always supports */ 206/* -Q GNU option, busybox always supports: */
200/* -k Std option, busybox always supports (by ignoring) */ 207/* -Q, --quote-name */
201/* It means "for -s, show sizes in kbytes" */ 208/* enclose entry names in double quotes */
202/* Seems to only affect "POSIXLY_CORRECT=1 ls -sk" */
203/* since otherwise -s shows kbytes anyway */
204/* -LHRctur Std options, busybox optionally supports */ 209/* -LHRctur Std options, busybox optionally supports */
205/* -Fp Std options, busybox optionally supports */ 210/* -Fp Std options, busybox optionally supports */
206/* -SXvhTw GNU options, busybox optionally supports */ 211/* -SXvhTw GNU options, busybox optionally supports */
207/* -T WIDTH Ignored (we don't use tabs on output) */ 212/* -T WIDTH Ignored (we don't use tabs on output) */
208/* -Z SELinux mandated option, busybox optionally supports */ 213/* -Z SELinux mandated option, busybox optionally supports */
214/* -q Std option, busybox always supports: */
215/* https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html: */
216/* Force each instance of non-printable filename characters and */
217/* <tab> characters to be written as the <question-mark> ('?') */
218/* character. Implementations may provide this option by default */
219/* if the output is to a terminal device. */
220/* -k Std option, busybox always supports (by ignoring) */
221/* It means "for -s, show sizes in kbytes" */
222/* Seems to only affect "POSIXLY_CORRECT=1 ls -sk" */
223/* since otherwise -s shows kbytes anyway */
209#define ls_options \ 224#define ls_options \
210 "Cadi1lgnsxAk" /* 12 opts, total 12 */ \ 225 "Cadi1lgnsxA" /* 11 opts, total 11 */ \
211 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 14 */ \ 226 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 13 */ \
212 IF_FEATURE_LS_RECURSIVE("R") /* 1, 15 */ \ 227 IF_FEATURE_LS_RECURSIVE("R") /* 1, 14 */ \
213 IF_SELINUX("Z") /* 1, 16 */ \ 228 IF_SELINUX("Z") /* 1, 15 */ \
214 "Q" /* 1, 17 */ \ 229 "Q" /* 1, 16 */ \
215 IF_FEATURE_LS_TIMESTAMPS("ctu") /* 3, 20 */ \ 230 IF_FEATURE_LS_TIMESTAMPS("ctu") /* 3, 19 */ \
216 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 24 */ \ 231 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 23 */ \
217 IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 26 */ \ 232 IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 25 */ \
218 IF_FEATURE_HUMAN_READABLE("h") /* 1, 27 */ \ 233 IF_FEATURE_HUMAN_READABLE("h") /* 1, 26 */ \
219 IF_FEATURE_LS_WIDTH("T:w:") /* 2, 29 */ 234 IF_FEATURE_LS_WIDTH("T:w:") /* 2, 28 */ \
235 IF_LONG_OPTS("\xff") /* 1, 29 */ \
236 IF_LONG_OPTS("\xfe") /* 1, 30 */ \
237 IF_LONG_OPTS("\xfd") /* 1, 31 */ \
238 "qk" /* 2, 33 */
239
240#if ENABLE_LONG_OPTS
241static const char ls_longopts[] ALIGN1 =
242 "full-time\0" No_argument "\xff"
243 "group-directories-first\0" No_argument "\xfe"
244 IF_FEATURE_LS_COLOR("color\0" Optional_argument "\xfd")
245;
246#endif
220 247
221enum { 248enum {
222 OPT_C = (1 << 0), 249 OPT_C = (1 << 0),
@@ -230,29 +257,31 @@ enum {
230 OPT_s = (1 << 8), 257 OPT_s = (1 << 8),
231 OPT_x = (1 << 9), 258 OPT_x = (1 << 9),
232 OPT_A = (1 << 10), 259 OPT_A = (1 << 10),
233 //OPT_k = (1 << 11),
234 260
235 OPTBIT_F = 12, 261 OPTBIT_F = 11,
236 OPTBIT_p, /* 13 */ 262 OPTBIT_p, /* 12 */
237 OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES, 263 OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES,
238 OPTBIT_Z = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE, 264 OPTBIT_Z = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE,
239 OPTBIT_Q = OPTBIT_Z + 1 * ENABLE_SELINUX, 265 OPTBIT_Q = OPTBIT_Z + 1 * ENABLE_SELINUX,
240 OPTBIT_c, /* 17 */ 266 OPTBIT_c, /* 16 */
241 OPTBIT_t, /* 18 */ 267 OPTBIT_t, /* 17 */
242 OPTBIT_u, /* 19 */ 268 OPTBIT_u, /* 18 */
243 OPTBIT_S = OPTBIT_c + 3 * ENABLE_FEATURE_LS_TIMESTAMPS, 269 OPTBIT_S = OPTBIT_c + 3 * ENABLE_FEATURE_LS_TIMESTAMPS,
244 OPTBIT_X, /* 21 */ 270 OPTBIT_X, /* 20 */
245 OPTBIT_r, /* 22 */ 271 OPTBIT_r, /* 21 */
246 OPTBIT_v, /* 23 */ 272 OPTBIT_v, /* 22 */
247 OPTBIT_L = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES, 273 OPTBIT_L = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES,
248 OPTBIT_H, /* 25 */ 274 OPTBIT_H, /* 24 */
249 OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS, 275 OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS,
250 OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE, 276 OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE,
251 OPTBIT_w, /* 28 */ 277 OPTBIT_w, /* 27 */
252 OPTBIT_full_time = OPTBIT_T + 2 * ENABLE_FEATURE_LS_WIDTH, 278 OPTBIT_full_time = OPTBIT_T + 2 * ENABLE_FEATURE_LS_WIDTH,
253 OPTBIT_dirs_first, 279 OPTBIT_dirs_first,
254 OPTBIT_color, /* 31 */ 280 OPTBIT_color, /* 30 */
255 /* with long opts, we use all 32 bits */ 281 OPTBIT_q = OPTBIT_color + 1, /* 31 */
282 OPTBIT_k = OPTBIT_q + 1, /* 32 */
283 /* with all options enabled, we use all 32 bits and even one extra bit! */
284 /* this works because -k is ignored, and getopt32 allows such "ignore" options past 31th bit */
256 285
257 OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES, 286 OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES,
258 OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES, 287 OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES,
@@ -274,6 +303,8 @@ enum {
274 OPT_full_time = (1 << OPTBIT_full_time ) * ENABLE_LONG_OPTS, 303 OPT_full_time = (1 << OPTBIT_full_time ) * ENABLE_LONG_OPTS,
275 OPT_dirs_first = (1 << OPTBIT_dirs_first) * ENABLE_LONG_OPTS, 304 OPT_dirs_first = (1 << OPTBIT_dirs_first) * ENABLE_LONG_OPTS,
276 OPT_color = (1 << OPTBIT_color ) * ENABLE_FEATURE_LS_COLOR, 305 OPT_color = (1 << OPTBIT_color ) * ENABLE_FEATURE_LS_COLOR,
306 OPT_q = (1 << OPTBIT_q),
307 //-k is ignored: OPT_k = (1 << OPTBIT_k),
277}; 308};
278 309
279/* 310/*
@@ -316,6 +347,9 @@ struct dnode {
316 int dn_rdev_min; 347 int dn_rdev_min;
317// dev_t dn_dev; 348// dev_t dn_dev;
318// blksize_t dn_blksize; 349// blksize_t dn_blksize;
350#if ENABLE_PLATFORM_MINGW32
351 DWORD dn_attr;
352#endif
319}; 353};
320 354
321struct globals { 355struct globals {
@@ -327,6 +361,7 @@ struct globals {
327#endif 361#endif
328 smallint exit_code; 362 smallint exit_code;
329 smallint show_dirname; 363 smallint show_dirname;
364 smallint tty_out;
330#if ENABLE_FEATURE_LS_WIDTH 365#if ENABLE_FEATURE_LS_WIDTH
331 unsigned terminal_width; 366 unsigned terminal_width;
332# define G_terminal_width (G.terminal_width) 367# define G_terminal_width (G.terminal_width)
@@ -337,22 +372,31 @@ struct globals {
337 /* Do time() just once. Saves one syscall per file for "ls -l" */ 372 /* Do time() just once. Saves one syscall per file for "ls -l" */
338 time_t current_time_t; 373 time_t current_time_t;
339#endif 374#endif
375#if ENABLE_PLATFORM_MINGW32
376 int a_count, A_count;
377# define HIDSYS (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)
378#endif
340} FIX_ALIASING; 379} FIX_ALIASING;
341#define G (*(struct globals*)bb_common_bufsiz1) 380#define G (*(struct globals*)bb_common_bufsiz1)
342#define INIT_G() do { \ 381#define INIT_G() do { \
343 setup_common_bufsiz(); \ 382 setup_common_bufsiz(); \
344 /* we have to zero it out because of NOEXEC */ \ 383 /* we have to zero it out because of NOEXEC */ \
345 memset(&G, 0, sizeof(G)); \ 384 memset(&G, 0, sizeof(G)); \
346 IF_FEATURE_LS_WIDTH(G_terminal_width = TERMINAL_WIDTH;) \ 385 IF_FEATURE_LS_WIDTH(G_terminal_width = ~0U;) \
347 IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \ 386 IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \
348} while (0) 387} while (0)
349 388
350#define ESC "\033" 389#define ESC "\033"
351 390
391static int G_isatty(void)
392{
393 if (!G.tty_out) /* not known yet? */
394 G.tty_out = isatty(STDOUT_FILENO) + 1;
395 return (G.tty_out == 2);
396}
352 397
353/*** Output code ***/ 398/*** Output code ***/
354 399
355
356/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket 400/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
357 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file 401 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
358 * 3/7:multiplexed char/block device) 402 * 3/7:multiplexed char/block device)
@@ -415,56 +459,93 @@ static char append_char(mode_t mode)
415} 459}
416#endif 460#endif
417 461
462/* Return the number of used columns.
463 * Note that only columnar output uses return value.
464 * -l and -1 modes don't care.
465 * coreutils 7.2 also supports:
466 * ls -b (--escape) = octal escapes (although it doesn't look like working)
467 * ls -N (--literal) = not escape at all
468 */
418static unsigned calc_name_len(const char *name) 469static unsigned calc_name_len(const char *name)
419{ 470{
420 unsigned len; 471 unsigned len;
421 uni_stat_t uni_stat; 472 uni_stat_t uni_stat;
422 473
423 // TODO: quote tab as \t, etc, if -Q 474 if (!(option_mask32 & (OPT_q|OPT_Q)))
424 name = printable_string2(&uni_stat, name); 475 return strlen(name);
425 476
426 if (!(option_mask32 & OPT_Q)) { 477 if (!(option_mask32 & OPT_Q)) {
478 /* the most likely branch: "ls" to tty (it auto-enables -q behavior) */
479 printable_string2(&uni_stat, name);
427 return uni_stat.unicode_width; 480 return uni_stat.unicode_width;
428 } 481 }
429 482
430 len = 2 + uni_stat.unicode_width; 483 len = 2 + strlen(name);
431 while (*name) { 484 while (*name) {
485 unsigned char ch = (unsigned char)*name;
486 if (ch < ' ' || ch > 0x7e) {
487 ch -= 7;
488 if (ch <= 6) {
489 /* quote chars 7..13 as \a,b,t,n,v,f,r */
490 goto two;
491 }
492 /* other chars <32 or >126 as \ooo octal */
493 len += 3;
494 goto next;
495 }
432 if (*name == '"' || *name == '\\') { 496 if (*name == '"' || *name == '\\') {
497 two:
433 len++; 498 len++;
434 } 499 }
500 next:
435 name++; 501 name++;
436 } 502 }
437 return len; 503 return len;
438} 504}
439
440/* Return the number of used columns.
441 * Note that only columnar output uses return value.
442 * -l and -1 modes don't care.
443 * coreutils 7.2 also supports:
444 * ls -b (--escape) = octal escapes (although it doesn't look like working)
445 * ls -N (--literal) = not escape at all
446 */
447static unsigned print_name(const char *name) 505static unsigned print_name(const char *name)
448{ 506{
449 unsigned len; 507 unsigned len;
450 uni_stat_t uni_stat; 508 uni_stat_t uni_stat;
451 509
452 // TODO: quote tab as \t, etc, if -Q 510 if (!(option_mask32 & (OPT_q|OPT_Q))) {
453 name = printable_string2(&uni_stat, name); 511 fputs_stdout(name);
512 return strlen(name);
513 }
454 514
455 if (!(option_mask32 & OPT_Q)) { 515 if (!(option_mask32 & OPT_Q)) {
516 /* the most likely branch: "ls" to tty (it auto-enables -q behavior) */
517 name = printable_string2(&uni_stat, name);
456 fputs_stdout(name); 518 fputs_stdout(name);
457 return uni_stat.unicode_width; 519 return uni_stat.unicode_width;
458 } 520 }
459 521
460 len = 2 + uni_stat.unicode_width; 522 len = 2 + strlen(name);
461 putchar('"'); 523 putchar('"');
462 while (*name) { 524 while (*name) {
463 if (*name == '"' || *name == '\\') { 525 unsigned char ch = (unsigned char)*name;
526 if (ch < ' ' || ch > 0x7e) {
527 putchar('\\');
528 ch -= 7;
529 if (ch <= 6) {
530 /* quote chars 7..13 as \a,b,t,n,v,f,r */
531 ch = c_escape_conv_str07[1 + 3 * ch];
532 goto two;
533 }
534 /* other chars <32 or >126 as \ooo octal */
535 ch = (unsigned char)*name;
536 putchar('0' + (ch>>6));
537 putchar('0' + ((ch>>3) & 7));
538 ch = '0' + (ch & 7);
539 len += 3;
540 goto put_ch;
541 }
542 if (ch == '"' || ch == '\\') {
464 putchar('\\'); 543 putchar('\\');
544 two:
465 len++; 545 len++;
466 } 546 }
467 putchar(*name); 547 put_ch:
548 putchar(ch);
468 name++; 549 name++;
469 } 550 }
470 putchar('"'); 551 putchar('"');
@@ -497,7 +578,11 @@ static NOINLINE unsigned display_single(const struct dnode *dn)
497 lpath = xmalloc_readlink_or_warn(dn->fullname); 578 lpath = xmalloc_readlink_or_warn(dn->fullname);
498 579
499 if (opt & OPT_i) /* show inode# */ 580 if (opt & OPT_i) /* show inode# */
500 column += printf("%7llu ", (long long) dn->dn_ino); 581#if !ENABLE_FEATURE_EXTRA_FILE_DATA
582 column += printf("%7"LL_FMT"u ", (long long) dn->dn_ino);
583#else
584 column += printf("%19"LL_FMT"u ", (long long) dn->dn_ino);
585#endif
501 if (opt & OPT_s) { /* show allocated blocks */ 586 if (opt & OPT_s) { /* show allocated blocks */
502 if (opt & OPT_h) { 587 if (opt & OPT_h) {
503 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ", 588 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
@@ -646,7 +731,7 @@ static void display_files(struct dnode **dn, unsigned nfiles)
646 unsigned i, ncols, nrows, row, nc; 731 unsigned i, ncols, nrows, row, nc;
647 unsigned column; 732 unsigned column;
648 unsigned nexttab; 733 unsigned nexttab;
649 unsigned column_width = 0; /* used only by coulmnal output */ 734 unsigned column_width = 0; /* used only by columnar output */
650 735
651 if (option_mask32 & (OPT_l|OPT_1)) { 736 if (option_mask32 & (OPT_l|OPT_1)) {
652 ncols = 1; 737 ncols = 1;
@@ -659,7 +744,11 @@ static void display_files(struct dnode **dn, unsigned nfiles)
659 } 744 }
660 column_width += 2 745 column_width += 2
661 + ((option_mask32 & OPT_Z) ? 33 : 0) /* context width */ 746 + ((option_mask32 & OPT_Z) ? 33 : 0) /* context width */
747#if !ENABLE_FEATURE_EXTRA_FILE_DATA
662 + ((option_mask32 & OPT_i) ? 8 : 0) /* inode# width */ 748 + ((option_mask32 & OPT_i) ? 8 : 0) /* inode# width */
749#else
750 + ((option_mask32 & OPT_i) ? 20 : 0) /* inode# width */
751#endif
663 + ((option_mask32 & OPT_s) ? 5 : 0) /* "alloc block" width */ 752 + ((option_mask32 & OPT_s) ? 5 : 0) /* "alloc block" width */
664 ; 753 ;
665 ncols = (unsigned)G_terminal_width / column_width; 754 ncols = (unsigned)G_terminal_width / column_width;
@@ -691,6 +780,11 @@ static void display_files(struct dnode **dn, unsigned nfiles)
691 } 780 }
692 nexttab = column + column_width; 781 nexttab = column + column_width;
693 column += display_single(dn[i]); 782 column += display_single(dn[i]);
783 } else {
784 /* if -w999999999, ncols can be very large */
785 //bb_error_msg(" col:%u ncol:%u i:%i", nc, ncols, i); sleep1();
786 /* without "break", we loop millions of times here */
787 break;
694 } 788 }
695 } 789 }
696 putchar('\n'); 790 putchar('\n');
@@ -740,6 +834,9 @@ static struct dnode *my_stat(const char *fullname, const char *name, int force_f
740 834
741 /* cur->dstat = statbuf: */ 835 /* cur->dstat = statbuf: */
742 cur->dn_mode = statbuf.st_mode ; 836 cur->dn_mode = statbuf.st_mode ;
837#if ENABLE_PLATFORM_MINGW32
838 cur->dn_attr = statbuf.st_attr ;
839#endif
743 cur->dn_size = statbuf.st_size ; 840 cur->dn_size = statbuf.st_size ;
744#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES 841#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
745 cur->dn_time = statbuf.st_mtime ; 842 cur->dn_time = statbuf.st_mtime ;
@@ -920,6 +1017,15 @@ static void sort_and_display_files(struct dnode **dn, unsigned nfiles)
920# define sort_and_display_files(dn, nfiles) display_files(dn, nfiles) 1017# define sort_and_display_files(dn, nfiles) display_files(dn, nfiles)
921#endif 1018#endif
922 1019
1020#if ENABLE_PLATFORM_MINGW32
1021static int hide_file(DWORD attr)
1022{
1023 return
1024 ((attr & FILE_ATTRIBUTE_HIDDEN) && !(option_mask32 & (OPT_a|OPT_A))) ||
1025 (((attr & HIDSYS) == HIDSYS) && MAX(G.a_count, G.A_count) > 1);
1026}
1027#endif
1028
923/* Returns NULL-terminated malloced vector of pointers (or NULL) */ 1029/* Returns NULL-terminated malloced vector of pointers (or NULL) */
924static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p) 1030static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p)
925{ 1031{
@@ -927,6 +1033,9 @@ static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p)
927 struct dirent *entry; 1033 struct dirent *entry;
928 DIR *dir; 1034 DIR *dir;
929 unsigned i, nfiles; 1035 unsigned i, nfiles;
1036#if ENABLE_PLATFORM_MINGW32
1037 struct stat statbuf;
1038#endif
930 1039
931 *nfiles_p = 0; 1040 *nfiles_p = 0;
932 dir = warn_opendir(path); 1041 dir = warn_opendir(path);
@@ -949,9 +1058,29 @@ static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p)
949 continue; /* if only -A, skip . and .. but show other dotfiles */ 1058 continue; /* if only -A, skip . and .. but show other dotfiles */
950 } 1059 }
951 } 1060 }
1061#if ENABLE_PLATFORM_MINGW32
1062 if (has_dos_drive_prefix(path) && path[2] == '\0')
1063 fullname = xasprintf("%s%s", path, entry->d_name);
1064 else
1065#endif
952 fullname = concat_path_file(path, entry->d_name); 1066 fullname = concat_path_file(path, entry->d_name);
1067#if ENABLE_PLATFORM_MINGW32
1068 /* When showing link targets we must first check the
1069 * attributes of the link itself to see if it's hidden. */
1070 if ((option_mask32 & OPT_L) && !lstat(fullname, &statbuf)) {
1071 if (hide_file(statbuf.st_attr)) {
1072 free(fullname);
1073 continue;
1074 }
1075 }
1076#endif
953 cur = my_stat(fullname, bb_basename(fullname), 0); 1077 cur = my_stat(fullname, bb_basename(fullname), 0);
1078#if !ENABLE_PLATFORM_MINGW32
954 if (!cur) { 1079 if (!cur) {
1080#else
1081 if (!cur || hide_file(cur->dn_attr)) {
1082 /* skip invalid or hidden files */
1083#endif
955 free(fullname); 1084 free(fullname);
956 continue; 1085 continue;
957 } 1086 }
@@ -1060,6 +1189,18 @@ static void scan_and_display_dirs_recur(struct dnode **dn, int first)
1060 } 1189 }
1061} 1190}
1062 1191
1192#if ENABLE_PLATFORM_MINGW32
1193static char *fix_backslash(char *p)
1194{
1195 const char *flag = getenv("BB_FIX_BACKSLASH");
1196 int value = flag ? atoi(flag) : 0;
1197
1198 if (value == 1)
1199 bs_to_slash(p);
1200 return p;
1201}
1202#endif
1203
1063 1204
1064int ls_main(int argc UNUSED_PARAM, char **argv) 1205int ls_main(int argc UNUSED_PARAM, char **argv)
1065{ /* ^^^^^^^^^^^^^^^^^ note: if FTPD, argc can be wrong, see ftpd.c */ 1206{ /* ^^^^^^^^^^^^^^^^^ note: if FTPD, argc can be wrong, see ftpd.c */
@@ -1090,25 +1231,11 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1090 /* need to initialize since --color has _an optional_ argument */ 1231 /* need to initialize since --color has _an optional_ argument */
1091 const char *color_opt = color_str; /* "always" */ 1232 const char *color_opt = color_str; /* "always" */
1092#endif 1233#endif
1093#if ENABLE_LONG_OPTS
1094 static const char ls_longopts[] ALIGN1 =
1095 "full-time\0" No_argument "\xff"
1096 "group-directories-first\0" No_argument "\xfe"
1097 IF_FEATURE_LS_COLOR("color\0" Optional_argument "\xfd")
1098 ;
1099#endif
1100 1234
1101 INIT_G(); 1235 INIT_G();
1102 1236
1103 init_unicode(); 1237 init_unicode();
1104 1238
1105#if ENABLE_FEATURE_LS_WIDTH
1106 /* obtain the terminal width */
1107 G_terminal_width = get_terminal_width(STDIN_FILENO);
1108 /* go one less... */
1109 G_terminal_width--;
1110#endif
1111
1112 /* process options */ 1239 /* process options */
1113 opt = getopt32long(argv, "^" 1240 opt = getopt32long(argv, "^"
1114 ls_options 1241 ls_options
@@ -1129,9 +1256,11 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1129 IF_FEATURE_LS_TIMESTAMPS(":c-u:u-c") /* mtime/atime */ 1256 IF_FEATURE_LS_TIMESTAMPS(":c-u:u-c") /* mtime/atime */
1130 /* -w NUM: */ 1257 /* -w NUM: */
1131 IF_FEATURE_LS_WIDTH(":w+") 1258 IF_FEATURE_LS_WIDTH(":w+")
1259 IF_PLATFORM_MINGW32(":aa:AA")
1132 , ls_longopts 1260 , ls_longopts
1133 IF_FEATURE_LS_WIDTH(, /*-T*/ NULL, /*-w*/ &G_terminal_width) 1261 IF_FEATURE_LS_WIDTH(, /*-T*/ NULL, /*-w*/ &G_terminal_width)
1134 IF_FEATURE_LS_COLOR(, &color_opt) 1262 IF_FEATURE_LS_COLOR(, &color_opt)
1263 IF_PLATFORM_MINGW32(, &G.a_count, &G.A_count)
1135 ); 1264 );
1136#if 0 /* option bits debug */ 1265#if 0 /* option bits debug */
1137 bb_error_msg("opt:0x%08x l:%x H:%x color:%x dirs:%x", opt, OPT_l, OPT_H, OPT_color, OPT_dirs_first); 1266 bb_error_msg("opt:0x%08x l:%x H:%x color:%x dirs:%x", opt, OPT_l, OPT_H, OPT_color, OPT_dirs_first);
@@ -1144,6 +1273,29 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1144 exit(0); 1273 exit(0);
1145#endif 1274#endif
1146 1275
1276 /* ftpd secret backdoor? */
1277 if (ENABLE_FTPD && applet_name[0] == 'f') {
1278 /* dirs first are much nicer */
1279 opt = option_mask32 |= OPT_dirs_first;
1280 /* don't show SEcontext */
1281 IF_SELINUX(opt = option_mask32 &= ~OPT_Z;)
1282 /* do not query stdout about size and tty-ness */
1283 IF_FEATURE_LS_WIDTH(G_terminal_width = INT_MAX;)
1284 G.tty_out = 1; /* not a tty */
1285 goto skip_if_ftpd;
1286 }
1287
1288#if ENABLE_FEATURE_LS_WIDTH
1289 if ((int)G_terminal_width < 0) {
1290 /* obtain the terminal width */
1291 G_terminal_width = get_terminal_width(STDIN_FILENO);
1292 /* go one less... */
1293 G_terminal_width--;
1294 }
1295 if (G_terminal_width == 0) /* -w0 */
1296 G_terminal_width = INT_MAX; /* "infinite" */
1297#endif
1298
1147#if ENABLE_SELINUX 1299#if ENABLE_SELINUX
1148 if (opt & OPT_Z) { 1300 if (opt & OPT_Z) {
1149 if (!is_selinux_enabled()) 1301 if (!is_selinux_enabled())
@@ -1155,9 +1307,14 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1155 /* set G_show_color = 1/0 */ 1307 /* set G_show_color = 1/0 */
1156 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && !is_TERM_dumb()) { 1308 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && !is_TERM_dumb()) {
1157 char *p = getenv("LS_COLORS"); 1309 char *p = getenv("LS_COLORS");
1310# if ENABLE_PLATFORM_MINGW32
1311 /* No colour if unset or empty: https://no-color.org */
1312 char *no_c = getenv("NO_COLOR");
1313 if (!no_c || no_c[0] == '\0')
1314# endif
1158 /* LS_COLORS is unset, or (not empty && not "none") ? */ 1315 /* LS_COLORS is unset, or (not empty && not "none") ? */
1159 if (!p || (p[0] && strcmp(p, "none") != 0)) { 1316 if (!p || (p[0] && strcmp(p, "none") != 0)) {
1160 if (isatty(STDOUT_FILENO)) { 1317 if (G_isatty()) {
1161 /* check isatty() last because it's expensive (syscall) */ 1318 /* check isatty() last because it's expensive (syscall) */
1162 G_show_color = 1; 1319 G_show_color = 1;
1163 } 1320 }
@@ -1166,23 +1323,28 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1166 if (opt & OPT_color) { 1323 if (opt & OPT_color) {
1167 if (color_opt[0] == 'n') 1324 if (color_opt[0] == 'n')
1168 G_show_color = 0; 1325 G_show_color = 0;
1169 else switch (index_in_substrings(color_str, color_opt)) { 1326 else if (!G_show_color) {
1170 case 3: 1327 /* if() is not needed, but avoids extra isatty() if G_show_color is already set */
1171 case 4: 1328 /* Check --color=COLOR_OPT and maybe set show_color=1 */
1172 case 5: 1329 switch (index_in_substrings(color_str, color_opt)) {
1173 if (!is_TERM_dumb() && isatty(STDOUT_FILENO)) { 1330 case 3: // auto
1174 case 0: 1331 case 4: // tty
1175 case 1: 1332 case 5: // if-tty
1176 case 2: 1333 if (!is_TERM_dumb() && G_isatty()) {
1177 G_show_color = 1; 1334 case 0: // always
1335 case 1: // yes
1336 case 2: // force
1337 G_show_color = 1;
1338 }
1178 } 1339 }
1179 } 1340 }
1180 } 1341 }
1181#endif 1342#endif
1343 skip_if_ftpd:
1182 1344
1183 /* sort out which command line options take precedence */ 1345 /* sort out which command line options take precedence */
1184 if (ENABLE_FEATURE_LS_RECURSIVE && (opt & OPT_d)) 1346 if (ENABLE_FEATURE_LS_RECURSIVE && (opt & OPT_d))
1185 option_mask32 &= ~OPT_R; /* no recurse if listing only dir */ 1347 opt = option_mask32 &= ~OPT_R; /* no recurse if listing only dir */
1186 if (!(opt & OPT_l)) { /* not -l? */ 1348 if (!(opt & OPT_l)) { /* not -l? */
1187 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) { 1349 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1188 /* when to sort by time? -t[cu] sorts by time even with -l */ 1350 /* when to sort by time? -t[cu] sorts by time even with -l */
@@ -1190,19 +1352,23 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1190 /* without -l, bare -c or -u enable sort too */ 1352 /* without -l, bare -c or -u enable sort too */
1191 /* (with -l, bare -c or -u just select which time to show) */ 1353 /* (with -l, bare -c or -u just select which time to show) */
1192 if (opt & (OPT_c|OPT_u)) { 1354 if (opt & (OPT_c|OPT_u)) {
1193 option_mask32 |= OPT_t; 1355 opt = option_mask32 |= OPT_t;
1194 } 1356 }
1195 } 1357 }
1196 } 1358 }
1197 1359
1198 /* choose a display format if one was not already specified by an option */ 1360 /* choose a display format if one was not already specified by an option */
1199 if (!(option_mask32 & (OPT_l|OPT_1|OPT_x|OPT_C))) 1361 if (!(opt & (OPT_l|OPT_1|OPT_x|OPT_C)))
1200 option_mask32 |= (isatty(STDOUT_FILENO) ? OPT_C : OPT_1); 1362 opt = option_mask32 |= (G_isatty() ? OPT_C : OPT_1);
1201 1363
1202 if (ENABLE_FTPD && applet_name[0] == 'f') { 1364 if (!(opt & OPT_q) && G_isatty())
1203 /* ftpd secret backdoor. dirs first are much nicer */ 1365 opt = option_mask32 |= OPT_q;
1204 option_mask32 |= OPT_dirs_first; 1366
1205 } 1367#if ENABLE_FEATURE_EXTRA_FILE_DATA
1368 /* Enable accurate link counts for directories */
1369 if (opt & OPT_l)
1370 count_subdirs(NULL);
1371#endif
1206 1372
1207 argv += optind; 1373 argv += optind;
1208 if (!argv[0]) 1374 if (!argv[0])
@@ -1215,6 +1381,9 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1215 dn = NULL; 1381 dn = NULL;
1216 nfiles = 0; 1382 nfiles = 0;
1217 do { 1383 do {
1384#if ENABLE_PLATFORM_MINGW32
1385 *argv = fix_backslash(*argv);
1386#endif
1218 cur = my_stat(*argv, *argv, 1387 cur = my_stat(*argv, *argv,
1219 /* follow links on command line unless -l, -i, -s or -F: */ 1388 /* follow links on command line unless -l, -i, -s or -F: */
1220 !(option_mask32 & (OPT_l|OPT_i|OPT_s|OPT_F)) 1389 !(option_mask32 & (OPT_l|OPT_i|OPT_s|OPT_F))