diff options
Diffstat (limited to '')
-rw-r--r-- | coreutils/ls.c | 331 |
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 | ||
241 | static 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 | ||
221 | enum { | 248 | enum { |
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 | ||
321 | struct globals { | 355 | struct 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 | ||
391 | static 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 | */ | ||
418 | static unsigned calc_name_len(const char *name) | 469 | static 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 | */ | ||
447 | static unsigned print_name(const char *name) | 505 | static 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 | ||
1021 | static 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) */ |
924 | static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p) | 1030 | static 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 | ||
1193 | static 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 | ||
1064 | int ls_main(int argc UNUSED_PARAM, char **argv) | 1205 | int 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)) |