ls: fix -Q to match GNU
No binary (build failed)
—
(log yok)
| @@ -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('"'); | |
| @@ -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); | |
| @@ -0,0 +1,20 @@ | |
| + | /* vi: set sw=4 ts=4: */ |
| + | /* |
| + | * Copyright (C) 2025 by Denys Vlasenko <[email protected]> |
| + | * |
| + | * 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 |
| + | ; |
| @@ -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); | |
| @@ -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\"'" \ | |
| "\ | |
| @@ -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 | |