Skip to content

Commit

Permalink
[builtins/printf] Implement printf %b for 'echo -e' escaping
Browse files Browse the repository at this point in the history
With special \c behavior

- Also refactor 'echo -e' a bit.

Addresses issue #357.
  • Loading branch information
Andy Chu committed Mar 19, 2020
1 parent c4fb75b commit 7132537
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 18 deletions.
28 changes: 26 additions & 2 deletions osh/builtin_printf.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,6 @@ def _ParseFormatStr(self):
if part.type.val in 'eEfFgG':
p_die("osh printf doesn't support floating point", token=part.type)
# These two could be implemented. %c needs utf-8 decoding.
if part.type.val == 'b':
p_die("osh printf doesn't support backslash escaping (try $'\\n')", token=part.type)
if part.type.val == 'c':
p_die("osh printf doesn't support single characters (bytes)", token=part.type)

Expand Down Expand Up @@ -201,6 +199,7 @@ def Run(self, cmd_val):
out = []
arg_index = 0
num_args = len(varargs)
backslash_c = False

while True:
for part in parts:
Expand Down Expand Up @@ -284,8 +283,30 @@ def Run(self, cmd_val):
if typ == 's':
if precision is not None:
s = s[:precision] # truncate

elif typ == 'q':
s = string_ops.ShellQuoteOneLine(s)

elif typ == 'b':
# Process just like echo -e, except \c handling is simpler.

parts = [] # type: List[str]
lex = match.EchoLexer(s)
while True:
id_, value = lex.Next()
if id_ == Id.Eol_Tok: # Note: This is really a NUL terminator
break

p = word_compile.EvalCStringToken(id_, value)

# Unusual behavior: '\c' aborts processing!
if p is None:
backslash_c = True
break

parts.append(p)
s = ''.join(parts)

elif typ in 'diouxX' or part.type.id == Id.Format_Time:
try:
d = int(s)
Expand Down Expand Up @@ -378,6 +399,9 @@ def Run(self, cmd_val):
else:
raise AssertionError()

if backslash_c: # 'printf %b a\cb xx' - \c terminates processing!
break

if arg_index >= num_args:
break
# Otherwise there are more args. So cycle through the loop once more to
Expand Down
20 changes: 11 additions & 9 deletions osh/builtin_pure.py
Original file line number Diff line number Diff line change
Expand Up @@ -576,12 +576,15 @@ def Run(self, cmd_val):
argv = cmd_val.argv[1:]
arg, arg_index = ECHO_SPEC.ParseLikeEcho(argv)
argv = argv[arg_index:]

backslash_c = False # \c terminates input

if arg.e:
new_argv = []
for a in argv:
parts = []
parts = [] # type: List[str]
lex = match.EchoLexer(a)
while True:
while not backslash_c:
id_, value = lex.Next()
if id_ == Id.Eol_Tok: # Note: This is really a NUL terminator
break
Expand All @@ -590,15 +593,14 @@ def Run(self, cmd_val):

# Unusual behavior: '\c' prints what is there and aborts processing!
if p is None:
new_argv.append(''.join(parts))
for i, a in enumerate(new_argv):
if i != 0:
sys.stdout.write(' ') # arg separator
sys.stdout.write(a)
return 0 # EARLY RETURN
backslash_c = True
break

parts.append(p)

new_argv.append(''.join(parts))
if backslash_c: # no more args either
break

# Replace it
argv = new_argv
Expand All @@ -620,7 +622,7 @@ def Run(self, cmd_val):
sys.stdout.write(' ') # arg separator
sys.stdout.write(a)

if not arg.n:
if not arg.n and not backslash_c:
sys.stdout.write('\n')

return 0
4 changes: 2 additions & 2 deletions spec/builtin-io.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,9 @@ echo -e 'ab\0cd'
flags='-e'
case $SH in dash) flags='' ;; esac

echo $flags xy 'ab\cde' 'ab\cde'
echo $flags xy 'ab\cde' 'zzz'
## stdout-json: "xy ab"
## N-I mksh stdout-json: "xy abde abde"
## N-I mksh stdout-json: "xy abde zzz"

#### echo -e with hex escape
echo -e 'abcd\x65f'
Expand Down
12 changes: 7 additions & 5 deletions spec/builtin-printf.test.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#!/usr/bin/env bash
#
# printf
# bash-completion uses this odd printf -v construction. It seems to mostly use
# %s and %q though.
Expand Down Expand Up @@ -436,9 +434,13 @@ echo status=$?
[$]
status=0
## END
## N-I osh STDOUT:
[\044]
status=2

#### printf %b with \c early return
printf '[%b]\n' 'ab\ncd\cxy'
echo $?
## STDOUT:
[ab
cd0
## END

#### printf %c -- doesn't respect UTF-8! Bad.
Expand Down

0 comments on commit 7132537

Please sign in to comment.