OSDN Git Service

Merge changes I698a1608,I9d66c41d,I3a4917a5,Id3d633ff,Id0dead30, ...
authorElliott Hughes <enh@google.com>
Thu, 15 Jan 2015 00:52:07 +0000 (00:52 +0000)
committerGerrit Code Review <noreply-gerritcodereview@google.com>
Thu, 15 Jan 2015 00:52:08 +0000 (00:52 +0000)
* changes:
  Let chown build standalone.
  sed bugfix: N or n at end of script would save the terminating NULL as the resume position, so the script would restart from beginning.
  sed s/// can have line continuations in the replacement part, with or without a \ escaping the newline.
  Promote printf.
  One more bugfix for printf.c, with test suite entry. (Make %-3d etc work.)
  Cleanup pass on printf.
  More half-finished cleanup of printf.c, and more test suite entries.
  Since "printf" is a shell builtin, printf.test wasn't actually testing anything.
  More printf cleanup, and test suite entries.

tests/printf.test
toys/pending/printf.c [deleted file]
toys/posix/chgrp.c
toys/posix/printf.c [new file with mode: 0644]
toys/posix/sed.c

index 96789bd..5cbefb5 100644 (file)
@@ -6,24 +6,54 @@
 [ -f testing.sh ] && . testing.sh
 
 #testing "name" "command" "result" "infile" "stdin"
-#set -x
 
-testing "printf TEXT" "printf toyTestText" "toyTestText" "" ""
-testing "printf MULTILINE_TEXT" \
-  "printf 'Testing\nmultiline\ntext\nfrom\ntoybox\tcommand.\b'" \
-  "Testing\nmultiline\ntext\nfrom\ntoybox\tcommand.\b" "" ""
+# Disable shell builtin
+PRINTF="$(which printf)"
+
+testing "printf text" "$PRINTF TEXT" "TEXT" "" ""
+testing "printf escapes" "$PRINTF 'one\ntwo\n\v\t\r\f\e\b\athree'" \
+  "one\ntwo\n\v\t\r\f\e\b\athree" "" ""
+testing "printf %b escapes" "$PRINTF %b 'one\ntwo\n\v\t\r\f\e\b\athree'" \
+  "one\ntwo\n\v\t\r\f\e\b\athree" "" ""
+testing "printf null" "$PRINTF 'x\0y' | od -An -tx1" ' 78 00 79\n' "" ""
+testing "printf trailing slash" "$PRINTF 'abc\'" 'abc\' "" ""
+testing "printf octal" "$PRINTF ' \1\002\429\045x'" ' \001\002"9%x' "" ""
+testing "printf not octal" "$PRINTF '\9'" '\9' "" ""
+testing "printf hex" "$PRINTF 'A\x1b\x2B\x3Q\xa' | od -An -tx1" \
+  ' 41 1b 2b 03 51 0a\n' "" ""
+testing "printf %x" "$PRINTF '%x\n' 0x2a" "2a\n" "" ""
+
+testing "printf %d 42" "$PRINTF %d 42" "42" "" ""
+testing "printf %d 0x2a" "$PRINTF %d 0x2a" "42" "" ""
+testing "printf %d 052" "$PRINTF %d 052" "42" "" ""
+
+testing "printf %s width precision" \
+  "$PRINTF '%3s,%.3s,%10s,%10.3s' abcde fghij klmno pqrst" \
+  "abcde,fgh,     klmno,       pqr" "" ""
+
+# posix: "The format operand shall be reused as often as necessary to satisfy
+# the argument operands."
+
+testing "printf extra args" "$PRINTF 'abc%s!%ddef\n' X 42 ARG 36" \
+       "abcX!42def\nabcARG!36def\n" "" ""
+
+testing "printf '%3c'" "$PRINTF '%3c' x" "  x" "" ""
+testing "printf '%-3c'" "$PRINTF '%-3c' x" "x  " "" ""
+testing "printf '%+d'" "$PRINTF '%+d' 5" "+5" "" ""
+
+
 testing "printf '%5d%4d' 1 21 321 4321 54321" \
