mirror of
https://git.busybox.net/busybox
synced 2026-01-19 15:29:37 +00:00
ls: implement -q, fix -w0, reduce startup time
function old new delta ls_main 598 660 +62 ls_longopts - 47 +47 G_isatty - 36 +36 print_name 102 134 +32 display_files 358 374 +16 .rodata 105829 105833 +4 vgetopt32 1330 1317 -13 static.ls_longopts 47 - -47 ------------------------------------------------------------------------------ (add/remove: 2/1 grow/shrink: 4/1 up/down: 197/-60) Total: 137 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
a98b95b715
commit
551bfdb97f
2 changed files with 122 additions and 62 deletions
171
coreutils/ls.c
171
coreutils/ls.c
|
|
@ -126,6 +126,8 @@
|
|||
//usage: "\n -F Append indicator (one of */=@|) to names"
|
||||
//usage: )
|
||||
//usage: "\n -l Long format"
|
||||
////usage: "\n -g Long format without group column"
|
||||
////TODO: support -G too ("suppress owner column", GNUism)
|
||||
//usage: "\n -i List inode numbers"
|
||||
//usage: "\n -n List numeric UIDs and GIDs instead of names"
|
||||
//usage: "\n -s List allocated blocks"
|
||||
|
|
@ -159,6 +161,8 @@
|
|||
//usage: IF_FEATURE_LS_WIDTH(
|
||||
//usage: "\n -w N Format N columns wide"
|
||||
//usage: )
|
||||
////usage: "\n -Q Double-quote names"
|
||||
////usage: "\n -q Replace unprintable chars with '?'"
|
||||
//usage: IF_FEATURE_LS_COLOR(
|
||||
//usage: "\n --color[={always,never,auto}]"
|
||||
//usage: )
|
||||
|
|
@ -196,27 +200,47 @@ SPLIT_SUBDIR = 2,
|
|||
|
||||
/* -Cadi1l Std options, busybox always supports */
|
||||
/* -gnsxA Std options, busybox always supports */
|
||||
/* -Q GNU option, busybox always supports */
|
||||
/* -k Std option, busybox always supports (by ignoring) */
|
||||
/* It means "for -s, show sizes in kbytes" */
|
||||
/* Seems to only affect "POSIXLY_CORRECT=1 ls -sk" */
|
||||
/* since otherwise -s shows kbytes anyway */
|
||||
/* -Q GNU option, busybox always supports: */
|
||||
/* -Q, --quote-name */
|
||||
/* enclose entry names in double quotes */
|
||||
/* -LHRctur Std options, busybox optionally supports */
|
||||
/* -Fp Std options, busybox optionally supports */
|
||||
/* -SXvhTw GNU options, busybox optionally supports */
|
||||
/* -T WIDTH Ignored (we don't use tabs on output) */
|
||||
/* -Z SELinux mandated option, busybox optionally supports */
|
||||
/* -q Std option, busybox always supports: */
|
||||
/* https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html: */
|
||||
/* Force each instance of non-printable filename characters and */
|
||||
/* <tab> characters to be written as the <question-mark> ('?') */
|
||||
/* character. Implementations may provide this option by default */
|
||||
/* if the output is to a terminal device. */
|
||||
/* -k Std option, busybox always supports (by ignoring) */
|
||||
/* It means "for -s, show sizes in kbytes" */
|
||||
/* Seems to only affect "POSIXLY_CORRECT=1 ls -sk" */
|
||||
/* since otherwise -s shows kbytes anyway */
|
||||
#define ls_options \
|
||||
"Cadi1lgnsxAk" /* 12 opts, total 12 */ \
|
||||
IF_FEATURE_LS_FILETYPES("Fp") /* 2, 14 */ \
|
||||
IF_FEATURE_LS_RECURSIVE("R") /* 1, 15 */ \
|
||||
IF_SELINUX("Z") /* 1, 16 */ \
|
||||
"Q" /* 1, 17 */ \
|
||||
IF_FEATURE_LS_TIMESTAMPS("ctu") /* 3, 20 */ \
|
||||
IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 24 */ \
|
||||
IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 26 */ \
|
||||
IF_FEATURE_HUMAN_READABLE("h") /* 1, 27 */ \
|
||||
IF_FEATURE_LS_WIDTH("T:w:") /* 2, 29 */
|
||||
"Cadi1lgnsxA" /* 11 opts, total 11 */ \
|
||||
IF_FEATURE_LS_FILETYPES("Fp") /* 2, 13 */ \
|
||||
IF_FEATURE_LS_RECURSIVE("R") /* 1, 14 */ \
|
||||
IF_SELINUX("Z") /* 1, 15 */ \
|
||||
"Q" /* 1, 16 */ \
|
||||
IF_FEATURE_LS_TIMESTAMPS("ctu") /* 3, 19 */ \
|
||||
IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 23 */ \
|
||||
IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 25 */ \
|
||||
IF_FEATURE_HUMAN_READABLE("h") /* 1, 26 */ \
|
||||
IF_FEATURE_LS_WIDTH("T:w:") /* 2, 28 */ \
|
||||
IF_LONG_OPTS("\xff") /* 1, 29 */ \
|
||||
IF_LONG_OPTS("\xfe") /* 1, 30 */ \
|
||||
IF_LONG_OPTS("\xfd") /* 1, 31 */ \
|
||||
"qk" /* 2, 33 */
|
||||
|
||||
#if ENABLE_LONG_OPTS
|
||||
static const char ls_longopts[] ALIGN1 =
|
||||
"full-time\0" No_argument "\xff"
|
||||
"group-directories-first\0" No_argument "\xfe"
|
||||
IF_FEATURE_LS_COLOR("color\0" Optional_argument "\xfd")
|
||||
;
|
||||
#endif
|
||||
|
||||
enum {
|
||||
OPT_C = (1 << 0),
|
||||
|
|
@ -230,29 +254,31 @@ enum {
|
|||
OPT_s = (1 << 8),
|
||||
OPT_x = (1 << 9),
|
||||
OPT_A = (1 << 10),
|
||||
//OPT_k = (1 << 11),
|
||||
|
||||
OPTBIT_F = 12,
|
||||
OPTBIT_p, /* 13 */
|
||||
OPTBIT_F = 11,
|
||||
OPTBIT_p, /* 12 */
|
||||
OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES,
|
||||
OPTBIT_Z = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE,
|
||||
OPTBIT_Q = OPTBIT_Z + 1 * ENABLE_SELINUX,
|
||||
OPTBIT_c, /* 17 */
|
||||
OPTBIT_t, /* 18 */
|
||||
OPTBIT_u, /* 19 */
|
||||
OPTBIT_c, /* 16 */
|
||||
OPTBIT_t, /* 17 */
|
||||
OPTBIT_u, /* 18 */
|
||||
OPTBIT_S = OPTBIT_c + 3 * ENABLE_FEATURE_LS_TIMESTAMPS,
|
||||
OPTBIT_X, /* 21 */
|
||||
OPTBIT_r, /* 22 */
|
||||
OPTBIT_v, /* 23 */
|
||||
OPTBIT_X, /* 20 */
|
||||
OPTBIT_r, /* 21 */
|
||||
OPTBIT_v, /* 22 */
|
||||
OPTBIT_L = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES,
|
||||
OPTBIT_H, /* 25 */
|
||||
OPTBIT_H, /* 24 */
|
||||
OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS,
|
||||
OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE,
|
||||
OPTBIT_w, /* 28 */
|
||||
OPTBIT_w, /* 27 */
|
||||
OPTBIT_full_time = OPTBIT_T + 2 * ENABLE_FEATURE_LS_WIDTH,
|
||||
OPTBIT_dirs_first,
|
||||
OPTBIT_color, /* 31 */
|
||||
/* with long opts, we use all 32 bits */
|
||||
OPTBIT_color, /* 30 */
|
||||
OPTBIT_q = OPTBIT_color + 1, /* 31 */
|
||||
OPTBIT_k = OPTBIT_q + 1, /* 32 */
|
||||
/* with all options enabled, we use all 32 bits and even one extra bit! */
|
||||
/* this works because -k is ignored, and getopt32 allows such "ignore" options past 31th bit */
|
||||
|
||||
OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES,
|
||||
OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES,
|
||||
|
|
@ -274,6 +300,8 @@ enum {
|
|||
OPT_full_time = (1 << OPTBIT_full_time ) * ENABLE_LONG_OPTS,
|
||||
OPT_dirs_first = (1 << OPTBIT_dirs_first) * ENABLE_LONG_OPTS,
|
||||
OPT_color = (1 << OPTBIT_color ) * ENABLE_FEATURE_LS_COLOR,
|
||||
OPT_q = (1 << OPTBIT_q),
|
||||
//-k is ignored: OPT_k = (1 << OPTBIT_k),
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -327,6 +355,7 @@ struct globals {
|
|||
#endif
|
||||
smallint exit_code;
|
||||
smallint show_dirname;
|
||||
smallint tty_out;
|
||||
#if ENABLE_FEATURE_LS_WIDTH
|
||||
unsigned terminal_width;
|
||||
# define G_terminal_width (G.terminal_width)
|
||||
|
|
@ -343,16 +372,21 @@ struct globals {
|
|||
setup_common_bufsiz(); \
|
||||
/* we have to zero it out because of NOEXEC */ \
|
||||
memset(&G, 0, sizeof(G)); \
|
||||
IF_FEATURE_LS_WIDTH(G_terminal_width = TERMINAL_WIDTH;) \
|
||||
IF_FEATURE_LS_WIDTH(G_terminal_width = ~0U;) \
|
||||
IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \
|
||||
} while (0)
|
||||
|
||||
#define ESC "\033"
|
||||
|
||||
static int G_isatty(void)
|
||||
{
|
||||
if (!G.tty_out) /* not known yet? */
|
||||
G.tty_out = isatty(STDOUT_FILENO) + 1;
|
||||
return (G.tty_out == 2);
|
||||
}
|
||||
|
||||
/*** Output code ***/
|
||||
|
||||
|
||||
/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
|
||||
* (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
|
||||
* 3/7:multiplexed char/block device)
|
||||
|
|
@ -420,6 +454,9 @@ static unsigned calc_name_len(const char *name)
|
|||
unsigned len;
|
||||
uni_stat_t uni_stat;
|
||||
|
||||
if (!(option_mask32 & OPT_q))
|
||||
return strlen(name);
|
||||
|
||||
// TODO: quote tab as \t, etc, if -Q
|
||||
name = printable_string2(&uni_stat, name);
|
||||
|
||||
|
|
@ -449,6 +486,11 @@ static unsigned print_name(const char *name)
|
|||
unsigned len;
|
||||
uni_stat_t uni_stat;
|
||||
|
||||
if (!(option_mask32 & OPT_q)) {
|
||||
fputs_stdout(name);
|
||||
return strlen(name);
|
||||
}
|
||||
|
||||
// TODO: quote tab as \t, etc, if -Q
|
||||
name = printable_string2(&uni_stat, name);
|
||||
|
||||
|
|
@ -646,7 +688,7 @@ static void display_files(struct dnode **dn, unsigned nfiles)
|
|||
unsigned i, ncols, nrows, row, nc;
|
||||
unsigned column;
|
||||
unsigned nexttab;
|
||||
unsigned column_width = 0; /* used only by coulmnal output */
|
||||
unsigned column_width = 0; /* used only by columnar output */
|
||||
|
||||
if (option_mask32 & (OPT_l|OPT_1)) {
|
||||
ncols = 1;
|
||||
|
|
@ -691,6 +733,11 @@ static void display_files(struct dnode **dn, unsigned nfiles)
|
|||
}
|
||||
nexttab = column + column_width;
|
||||
column += display_single(dn[i]);
|
||||
} else {
|
||||
/* if -w999999999, ncols can be very large */
|
||||
//bb_error_msg(" col:%u ncol:%u i:%i", nc, ncols, i); sleep1();
|
||||
/* without "break", we loop millions of times here */
|
||||
break;
|
||||
}
|
||||
}
|
||||
putchar('\n');
|
||||
|
|
@ -1090,25 +1137,11 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
|
|||
/* need to initialize since --color has _an optional_ argument */
|
||||
const char *color_opt = color_str; /* "always" */
|
||||
#endif
|
||||
#if ENABLE_LONG_OPTS
|
||||
static const char ls_longopts[] ALIGN1 =
|
||||
"full-time\0" No_argument "\xff"
|
||||
"group-directories-first\0" No_argument "\xfe"
|
||||
IF_FEATURE_LS_COLOR("color\0" Optional_argument "\xfd")
|
||||
;
|
||||
#endif
|
||||
|
||||
INIT_G();
|
||||
|
||||
init_unicode();
|
||||
|
||||
#if ENABLE_FEATURE_LS_WIDTH
|
||||
/* obtain the terminal width */
|
||||
G_terminal_width = get_terminal_width(STDIN_FILENO);
|
||||
/* go one less... */
|
||||
G_terminal_width--;
|
||||
#endif
|
||||
|
||||
/* process options */
|
||||
opt = getopt32long(argv, "^"
|
||||
ls_options
|
||||
|
|
@ -1144,6 +1177,17 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
|
|||
exit(0);
|
||||
#endif
|
||||
|
||||
#if ENABLE_FEATURE_LS_WIDTH
|
||||
if ((int)G_terminal_width < 0) {
|
||||
/* obtain the terminal width */
|
||||
G_terminal_width = get_terminal_width(STDIN_FILENO);
|
||||
/* go one less... */
|
||||
G_terminal_width--;
|
||||
}
|
||||
if (G_terminal_width == 0) /* -w0 */
|
||||
G_terminal_width = INT_MAX; /* "infinite" */
|
||||
#endif
|
||||
|
||||
#if ENABLE_SELINUX
|
||||
if (opt & OPT_Z) {
|
||||
if (!is_selinux_enabled())
|
||||
|
|
@ -1157,7 +1201,7 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
|
|||
char *p = getenv("LS_COLORS");
|
||||
/* LS_COLORS is unset, or (not empty && not "none") ? */
|
||||
if (!p || (p[0] && strcmp(p, "none") != 0)) {
|
||||
if (isatty(STDOUT_FILENO)) {
|
||||
if (G_isatty()) {
|
||||
/* check isatty() last because it's expensive (syscall) */
|
||||
G_show_color = 1;
|
||||
}
|
||||
|
|
@ -1166,15 +1210,19 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
|
|||
if (opt & OPT_color) {
|
||||
if (color_opt[0] == 'n')
|
||||
G_show_color = 0;
|
||||
else switch (index_in_substrings(color_str, color_opt)) {
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
if (!is_TERM_dumb() && isatty(STDOUT_FILENO)) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
G_show_color = 1;
|
||||
else if (!G_show_color) {
|
||||
/* if() is not needed, but avoids extra isatty() if G_show_color is already set */
|
||||
/* Check --color=COLOR_OPT and maybe set show_color=1 */
|
||||
switch (index_in_substrings(color_str, color_opt)) {
|
||||
case 3: // auto
|
||||
case 4: // tty
|
||||
case 5: // if-tty
|
||||
if (!is_TERM_dumb() && G_isatty()) {
|
||||
case 0: // always
|
||||
case 1: // yes
|
||||
case 2: // force
|
||||
G_show_color = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1182,7 +1230,7 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
|
|||
|
||||
/* sort out which command line options take precedence */
|
||||
if (ENABLE_FEATURE_LS_RECURSIVE && (opt & OPT_d))
|
||||
option_mask32 &= ~OPT_R; /* no recurse if listing only dir */
|
||||
opt = option_mask32 &= ~OPT_R; /* no recurse if listing only dir */
|
||||
if (!(opt & OPT_l)) { /* not -l? */
|
||||
if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
|
||||
/* when to sort by time? -t[cu] sorts by time even with -l */
|
||||
|
|
@ -1190,18 +1238,21 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
|
|||
/* without -l, bare -c or -u enable sort too */
|
||||
/* (with -l, bare -c or -u just select which time to show) */
|
||||
if (opt & (OPT_c|OPT_u)) {
|
||||
option_mask32 |= OPT_t;
|
||||
opt = option_mask32 |= OPT_t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* choose a display format if one was not already specified by an option */
|
||||
if (!(option_mask32 & (OPT_l|OPT_1|OPT_x|OPT_C)))
|
||||
option_mask32 |= (isatty(STDOUT_FILENO) ? OPT_C : OPT_1);
|
||||
if (!(opt & (OPT_l|OPT_1|OPT_x|OPT_C)))
|
||||
opt = option_mask32 |= (G_isatty() ? OPT_C : OPT_1);
|
||||
|
||||
if (!(opt & OPT_q) && G_isatty())
|
||||
opt = option_mask32 |= OPT_q;
|
||||
|
||||
if (ENABLE_FTPD && applet_name[0] == 'f') {
|
||||
/* ftpd secret backdoor. dirs first are much nicer */
|
||||
option_mask32 |= OPT_dirs_first;
|
||||
opt = option_mask32 |= OPT_dirs_first;
|
||||
}
|
||||
|
||||
argv += optind;
|
||||
|
|
|
|||
|
|
@ -530,6 +530,7 @@ vgetopt32(char **argv, const char *applet_opts, const char *applet_long_options,
|
|||
* "fake" short options, like this one:
|
||||
* wget $'-\203' "Test: test" http://kernel.org/
|
||||
* (supposed to act as --header, but doesn't) */
|
||||
next_opt:
|
||||
#if ENABLE_LONG_OPTS
|
||||
while ((c = getopt_long(argc, argv, applet_opts,
|
||||
long_options, NULL)) != -1) {
|
||||
|
|
@ -544,8 +545,16 @@ vgetopt32(char **argv, const char *applet_opts, const char *applet_long_options,
|
|||
* but we construct long opts so that flag
|
||||
* is always NULL (see above) */
|
||||
if (on_off->opt_char == '\0' /* && c != '\0' */) {
|
||||
/* c is probably '?' - "bad option" */
|
||||
goto error;
|
||||
/* We reached the end of complementary[] and did not find -c */
|
||||
if (c == '?') /* getopt says: "bad option, or option has no required argument" */
|
||||
goto error;
|
||||
/* if there were options beyond 32 bits (example: ls),
|
||||
* they got no complementary[] slot, and no result bit.
|
||||
* IOW: they must be "accept but ignore" options.
|
||||
* For them, we end up here.
|
||||
*/
|
||||
//bb_error_msg("ignored option '%c', skipping", c);
|
||||
goto next_opt;
|
||||
}
|
||||
}
|
||||
if (flags & on_off->incongruously)
|
||||
|
|
|
|||
Loading…
Reference in a new issue