ash: optional support for $'...\t...\n...' bashism
authorDenis Vlasenko <vda.linux@googlemail.com>
Mon, 23 Jun 2008 01:52:30 +0000 (01:52 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Mon, 23 Jun 2008 01:52:30 +0000 (01:52 -0000)
function                                             old     new   delta
readtoken1                                          2824    3172    +348
static.C_escapes                                       -      18     +18
parse_command                                       1504    1500      -4
SIT                                                   89      83      -6
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 1/2 up/down: 366/-10)           Total: 356 bytes

shell/ash.c
shell/ash_test/ash-quoting/dollar_squote_bash1.right [new file with mode: 0644]
shell/ash_test/ash-quoting/dollar_squote_bash1.tests [new file with mode: 0755]

index 7924c4dd9bf26d115d1597da7038bfa08a744949..8a731fd6815e6335b2b3af23cdf6f678b9057638 100644 (file)
@@ -1460,19 +1460,19 @@ _STPUTC(int c, char *p)
                if (l > m) \
                        (p) = makestrspace(l, q); \
        } while (0)
-#define USTPUTC(c, p)           (*p++ = (c))
+#define USTPUTC(c, p)           (*(p)++ = (c))
 #define STACKSTRNUL(p) \
        do { \
                if ((p) == sstrend) \
-                       p = growstackstr(); \
-               *p = '\0'; \
+                       (p) = growstackstr(); \
+               *(p) = '\0'; \
        } while (0)
-#define STUNPUTC(p)             (--p)
-#define STTOPC(p)               (p[-1])
-#define STADJUST(amount, p)     (p += (amount))
+#define STUNPUTC(p)             (--(p))
+#define STTOPC(p)               ((p)[-1])
+#define STADJUST(amount, p)     ((p) += (amount))
 
 #define grabstackstr(p)         stalloc((char *)(p) - (char *)stackblock())
-#define ungrabstackstr(s, p)    stunalloc((s))
+#define ungrabstackstr(s, p)    stunalloc(s)
 #define stackstrend()           ((void *)sstrend)
 
 
@@ -2687,10 +2687,10 @@ SIT(int c, int syntax)
        ) {
                return CCTL;
        } else {
-               s = strchr(spec_symbls, c);
-               if (s == NULL || *s == '\0')
+               s = strchrnul(spec_symbls, c);
+               if (*s == '\0')
                        return CWORD;
-               indx = syntax_index_table[(s - spec_symbls)];
+               indx = syntax_index_table[s - spec_symbls];
        }
        return S_I_T[indx][syntax];
 }
@@ -4201,7 +4201,7 @@ cmdputs(const char *s)
        nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
        p = s;
        while ((c = *p++) != 0) {
-               str = 0;
+               str = NULL;
                switch (c) {
                case CTLESC:
                        c = *p++;
@@ -10345,6 +10345,52 @@ parse_command(void)
        return n1;
 }
 
+#if ENABLE_ASH_BASH_COMPAT
+static int decode_dollar_squote(void)
+{
+       static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
+       int c, cnt;
+       char *p;
+       char buf[4];
+
+       c = pgetc();
+       p = strchr(C_escapes, c);
+       if (p) {
+               buf[0] = c;
+               p = buf;
+               cnt = 3;
+               if ((unsigned char)(c - '0') <= 7) { /* \ooo */
+                       do {
+                               c = pgetc();
+                               *++p = c;
+                       } while ((unsigned char)(c - '0') <= 7 && --cnt);
+                       pungetc();
+               } else if (c == 'x') { /* \xHH */
+                       do {
+                               c = pgetc();
+                               *++p = c;
+                       } while (isxdigit(c) && --cnt);
+                       pungetc();
+                       if (cnt == 3) { /* \x but next char is "bad" */
+                               c = 'x';
+                               goto unrecognized;
+                       }
+               } else { /* simple seq like \\ or \t */
+                       p++;
+               }
+               *p = '\0';
+               p = buf;
+               c = bb_process_escape_sequence((void*)&p);
+       } else { /* unrecognized "\z": print both chars unless ' or " */
+               if (c != '\'' && c != '"') {
+ unrecognized:
+                       c |= 0x100; /* "please encode \, then me" */
+               }
+       }
+       return c;
+}
+#endif
+
 /*
  * If eofmark is NULL, read a word or a redirection symbol.  If eofmark
  * is not NULL, read a here document.  In the latter case, eofmark is the
@@ -10356,14 +10402,12 @@ parse_command(void)
  * using goto's to implement the subroutine linkage.  The following macros
  * will run code that appears at the end of readtoken1.
  */
-
 #define CHECKEND()      {goto checkend; checkend_return:;}
 #define PARSEREDIR()    {goto parseredir; parseredir_return:;}
 #define PARSESUB()      {goto parsesub; parsesub_return:;}
 #define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
 #define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
 #define PARSEARITH()    {goto parsearith; parsearith_return:;}
-
 static int
 readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
 {
@@ -10385,6 +10429,8 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
        int parenlevel;      /* levels of parens in arithmetic */
        int dqvarnest;       /* levels of variables expansion within double quotes */
 
+       USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
+
 #if __GNUC__
        /* Avoid longjmp clobbering */
        (void) &out;
@@ -10435,6 +10481,15 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
                        case CCTL:
                                if (eofmark == NULL || dblquote)
                                        USTPUTC(CTLESC, out);
+#if ENABLE_ASH_BASH_COMPAT
+                               if (c == '\\' && bash_dollar_squote) {
+                                       c = decode_dollar_squote();
+                                       if (c & 0x100) {
+                                               USTPUTC('\\', out);
+                                               c = (unsigned char)c;
+                                       }
+                               }
+#endif
                                USTPUTC(c, out);
                                break;
                        case CBACK:     /* backslash */
@@ -10453,11 +10508,9 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
                                                USTPUTC('\\', out);
                                        }
 #endif
-                                       if (dblquote &&
-                                               c != '\\' && c != '`' &&
-                                               c != '$' && (
-                                                       c != '"' ||
-                                                       eofmark != NULL)
+                                       if (dblquote && c != '\\'
+                                        && c != '`' && c != '$'
+                                        && (c != '"' || eofmark != NULL)
                                        ) {
                                                USTPUTC(CTLESC, out);
                                                USTPUTC('\\', out);
@@ -10480,6 +10533,7 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
                                dblquote = 1;
                                goto quotemark;
                        case CENDQUOTE:
+                               USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
                                if (eofmark != NULL && arinest == 0
                                 && varnest == 0
                                ) {
@@ -10552,7 +10606,7 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
 
                        }
                        c = pgetc_macro();
-               }
+               } /* for(;;) */
        }
  endword:
 #if ENABLE_ASH_MATH_SUPPORT