-  "printf '%5d%4d' 1 21 321 4321 54321" "    1  21  321432154321   0" "" ""
-testing "printf '%c %c' 78 79" "printf '%c %c' 78 79" "7 7" "" ""
-testing "printf '%d %d' 78 79" "printf '%d %d' 78 79" "78 79" "" ""
-testing "printf '%f %f' 78 79" "printf '%f %f' 78 79" \
+  "$PRINTF '%5d%4d' 1 21 321 4321 54321" "    1  21  321432154321   0" "" ""
+testing "printf '%c %c' 78 79" "$PRINTF '%c %c' 78 79" "7 7" "" ""
+testing "printf '%d %d' 78 79" "$PRINTF '%d %d' 78 79" "78 79" "" ""
+testing "printf '%f %f' 78 79" "$PRINTF '%f %f' 78 79" \
   "78.000000 79.000000" "" ""
-testing "printf 'f f' 78 79" "printf 'f f' 78 79" "f f" "" ""
-testing "printf '%i %i' 78 79" "printf '%i %i' 78 79" "78 79" "" ""
-testing "printf '%o %o' 78 79" "printf '%o %o' 78 79" "116 117" "" ""
-testing "printf '%u %u' 78 79" "printf '%u %u' 78 79" "78 79" "" ""
-testing "printf '%u %u' -1 -2" "printf '%u %u' -1 -2" \
+testing "printf 'f f' 78 79" "$PRINTF 'f f' 78 79" "f f" "" ""
+testing "printf '%i %i' 78 79" "$PRINTF '%i %i' 78 79" "78 79" "" ""
+testing "printf '%o %o' 78 79" "$PRINTF '%o %o' 78 79" "116 117" "" ""
+testing "printf '%u %u' 78 79" "$PRINTF '%u %u' 78 79" "78 79" "" ""
+testing "printf '%u %u' -1 -2" "$PRINTF '%u %u' -1 -2" \
   "18446744073709551615 18446744073709551614" "" ""
