aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Landley <rob@landley.net>2016-03-24 16:20:36 -0500
committerRob Landley <rob@landley.net>2016-03-24 16:20:36 -0500
commitaf51034338e107f1f45a66547e785b7730d20459 (patch)
tree4bd0f5294710bd90562e3ea79f9f7c32309383d8
parent1ffa7f45eb4fe3b73f94e69c929ad311d9c44b81 (diff)
downloadtoybox-af51034338e107f1f45a66547e785b7730d20459.tar.gz
Debian bug 635570 did something horribly nonstandard (depending on a side
effect of "sed -e 'a\'" with no trailing line). But there's an actual user, and it's not hard to implement, and it's not hard to implement, and there isn't an obvious _other_ way to do it, so add the behavior and a test for it. Fix some bad/missing comments while I was there, and add a couple TODOs.
-rwxr-xr-xtests/sed.test10
-rw-r--r--toys/posix/sed.c21
2 files changed, 22 insertions, 9 deletions
diff --git a/tests/sed.test b/tests/sed.test
index b308b699..7a4a8cda 100755
--- a/tests/sed.test
+++ b/tests/sed.test
@@ -77,9 +77,7 @@ testing "c {range}" "sed -e '2,4{c blah' -e '}'" \
"" "one\ntwo\nthree\nfour\nfive\nsix"
testing "c multiple continuation" \
"sed -e 'c\\' -e 'two\\' -e ''" "two\n\n" "" "hello"
-# NOTE: will print 'unfinished c' to stderr and exit 1
-testing "c empty continuation" \
- "sed -e 'c\\'" "" "" "hello"
+SKIP_HOST=1 testing "c empty continuation" "sed -e 'c\\'" "\n" "" "hello"
testing "D further processing depends on whether line is blank" \
"sed -e '/one/,/three/{' -e 'i meep' -e'N;2D;}'" \
"meep\nmeep\ntwo\nthree\n" "" "one\ntwo\nthree\n"
@@ -138,6 +136,12 @@ testing "" "sed 'y/a\\bc/de\f/'" "db\f" "" "abc"
testing "[a-a] (for perl)" "sed '"'s/\([^a-zA-Z0-9.:_\-\/]\)/\\\1/g'"'" \
'he\ llo' "" "he llo"
+# Debian bug https://bugs.debian.org/635570 added code to ensure a file
+# ends with a newline via "sed -e '$a\'". Apparently all a\ with no additional
+# pattern lines after it does (other than making posix throw up) is
+# flush the pending newline as _if_ it had added another line. *shrug* Ok?
+testing "trailing a\ (for debian)" "sed 'a\\'" "hello\n" "" "hello"
+
# You have to match the first line of a range in order to activate
# the range, numeric and ascii work the same way
testing "skip start of range" "sed -e n -e '1,2s/b/c/'" "a\nb\n" "" "a\nb\n"
diff --git a/toys/posix/sed.c b/toys/posix/sed.c
index 3def9d40..e1c00bab 100644
--- a/toys/posix/sed.c
+++ b/toys/posix/sed.c
@@ -6,6 +6,9 @@
*
* TODO: lines > 2G could wrap signed int length counters. Not just getline()
* but N and s///
+ * TODO: make y// handle unicode
+ * TODO: handle error return from emit(), error_msg/exit consistently
+ * What's the right thing to do for -i when write fails? Skip to next?
USE_SED(NEWTOY(sed, "(version)e*f*inEr[+Er]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
@@ -195,9 +198,9 @@ static int emit(char *line, long len, int eol)
int l, old = line[len];
if (TT.noeol && !writeall(TT.fdout, "\n", 1)) return 1;
+ TT.noeol = !eol;
if (eol) line[len++] = '\n';
if (!len) return 0;
- TT.noeol = len && !eol;
l = writeall(TT.fdout, line, len);
if (eol) line[len-1] = old;
if (l != len) {
@@ -366,7 +369,7 @@ static void walk_pattern(char **pline, long plen)
if (c=='a' || c=='r') {
struct append *a = xzalloc(sizeof(struct append));
- a->str = logrus->arg1+(char *)logrus;
+ if (logrus->arg1) a->str = logrus->arg1+(char *)logrus;
a->file = c=='r';
dlist_add_nomalloc((void *)&append, (void *)a);
} else if (c=='b' || c=='t' || c=='T') {
@@ -639,7 +642,8 @@ done:
xsendfile(fd, TT.fdout);
close(fd);
}
- } else emit(append->str, strlen(append->str), 1);
+ } else if (append->str) emit(append->str, strlen(append->str), 1);
+ else emit(line, 0, 0);
free(append);
append = a;
}
@@ -764,7 +768,6 @@ static void jewel_of_judgement(char **pline, long len)
// Append additional line to pattern argument string?
// We temporarily repurpose "hit" to indicate line continuations
if (corwin && corwin->prev->hit) {
- if (!pline || !*pline) error_exit("unfinished %c", corwin->prev->c);;
// Remove half-finished entry from list so remalloc() doesn't confuse it
TT.pattern = TT.pattern->prev;
corwin = dlist_pop(&TT.pattern);
@@ -958,6 +961,7 @@ writenow:
} else if (strchr("abcirtTw:", c)) {
int end;
+ // trim leading spaces
while (isspace(*line) && *line != '\n') line++;
// Resume logic differs from 's' case because we don't add a newline
@@ -965,8 +969,9 @@ writenow:
resume_a:
corwin->hit = 0;
- // Trim whitespace from "b ;" and ": blah " but only first space in "w x "
- if (!(end = strcspn(line, strchr("btT:", c) ? "; \t\r\n\v\f" : "\n"))) {
+ // btT: end with space or semicolon, aicrw continue to newline.
+ if (!(end = strcspn(line, strchr(":btT", c) ? "; \t\r\n\v\f" : "\n"))) {
+ // Argument's optional for btT
if (strchr("btT", c)) continue;
else if (!corwin->arg1) break;
}
@@ -976,6 +981,10 @@ resume_a:
// \n over NUL terminator because call to extend_string() adds it back.
if (!corwin->arg1) corwin->arg1 = reg - (char*)corwin;
else if (*(corwin->arg1+(char *)corwin)) *(reg++) = '\n';
+ else if (!pline) {
+ corwin->arg1 = 0;
+ continue;
+ }
reg = extend_string((void *)&corwin, line, reg - (char *)corwin, end);
// Recopy data to remove escape sequences and handle line continuation.