@@ -10573,12 +10627,13 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
                if ((c == '>' || c == '<')
                 && quotef == 0
                 && len <= 2
-                && (*out == '\0' || isdigit(*out))) {
+                && (*out == '\0' || isdigit(*out))
+               ) {
                        PARSEREDIR();
-                       return lasttoken = TREDIR;
-               } else {
-                       pungetc();
+                       lasttoken = TREDIR;
+                       return lasttoken;
                }
+               pungetc();
        }
        quoteflag = quotef;
        backquotelist = bqlist;
@@ -10697,8 +10752,8 @@ parseredir: {
 /* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
  * (assuming ascii char codes, as the original implementation did) */
 #define is_special(c) \
-       ((((unsigned int)c) - 33 < 32) \
-                       && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
+       (((unsigned)(c) - 33 < 32) \
+                       && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
 parsesub: {
        int subtype;
        int typeloc;
@@ -10707,11 +10762,15 @@ parsesub: {
        static const char types[] ALIGN1 = "}-+?=";
 
        c = pgetc();
-       if (
-               c <= PEOA_OR_PEOF  ||
-               (c != '(' && c != '{' && !is_name(c) && !is_special(c))
+       if (c <= PEOA_OR_PEOF
+        || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
        ) {
-               USTPUTC('$', out);
+#if ENABLE_ASH_BASH_COMPAT
+               if (c == '\'')
+                       bash_dollar_squote = 1;
+               else
+#endif
+                       USTPUTC('$', out);
                pungetc();
        } else if (c == '(') {  /* $(command) or $((arith)) */
                if (pgetc() == '(') {
diff --git a/shell/ash_test/ash-quoting/dollar_squote_bash1.right b/shell/ash_test/ash-quoting/dollar_squote_bash1.right
new file mode 100644 (file)
index 0000000..57536b1
--- /dev/null
@@ -0,0 +1,9 @@
+a      b
+a
+b c
+def
+a'b c"d e\f
+a3b c3b e33f
+a\80b c08b
+a3b c30b
+x      y
diff --git a/shell/ash_test/ash-quoting/dollar_squote_bash1.tests b/shell/ash_test/ash-quoting/dollar_squote_bash1.tests
new file mode 100755 (executable)
index 0000000..93a56ca
--- /dev/null
@@ -0,0 +1,7 @@
+echo $'a\tb'
+echo $'a\nb' $'c\nd''ef'
+echo $'a\'b' $'c\"d' $'e\\f'
+echo $'a\63b' $'c\063b' $'e\0633f'
+echo $'a\80b' $'c\608b'
+echo $'a\x33b' $'c\x330b'
+echo $'x\x9y'