-testing "printf '%x %X' 78 79" "printf '%x %X' 78 79" "4e 4F" "" ""
-testing "printf '%g %G' 78 79" "printf '%g %G' 78 79" "78 79" "" ""
-testing "printf '%s %s' 78 79" "printf '%s %s' 78 79" "78 79" "" ""
+testing "printf '%x %X' 78 79" "$PRINTF '%x %X' 78 79" "4e 4F" "" ""
+testing "printf '%g %G' 78 79" "$PRINTF '%g %G' 78 79" "78 79" "" ""
+testing "printf '%s %s' 78 79" "$PRINTF '%s %s' 78 79" "78 79" "" ""
diff --git a/toys/pending/printf.c b/toys/pending/printf.c
deleted file mode 100644 (file)
index b683128..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-/* printf.c - Format and Print the data.
- *
- * Copyright 2014 Sandeep Sharma <sandeep.jack2756@gmail.com>
- * Copyright 2014 Kyungwan Han <asura321@gmail.com>
- *
- * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html
-
-USE_PRINTF(NEWTOY(printf, "<1", TOYFLAG_USR|TOYFLAG_BIN))
-
-config PRINTF 
-  bool "printf"
-  default n
-  help
-    usage: printf FORMAT [ARGUMENT...]
-    
-    Format and print ARGUMENT(s) according to FORMAT, using C printf syntax
-    (percent escapes for cdeEfgGiosuxX, slash escapes for \abefnrtv0 or
-    \0OCT or 0xHEX).
-*/
-
-#define FOR_printf
-#include "toys.h"
-
-GLOBALS(
-  char *hv_w;
-  char *hv_p;
-  int encountered;
-)
-
-// Detect matching character (return true/valse) and advance pointer if match.
-static int eat(char **s, char c)
-{
-  int x = (**s == c);
-
-  if (x) ++*s;
-
-  return x;
-}
-
-// Add ll and L to Interger and floating point formats respectively.
-static char *get_format(char *f)
-{
-  int len = strlen(f);
-  char last = f[--len], *post = "";
-
-  f[len] = 0;
-  if (strchr("diouxX", last)) post = "ll";  // add ll to integer modifier.
-  else if (strchr("feEgG", last)) post = "L"; // add L to float modifier.
-  return xmprintf("%s%s%c", f, post, last);
-}
-
-// Print arguments with corresponding conversion and width and precision.
-static void print(char *fmt, int w, int p)
-{
-  char *ptr = fmt, *ep = 0, *format = 0, *arg = *toys.optargs;
-
-  errno = 0;
-  if (strchr("diouxX", *ptr)) {
-    long long val = 0;
-
-    if (arg) {
-      if (*arg == '\'' || *arg == '"') val = arg[1];
-      else {
-        val = strtoll(arg, &ep, 0);
-        if (errno || (ep && (*ep || ep == arg))) {
-          perror_msg("Invalid num %s", arg);
-          val = 0;
-        }
-      }
-    }
-    format = get_format(fmt);
-    TT.hv_w ? (TT.hv_p ? printf(format, w, p, val) : printf(format, w, val))
-      : (TT.hv_p ? printf(format, p, val) : printf(format, val));
-  } else if (strchr("gGeEf", *ptr)) {
-    long double dval = 0;
-
-    if (arg) {
-      dval = strtold(arg, &ep);
-      if (errno || (ep && (*ep || ep == arg))) {
-        perror_msg("Invalid num %s", arg);
-        dval = 0;
-      }
-    }
-    format = get_format(fmt);
-    TT.hv_w ? (TT.hv_p ? printf(format, w, p, dval) : printf(format, w, dval))
-      : (TT.hv_p ? printf(format, p, dval) :  printf(format, dval));
-  } else if (*ptr == 's') {
-    char *str = arg;
-
-    if (!str) str = "";
-
-    TT.hv_w ? (TT.hv_p ? printf(fmt,w,p,str): printf(fmt, w, str))
-      : (TT.hv_p ? printf(fmt, p, str) : printf(fmt, str));
-  } else if (*ptr == 'c') printf(fmt, arg ? *arg : 0);
-
-  if (format) free(format);
-}
-
-// Parse escape sequences.
-static int handle_slash(char **esc_val)
-{
-  char *ptr = *esc_val;
-  int len = 1, base = 0;
-  unsigned result = 0;
-
-  if (*ptr == 'c') xexit();
-
-  // 0x12 hex escapes have 1-2 digits, \123 octal escapes have 1-3 digits.
-  if (eat(&ptr, 'x')) base = 16;
-  else if (*ptr >= '0' && *ptr <= '8') base = 8;
-  len += (base-8)/8;
-
-  // Not a hex or octal escape? (This catches trailing \)
-  if (!len) {
-    if (!(result = unescape(*ptr))) result = '\\';
-    else ++*esc_val;
-
-    return result;
-  }
-
-  while (len) {
-    unsigned num = tolower(*ptr)-'0';
-
-    if (num > 10) num += '0'-'a'+10;
-    if (num >= base) {
-      // Don't parse invalid hex value ala "\xvd", print it verbatim
-      if (base == 16 && len == 2) {
-        ptr--;
-        result = '\\';
-      }
-      break;
-    }
-    result = (result*base)+num;
-    ptr++;
-    len--;
-  }
-  *esc_val = ptr;
-
-  return (char)result;
-}
-
-void printf_main(void)
-{
-  char *format = *toys.optargs, **arg = toys.optargs+1, *f, *p;
-
-  for (f = format; *f; f++) {
-    if (eat(&f, '\\')) putchar(handle_slash(&f));
-    else if (*f != '%' || *++f == '%') xputc(*f);
-    else if (*f == 'b')
-      for (p = *arg ? *(arg++) : ""; *p; p++) 
-        putchar(eat(&p, '\\') ? handle_slash(&p) : *p);
-    else {
-      char *start = f;
-      int wp[2], i;
-
-      // todo: we currently ignore these?
-      if (strchr("-+# ", *f)) f++;
-      memset(wp, 0, 8);
-      for (i=0; i<2; i++) {
-        if (eat(&f, '*')) {
-          if (*arg) wp[i] = atolx(*(arg++));
-        } else while (isdigit(*f)) f++;
-        if (!eat(&f, '.')) break;
-      }
-      if (!(p = strchr("diouxXfeEgGcs", *f)))
-        perror_exit("bad format@%ld", f-format);
-      else {
-        int len = f-start;
-
-        TT.hv_p = strstr(start, ".*");
-        TT.hv_w = strchr(start, '*');
-        //pitfall: handle diff b/w * and .*
-        if ((TT.hv_w-1) == TT.hv_p) TT.hv_w = NULL;
-        memcpy((p = xzalloc(len+1)), start, len);
-        print(p+len-1, wp[0], wp[1]);
-        if (*arg) arg++;
-        free(p);
-        p = NULL;
-      } 
-      TT.encountered = 1;
-    }
-  }
-}
index f573add..dc7741b 100644 (file)
@@ -4,20 +4,17 @@
  *
  * See http://opengroup.org/onlinepubs/9699919799/utilities/chown.html
  * See http://opengroup.org/onlinepubs/9699919799/utilities/chgrp.html
