diff options
| author | Denys Vlasenko <vda.linux@googlemail.com> | 2025-08-01 01:02:43 +0200 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2025-08-01 01:02:43 +0200 |
| commit | 4f3a56dc12a2126bdacff73f1cfe586d06e800c0 (patch) | |
| tree | 863d23f2fc3d2bf6b77213ef729a6ebda7101244 | |
| parent | bb18473216253b8602ce081dd944f854aad9e572 (diff) | |
ls: fix -Q to match GNU
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@googlemail.com>
| -rw-r--r-- | coreutils/ls.c | 50 | ||||
| -rw-r--r-- | include/libbb.h | 3 | ||||
| -rw-r--r-- | libbb/c_escape.c | 20 | ||||
| -rw-r--r-- | libbb/dump.c | 39 | ||||
| -rwxr-xr-x | testsuite/hexdump.tests | 10 | ||||
| -rwxr-xr-x | 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@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 |
