From d9916c6344c806696554115baa764eea09ca5f08 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Fri, 10 Feb 2023 12:54:03 +0000 Subject: ash: remove CRs from CRLF during field splitting Commit e371e46fa0 (shell: add \r to IFS) added '\r' to the IFS variable so field splitting would remove carriage returns. Rather than change IFS, remove CRs preceding LFs in regions being scanned for field splitting before IFS is applied. This prevents free-standing CRs from being removed. Costs 112-120 bytes. (GitHub issue #285) --- shell/ash.c | 37 ++++++++++++++++++++++++++++++++++--- shell/shell_common.c | 2 +- testsuite/sh.tests | 6 +++++- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index 63eb4947c..87190c453 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -193,12 +193,14 @@ //config: case-insensitive filename globbing. //config: //config:config ASH_IGNORE_CR -//config: bool "Ignore CR in scripts" +//config: bool "Ignore CR in CRLF line endings" //config: default y //config: depends on (ASH || SH_IS_ASH || BASH_IS_ASH) && PLATFORM_MINGW32 //config: help -//config: Allow CRs to be ignored when shell scripts are parsed. This -//config: makes it possible to run scripts with CRLF line endings. +//config: Allow CRs to be ignored when they appear in CRLF line endings +//config: but not in other circumstances. This isn't a general-purpose +//config: option: it only covers certain new cases which are under test. +//config: Enabled by default. May be removed in future. //config: //config:endif # ash options @@ -6806,6 +6808,9 @@ ifsbreakup(char *string, struct arglist *arglist) const char *ifs, *realifs; int ifsspc; int nulonly; +#if ENABLE_ASH_IGNORE_CR + int lshift = 0; +#endif start = string; if (ifslastp != NULL) { @@ -6816,7 +6821,33 @@ ifsbreakup(char *string, struct arglist *arglist) do { int afternul; +#if ENABLE_ASH_IGNORE_CR + /* Adjust region offsets for left-shifted string. */ + ifsp->begoff -= lshift; + ifsp->endoff -= lshift; +#endif p = string + ifsp->begoff; +#if ENABLE_ASH_IGNORE_CR + if (ifsp->endoff > ifsp->begoff + 1) { + /* Transform CRLF to LF. Skip regions having zero or + * one characters: they can't contain CRLF. If the + * region shrinks shift the rest of the string left. */ + int oldlen = ifsp->endoff - ifsp->begoff; + int newlen = remove_cr(p, oldlen); + int delta = oldlen - newlen; + + if (delta > 0) { + char *t = string + ifsp->endoff; + char *s = string + ifsp->endoff - delta; + + while (*t) + *s++ = *t++; + *s = '\0'; + lshift += delta; + ifsp->endoff -= delta; + } + } +#endif afternul = nulonly; nulonly = ifsp->nulonly; ifs = nulonly ? nullstr : realifs; diff --git a/shell/shell_common.c b/shell/shell_common.c index 399d5e684..c0dd32fb4 100644 --- a/shell/shell_common.c +++ b/shell/shell_common.c @@ -19,7 +19,7 @@ #include "libbb.h" #include "shell_common.h" -#if !ENABLE_PLATFORM_MINGW32 +#if !ENABLE_PLATFORM_MINGW32 || ENABLE_ASH_IGNORE_CR const char defifsvar[] ALIGN1 = "IFS= \t\n"; #else const char defifsvar[] ALIGN1 = "IFS= \t\n\r"; diff --git a/testsuite/sh.tests b/testsuite/sh.tests index 872611a1b..6fc3ca104 100755 --- a/testsuite/sh.tests +++ b/testsuite/sh.tests @@ -89,8 +89,12 @@ IyEvYmluL3NoICAtIAplY2hvICJIZWxsbyB3b3JsZCIK " rm -f shebang_leading_argument_trailing_space.sh -testing "remove CRs from string being evaluated" \ +testing "sh remove CRs from string being evaluated" \ "sh -c \"$(printf 'set -e\r\necho Hello world\r\n')\"" \ "Hello world\n" "" "" +testing "sh preserve lone CRs during field splitting" \ + "sh input" \ + "Hello\r world\n" "echo \$(printf \"Hello\\\\r\\\\r\\\\nworld\\\\r\\\\n\")" "" + exit $FAILCOUNT -- cgit v1.2.3-55-g6feb