- *
- * TODO: group only one of [HLP]
 
-USE_CHGRP(NEWTOY(chgrp, "<2hPLHRfv", TOYFLAG_BIN))
-USE_CHGRP(OLDTOY(chown, chgrp, TOYFLAG_BIN))
+USE_CHGRP(NEWTOY(chgrp, "<2hPLHRfv[-HLP]", TOYFLAG_BIN))
+USE_CHOWN(OLDTOY(chown, chgrp, TOYFLAG_BIN))
 
 config CHGRP
-  bool "chgrp/chown"
+  bool "chgrp"
   default y
   help
-    usage: chown [-RHLP] [-fvh] [owner][:group] file...
-    usage: chgrp [-RHLP] [-fvh] group file...
+    usage: chgrp/chown [-RHLP] [-fvh] group file...
 
-    Change ownership of one or more files.
+    Change group of one or more files.
 
     -f suppress most error messages.
     -h change symlinks instead of what they point to
@@ -26,9 +23,16 @@ config CHGRP
     -L with -R change target of symlink, follow all symlinks
     -P with -R change symlink, do not follow symlinks (default)
     -v verbose output.
+
+config CHOWN
+  bool "chown"
+  default y
+  help
+    see: chgrp
 */
 
 #define FOR_chgrp
+#define FORCE_FLAGS
 #include "toys.h"
 
 GLOBALS(
@@ -109,3 +113,8 @@ void chgrp_main(void)
 
   if (CFG_TOYBOX_FREE && ischown) free(own);
 }
