[git commit] ls: fix -Q to match GNU
Denys Vlasenko
vda.linux at googlemail.com
Thu Jul 31 23:02:43 UTC 2025
commit: https://git.busybox.net/busybox/commit/?id=4f3a56dc12a2126bdacff73f1cfe586d06e800c0
branch: https://git.busybox.net/busybox/commit/?id=refs/heads/master
function old new delta
print_name 137 229 +92
display_files 375 402 +27
c_escape_conv_str00 - 24 +24
display 1476 1485 +9
conv_str 33 - -33
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 3/0 up/down: 152/-33) Total: 119 bytes
Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
coreutils/ls.c | 50 +++++++++++++++++++++++++++++++++++++------------
include/libbb.h | 3 +++
libbb/c_escape.c | 20 ++++++++++++++++++++
libbb/dump.c | 39 +++++++++++++++++++-------------------
testsuite/hexdump.tests | 10 ++++++++++
testsuite/ls.tests | 26 +++++++++++++++++++++++--
6 files changed, 114 insertions(+), 34 deletions(-)
diff --git a/coreutils/ls.c b/coreutils/ls.c
index c725be92d..9e4b83032 100644
--- a/coreutils/ls.c
+++ b/coreutils/ls.c
@@ -457,19 +457,29 @@ static unsigned calc_name_len(const char *name)
if (!(option_mask32 & (OPT_q|OPT_Q)))
return strlen(name);
- name = printable_string2(&uni_stat, name);
-
if (!(option_mask32 & OPT_Q)) {
+ printable_string2(&uni_stat, name);
return uni_stat.unicode_width;
}
- // TODO: quote chars 7..13 as \a,b,t,n,v,f,r
- // other chars <32 or >127 as \ooo octal
- len = 2 + uni_stat.unicode_width;
+ len = 2 + strlen(name);
while (*name) {
+ unsigned char ch = (unsigned char)*name;
+ if (ch < ' ' || ch > 0x7e) {
+ ch -= 7;
+ if ((signed char)ch >= 0 && ch <= 6) {
+ // quote chars 7..13 as \a,b,t,n,v,f,r
+ len++;
+ goto next;
+ }
+ // other chars <32 or >126 as \ooo octal
+ len += 3;
+ goto next;
+ }
if (*name == '"' || *name == '\\') {
len++;
}
+ next:
name++;
}
return len;
@@ -492,23 +502,39 @@ static unsigned print_name(const char *name)
return strlen(name);
}
- name = printable_string2(&uni_stat, name);
-
if (!(option_mask32 & OPT_Q)) {
+ name = printable_string2(&uni_stat, name);
fputs_stdout(name);
return uni_stat.unicode_width;
}
- // TODO: quote chars 7..13 as \a,b,t,n,v,f,r
- // other chars <32 or >127 as \ooo octal
- len = 2 + uni_stat.unicode_width;
+ len = 2 + strlen(name);
putchar('"');
while (*name) {
- if (*name == '"' || *name == '\\') {
+ unsigned char ch = (unsigned char)*name;
+ if (ch < ' ' || ch > 0x7e) {
+ putchar('\\');
+ ch -= 7;
+ if ((signed char)ch >= 0 && ch <= 6) {
+ // quote chars 7..13 as \a,b,t,n,v,f,r
+ ch = c_escape_conv_str07[1 + 3 * ch];
+ len++;
+ goto put_ch;
+ }
+ // other chars <32 or >126 as \ooo octal
+ ch = (unsigned char)*name;
+ putchar('0' + ((ch>>6) & 7));
+ putchar('0' + ((ch>>3) & 7));
+ ch = '0' + (ch & 7);
+ len += 3;
+ goto put_ch;
+ }
+ if (ch == '"' || ch == '\\') {
putchar('\\');
len++;
}
- putchar(*name);
+ put_ch:
+ putchar(ch);
name++;
}
putchar('"');
diff --git a/include/libbb.h b/include/libbb.h
index 7105bd479..cdc05049c 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -1113,6 +1113,9 @@ char *bin2hex(char *dst, const char *src, int count) FAST_FUNC;
/* Reverse */
char* hex2bin(char *dst, const char *src, int count) FAST_FUNC;
+extern const char c_escape_conv_str00[];
+#define c_escape_conv_str07 (c_escape_conv_str00+3)
+
void FAST_FUNC xorbuf_3(void *dst, const void *src1, const void *src2, unsigned count);
void FAST_FUNC xorbuf(void* buf, const void* mask, unsigned count);
void FAST_FUNC xorbuf16_aligned_long(void* buf, const void* mask);
diff --git a/libbb/c_escape.c b/libbb/c_escape.c
new file mode 100644
index 000000000..6c109f2e0
--- /dev/null
+++ b/libbb/c_escape.c
@@ -0,0 +1,20 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2025 by Denys Vlasenko <vda.linux at googlemail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+//kbuild:lib-y += c_escape.o
+
+#include "libbb.h"
+
+const char c_escape_conv_str00[] ALIGN1 =
+ "\\""0""\0" // [0]:00
+ "\\""a""\0" // [1]:07
+ "\\""b""\0" // [2]:08
+ "\\""t""\0" // [3]:09
+ "\\""n""\0" // [4]:0a
+ "\\""v""\0" // [5]:0b
+ "\\""f""\0" // [6]:0c
+ "\\""r" // [7]:0d
+ ;
diff --git a/libbb/dump.c b/libbb/dump.c
index ac5d47d9e..0cc7775d6 100644
--- a/libbb/dump.c
+++ b/libbb/dump.c
@@ -478,37 +478,36 @@ static void bpad(PR *pr)
continue;
}
-static const char conv_str[] ALIGN1 =
- "\0" "\\""0""\0"
- "\007""\\""a""\0"
- "\b" "\\""b""\0"
- "\f" "\\""f""\0"
- "\n" "\\""n""\0"
- "\r" "\\""r""\0"
- "\t" "\\""t""\0"
- "\v" "\\""v""\0"
- ;
-
static void conv_c(PR *pr, unsigned char *p)
{
- const char *str = conv_str;
-
- do {
- if (*p == *str) {
- ++str;
- goto strpr; /* map e.g. '\n' to "\\n" */
- }
- str += 4;
- } while (*str);
+ const char *str;
+ unsigned char ch;
+
+ ch = *p;
+ if (ch == 0 || (ch -= 6, (signed char)ch > 0 && ch <= 7)) {
+ /* map chars 0,7..13 to "\0","\{a,b,t,n,v,f,r}" */
+ str = c_escape_conv_str00 + 3 * ch;
+ goto strpr;
+ }
if (isprint_asciionly(*p)) {
*pr->cchar = 'c';
printf(pr->fmt, *p);
} else {
+#if 1
char buf[4];
/* gcc-8.0.1 needs lots of casts to shut up */
sprintf(buf, "%03o", (unsigned)(uint8_t)*p);
str = buf;
+#else // use faster version? +20 bytes of code
+ char buf[4];
+ buf[3] = '\0';
+ ch = *p;
+ buf[2] = '0' + (ch & 7); ch >>= 3;
+ buf[1] = '0' + (ch & 7); ch >>= 3;
+ buf[0] = '0' + ch;
+ str = buf;
+#endif
strpr:
*pr->cchar = 's';
printf(pr->fmt, str);
diff --git a/testsuite/hexdump.tests b/testsuite/hexdump.tests
index b2f6a2201..d2c0a5dc8 100755
--- a/testsuite/hexdump.tests
+++ b/testsuite/hexdump.tests
@@ -56,6 +56,16 @@ testing "hexdump -e %3_u" \
" \
"" "$input"
+testing "hexdump -e %3_c" \
+ "hexdump -e '16/1 \" %3_c\" \"\n\"'" \
+' \\0 001 002 003 004 005 006 \\a \\b \\t \\n \\v \\f \\r 016 017
+ 020 021 022 023 024 025 026 027 030 031 032 033 034 035 036 037
+ p q r s t u v w x y z { | } ~ 177
+ 200 201 202 203 204 205 206 207 210 211 212 213 214 215 216 217
+ 360 361 362 363 364 365 366 367 370 371 372 373 374 375 376 377
+' \
+ "" "$input"
+
testing "hexdump -e /1 %d" \
"hexdump -e '16/1 \" %4d\" \"\n\"'" \
"\
diff --git a/testsuite/ls.tests b/testsuite/ls.tests
index 9309d366b..a95911034 100755
--- a/testsuite/ls.tests
+++ b/testsuite/ls.tests
@@ -19,7 +19,7 @@ test x"$CONFIG_UNICODE_SUPPORT" = x"y" \
&& test x"$CONFIG_LAST_SUPPORTED_WCHAR" = x"767" \
&& test x"$CONFIG_FEATURE_LS_SORTFILES" = x"y" \
&& testing "ls unicode test with codepoints limited to 767" \
-"(cd ls.testdir && sh ../ls.mk_uni_tests) && ls -1 ls.testdir" \
+"(cd ls.testdir && sh ../ls.mk_uni_tests) && ls -1q ls.testdir" \
'0001_1__Some_correct_UTF-8_text___________________________________________|
0002_2__Boundary_condition_test_cases_____________________________________|
0003_2.1__First_possible_sequence_of_a_certain_length_____________________|
@@ -138,7 +138,7 @@ test x"$CONFIG_UNICODE_SUPPORT" = x"y" \
&& test x"$CONFIG_SUBST_WCHAR" = x"63" \
&& test x"$CONFIG_LAST_SUPPORTED_WCHAR" = x"0" \
&& testing "ls unicode test with unlimited codepoints" \
-"(cd ls.testdir && sh ../ls.mk_uni_tests) && ls -1 ls.testdir" \
+"(cd ls.testdir && sh ../ls.mk_uni_tests) && ls -1q ls.testdir" \
'0001_1__Some_correct_UTF-8_text___________________________________________|
0002_2__Boundary_condition_test_cases_____________________________________|
0003_2.1__First_possible_sequence_of_a_certain_length_____________________|
@@ -262,6 +262,28 @@ test x"$CONFIG_FEATURE_LS_SORTFILES" = x"y" \
"A\nB\nA\nB\nA\nB\n" \
"" ""
+rm -rf ls.testdir 2>/dev/null
+mkdir ls.testdir || exit 1
+touch "`printf "ls.testdir/\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f_\x7f\x80\xfe\xff_\x22_\x27_\x5c"`"
+
+sq="'"
+
+# testing "test name" "command" "expected result" "file input" "stdin"
+testing "ls -q" \
+'ls -q ls.testdir' \
+'???????????????????????????????_????_"_'$sq'_\\''\n' \
+"" ""
+
+testing "ls -Q" \
+'ls -Q ls.testdir' \
+'"\\001\\002\\003\\004\\005\\006\\a\\b\\t\\n\\v\\f\\r\\016\\017\\020\\021\\022\\023\\024\\025\\026\\027\\030\\031\\032\\033\\034\\035\\036\\037_\\177\\200\\376\\377_\\"_'$sq'_\\\\"\n' \
+"" ""
+
+testing "ls -qQ" \
+'ls -qQ ls.testdir' \
+'"\\001\\002\\003\\004\\005\\006\\a\\b\\t\\n\\v\\f\\r\\016\\017\\020\\021\\022\\023\\024\\025\\026\\027\\030\\031\\032\\033\\034\\035\\036\\037_\\177\\200\\376\\377_\\"_'$sq'_\\\\"\n' \
+"" ""
+
# Clean up
rm -rf ls.testdir 2>/dev/null
More information about the busybox-cvs
mailing list