+
+void chown_main()
+{
+  chgrp_main();
+}
diff --git a/toys/posix/printf.c b/toys/posix/printf.c
new file mode 100644 (file)
index 0000000..5fec4f9
--- /dev/null
@@ -0,0 +1,143 @@
+/* printf.c - Format and Print the data.
+ *
+ * Copyright 2014 Sandeep Sharma <sandeep.jack2756@gmail.com>
+ * Copyright 2014 Kyungwan Han <asura321@gmail.com>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html
+ *
+ * todo: *m$ ala printf("%1$d:%2$.*3$d:%4$.*3$d\n", hour, min, precision, sec);
+
+USE_PRINTF(NEWTOY(printf, "<1", TOYFLAG_USR|TOYFLAG_BIN))
+
+config PRINTF 
+  bool "printf"
+  default y
+  help
+    usage: printf FORMAT [ARGUMENT...]
+    
+    Format and print ARGUMENT(s) according to FORMAT, using C printf syntax
+    (% escapes for cdeEfgGiosuxX, \ escapes for abefnrtv0 or \OCTAL or \xHEX).
+*/
+
+#define FOR_printf
+#include "toys.h"
+
+// Detect matching character (return true/valse) and advance pointer if match.
+static int eat(char **s, char c)
+{
+  int x = (**s == c);
+
+  if (x) ++*s;
+
+  return x;
+}
+
+// Parse escape sequences.
+static int handle_slash(char **esc_val)
+{
+  char *ptr = *esc_val;
+  int len, base = 0;
+  unsigned result = 0, num;
+
+  if (*ptr == 'c') xexit();
+
+  // 0x12 hex escapes have 1-2 digits, \123 octal escapes have 1-3 digits.
+  if (eat(&ptr, 'x')) base = 16;
+  else if (*ptr >= '0' && *ptr <= '8') base = 8;
+  len = (char []){0,3,2}[base/8];
+
+  // Not a hex or octal escape? (This catches trailing \)
+  if (!len) {
+    if (!(result = unescape(*ptr))) result = '\\';
+    else ++*esc_val;
+
+    return result;
+  }
+
+  while (len) {
+    num = tolower(*ptr) - '0';
+    if (num >= 'a'-'0') num += '0'-'a'+10;
+    if (num >= base) {
+      // Don't parse invalid hex value ala "\xvd", print it verbatim
+      if (base == 16 && len == 2) {
+        ptr--;
+        result = '\\';
+      }
+      break;
+    }
+    result = (result*base)+num;
+    ptr++;
+    len--;
+  }
+  *esc_val = ptr;
+
+  return result;
+}
+
+void printf_main(void)
+{
+  char **arg = toys.optargs+1;
+
+  // Repeat format until arguments consumed
+  for (;;) {
+    int seen = 0;
+    char *f = *toys.optargs;
+
+    // Loop through characters in format
+    while (*f) {
+      if (eat(&f, '\\')) putchar(handle_slash(&f));
+      else if (!eat(&f, '%') || *f == '%') putchar(*f++);
+
+      // Handle %escape
+      else {
+        char c, *end = 0, *aa, *to = toybuf;
+        int wp[] = {0,-1}, i;
+
+        // Parse width.precision between % and type indicator.
+        *to++ = '%';
+        while (strchr("-+# '0", *f) && (to-toybuf)<10) *to++ = *f++;
+        for (i=0; i<2; i++) {
+          if (eat(&f, '*')) {
+            if (*arg) wp[i] = atolx(*arg++);
+          } else while (*f >= '0' && *f <= '9') {
+            if (wp[i]<0) wp[i] = 0;
+            wp[i] = (wp[i]*10)+(*f++)-'0';
+          }
+          if (!eat(&f, '.')) break;
+        }
+        c = *f++;
+        seen = sprintf(to, "*.*%c", c);;
+        errno = 0;
+        aa = *arg ? *arg++ : "";
+
+        // Output %esc using parsed format string
+        if (c == 'b') {
+          while (*aa) putchar(eat(&aa, '\\') ? handle_slash(&aa) : *aa++);
+
+          continue;
+        } else if (c == 'c') printf(toybuf, wp[0], wp[1], *aa);
+        else if (c == 's') printf(toybuf, wp[0], wp[1], aa);
+        else if (strchr("diouxX", c)) {
+          long ll;
+
+          if (*aa == '\'' || *aa == '"') ll = aa[1];
+          else ll = strtoll(aa, &end, 0);
+
+          sprintf(to, "*.*ll%c", c);
+          printf(toybuf, wp[0], wp[1], ll);
+        } else if (strchr("feEgG", c)) {
+          long double ld = strtold(aa, &end);
+
+          sprintf(to, "*.*L%c", c);
+          printf(toybuf, wp[0], wp[1], ld);
+        } else error_exit("bad %%%c@%ld", c, f-*toys.optargs);
+
+        if (end && (errno || *end)) perror_msg("bad %%%c %s", c, aa);
+      }
+    }
+
+    // Posix says to keep looping through format until we consume all args.
+    // This only works if the format actually consumed at least one arg.
+    if (!seen || !*arg) break;
+  }
+}
index 8bd309f..e35325d 100644 (file)
@@ -297,7 +297,9 @@ static void walk_pattern(char **pline, long plen)
   if (line[len-1] == '\n') line[--len] = eol++;
   TT.count++;
 
-  logrus = TT.restart ? TT.restart : (void *)TT.pattern;
+  // The restart-1 is because we added one to make sure it wasn't NULL,
+  // otherwise N as last command would restart script
+  logrus = TT.restart ? ((struct step *)TT.restart)-1 : (void *)TT.pattern;
   TT.restart = 0;
 
   while (logrus) {
@@ -452,14 +454,14 @@ static void walk_pattern(char **pline, long plen)
       toybuf[off++] = '$';
       emit(toybuf, off, 1);
     } else if (c=='n') {
-      TT.restart = logrus->next;
+      TT.restart = logrus->next+1;
 
       break;
     } else if (c=='N') {
       // Can't just grab next line because we could have multiple N and
       // we need to actually read ahead to get N;$p EOF detection right.
       if (pline) {
-        TT.restart = logrus->next;
+        TT.restart = logrus->next+1;
         extend_string(&line, TT.nextline, len, -TT.nextlen);
         free(TT.nextline);
         TT.nextline = line;
@@ -711,7 +713,11 @@ static char *unescape_delimited_string(char **pstr, char *delim, int regex)
   to = delim = xmalloc(strlen(*pstr)+1);
 
   while (mode || *from != d) {
-    if (!*from) return 0;
+    if (!*from) {
+      *to = 0;
+
+      return 0;
+    }
 
     // delimiter in regex character range doesn't count
     if (*from == '[') {
@@ -746,22 +752,25 @@ static char *unescape_delimited_string(char **pstr, char *delim, int regex)
 static void jewel_of_judgement(char **pline, long len)
 {
   struct step *corwin = (void *)TT.pattern;
-  char *line = *pline, *reg, c, *errstart = *pline;
+  char *line, *reg, c, *errstart;
   int i;
 
+  line = errstart = pline ? *pline : "";
+
   // Append additional line to pattern argument string?
   // We temporarily repurpose "hit" to indicate line continuations
   if (corwin && corwin->prev->hit) {
+    if (!*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);
-    corwin->hit = 0;
     c = corwin->c;
     reg = (char *)corwin;
     reg += corwin->arg1 + strlen(reg + corwin->arg1);
 
-    // Resume parsing
-    goto append;
+    // Resume parsing for 'a' or 's' command
+    if (corwin->hit < 256) goto resume_s;
+    else goto resume_a;
   }
 
   // Loop through commands in line
@@ -770,8 +779,12 @@ static void jewel_of_judgement(char **pline, long len)
   for (;;) {
     if (corwin) dlist_add_nomalloc(&TT.pattern, (void *)corwin);
 
-    while (isspace(*line) || *line == ';') line++;
-    if (!*line || *line == '#') return;
+    for (;;) {
+      while (isspace(*line) || *line == ';') line++;
+      if (*line == '#') while (*line && *line != '\n') line++;
+      else break;
+    }
+    if (!*line) return;
 
     errstart = line;
     memset(toybuf, 0, sizeof(struct step));
@@ -824,27 +837,51 @@ static void jewel_of_judgement(char **pline, long len)
     else if (c == '}') {
       if (!TT.nextlen--) break;
     } else if (c == 's') {
-      char *merlin, *fiona, delim = 0;
+      char *fiona, delim = 0;
 
       // s/pattern/replacement/flags
 
+      // line continuations use arg1, so we fill out arg2 first (since the
+      // regex part can't be multiple lines) and swap them back later.
+
       // get pattern (just record, we parse it later)
-      corwin->arg1 = reg - (char *)corwin;
-      if (!(merlin = unescape_delimited_string(&line, &delim, 1))) goto brand;
+      corwin->arg2 = reg - (char *)corwin;
+      if (!(TT.remember = unescape_delimited_string(&line, &delim, 1)))
+        goto brand;
 
+      reg += sizeof(regex_t);
+      corwin->arg1 = reg-(char *)corwin;
+      corwin->hit = delim;
+resume_s:
       // get replacement - don't replace escapes because \1 and \& need
       // processing later, after we replace \\ with \ we can't tell \\1 from \1
       fiona = line;
-      while (*line != delim) {
-        if (!*line) goto brand;
-        if (*line == '\\') {
-          if (!line[1]) goto brand;
-          line += 2;
-        } else line++;
+      while (*fiona != corwin->hit) {
+        if (!*fiona) break;
+        if (*fiona++ == '\\') {
+          if (!*fiona || *fiona == '\n') {
+            fiona[-1] = '\n';
+            break;
+          }
+          fiona++;
+        }
       }
 
-      corwin->arg2 = corwin->arg1 + sizeof(regex_t);
-      reg = extend_string((void *)&corwin, fiona, corwin->arg2, line-fiona)+1;
+      reg = extend_string((void *)&corwin, line, reg-(char *)corwin,fiona-line);
+      line = fiona;
+      // line continuation? (note: '\n' can't be a valid delim).
+      if (*line == corwin->hit) corwin->hit = 0;
+      else {
+        if (!*line) continue;
+        reg--;
+        line++;
+        goto resume_s;
+      }
+
+      // swap arg1/arg2 so they're back in order arguments occur.
+      i = corwin->arg1;
+      corwin->arg1 = corwin->arg2;
+      corwin->arg2 = i;
 
       // get flags
       for (line++; *line; line++) {
@@ -861,10 +898,11 @@ static void jewel_of_judgement(char **pline, long len)
 
       // We deferred actually parsing the regex until we had the s///i flag
       // allocating the space was done by extend_string() above
-      if (!*merlin) corwin->arg1 = 0;
-      else xregcomp((void *)(corwin->arg1 + (char *)corwin), merlin,
+      if (!*TT.remember) corwin->arg1 = 0;
+      else xregcomp((void *)(corwin->arg1 + (char *)corwin), TT.remember,
         ((toys.optflags & FLAG_r)*REG_EXTENDED)|((corwin->sflags&1)*REG_ICASE));
-      free(merlin);
+      free(TT.remember);
+      TT.remember = 0;
       if (*line == 'w') {
         line++;
         goto writenow;
@@ -917,10 +955,14 @@ writenow:
     } else if (strchr("abcirtTw:", c)) {
       int end;
 
-      // Trim whitespace from "b ;" and ": blah " but only first space in "w x "
-
       while (isspace(*line) && *line != '\n') line++;
-append:
+
+      // Resume logic differs from 's' case because we don't add a newline
+      // unless it's after something, so we add it on return instead.
+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"))) {
         if (strchr("btT", c)) continue;
         else if (!corwin->arg1) break;
@@ -938,13 +980,17 @@ append:
       // Line continuation? (Two slightly different input methods, -e with
       // embedded newline vs -f line by line. Must parse both correctly.)
       if (!strchr("btT:", c) && line[-1] == '\\') {
-        // reg is next available space, so reg[-1] is the null terminator
-        reg[-2] = 0;
-        if (*line && line[1]) {
-          reg -= 2;
-          line++;
-          goto append;
-        } else corwin->hit++;
+        // backslash only matters if we have an odd number of them
+        for (i = 0; i<end; i++) if (line[-i-1] != '\\') break;
+        if (i&1) {
+          // reg is next available space, so reg[-1] is the null terminator
+          reg[-2] = 0;
+          if (*line && line[1]) {
+            reg -= 2;
+            line++;
+            goto resume_a;
+          } else corwin->hit = 256;
+        }
       }
 
     // Commands that take no arguments
@@ -982,6 +1028,7 @@ void sed_main(void)
     jewel_of_judgement(&dworkin->arg, strlen(dworkin->arg));
   for (dworkin = TT.f; dworkin; dworkin = dworkin->next)
     do_lines(xopen(dworkin->arg, O_RDONLY), dworkin->arg, jewel_of_judgement);
+  jewel_of_judgement(0, 0);
   dlist_terminate(TT.pattern);
   if (TT.nextlen) error_exit("no }");