]> granicus.if.org Git - nethack/commitdiff
escapes() revamp
authornethack.rankin <nethack.rankin>
Thu, 4 Aug 2011 02:41:44 +0000 (02:41 +0000)
committernethack.rankin <nethack.rankin>
Thu, 4 Aug 2011 02:41:44 +0000 (02:41 +0000)
     Partial rewrite of escapes(), mostly changing its if-then-else
logic so that end-of-string can be checked once instead for each case.
The previous version had a bug if the input string ended with backslash
and one decimal digit (due to being lumped together with the handling
for trailing \X or \O).

src/options.c

index c4a696aa0827f58e886651803c49b427c0ed4c02..49911f0f47c83c7107a9089325bf0473ccafa4ef 100644 (file)
@@ -753,40 +753,58 @@ nmcpy(dest, src, maxlen)
  * has the effect of 'meta'-ing the value which follows (so that the
  * alternate character set will be enabled).
  *
- * For 3.4.3 and earlier, ending with "\M", backslash, or caret prior
- * to terminating '\0' would pull that '\0' into the output and then
- * keep processing past it.  Now, a trailing escape will be handled
- * as if it was preceded with its own backslash and be kept as is,
- * with end of string terminator always honored as end of input.
+ * For 3.4.3 and earlier, input ending with "\M", backslash, or caret
+ * prior to terminating '\0' would pull that '\0' into the output and then
+ * keep processing past it, potentially overflowing the output buffer.
+ * Now, trailing \ or ^ will act like \\ or \^ and add '\\' or '^' to the
+ * output and stop there; trailing \M will fall through to \<other> and
+ * yield 'M', then stop.  Any \X or \O followed by something other than
+ * an appropriate digit will also fall through to \<other> and yield 'X'
+ * or 'O', plus stop if the non-digit is end-of-string.
  */
 STATIC_OVL void
 escapes(cp, tp)
 const char     *cp;
 char *tp;
 {
+    static NEARDATA const char
+       oct[] = "01234567", dec[] = "0123456789",
+       hex[] = "00112233445566778899aAbBcCdDeEfF";
+    const char *dp;
+    int cval, meta, dcount;
+
     while (*cp) {
-       int     cval = 0, meta = 0;
-
-       if (*cp == '\\' && cp[1] && index("mM", cp[1]) && cp[2]) {
-               meta = 1;
-               cp += 2;
-       }
-       if (*cp == '\\' && cp[1] && index("0123456789xXoO", cp[1]) && cp[2]) {
-           NEARDATA const char hex[] = "00112233445566778899aAbBcCdDeEfF";
-           const char *dp;
-           int dcount = 0;
-
-           cp++;
-           if (*cp == 'x' || *cp == 'X')
-               for (++cp; *cp && (dp = index(hex, *cp)) && (dcount++ < 2); cp++)
-                   cval = (cval * 16) + ((int)(dp - hex) / 2);
-           else if (*cp == 'o' || *cp == 'O')
-               for (++cp; *cp && (index("01234567",*cp)) && (dcount++ < 3); cp++)
-                   cval = (cval * 8) + (*cp - '0');
-           else
-               for (; *cp && (index("0123456789",*cp)) && (dcount++ < 3); cp++)
-                   cval = (cval * 10) + (*cp - '0');
-       } else if (*cp == '\\' && cp[1]) {      /* C-style character escapes */
+       /* \M has to be followed by something to do meta conversion,
+          otherwise it will just be \M which ultimately yields 'M' */
+       meta = (*cp == '\\' && (cp[1] == 'm' || cp[1] == 'M') && cp[2]);
+       if (meta) cp += 2;
+
+       cval = dcount = 0; /* for decimal, octal, hexadecimal cases */
+       if ((*cp != '\\' && *cp != '^') || !cp[1]) {
+           /* simple character, or nothing left for \ or ^ to escape */
+           cval = *cp++;
+       } else if (*cp == '^') {        /* expand control-character syntax */
+           cval = (*++cp & 0x1f);
+           ++cp;
+       /* remaining cases are all for backslash and we know cp[1] is not \0 */
+       } else if (index(dec, cp[1])) {
+           ++cp;       /* move past backslash to first digit */
+           do {
+               cval = (cval * 10) + (*cp - '0');
+           } while (*++cp && index(dec, *cp) && ++dcount < 3);
+       } else if ((cp[1] == 'o' || cp[1] == 'O') &&
+               cp[2] && index(oct, cp[2])) {
+           cp += 2;    /* move past backslash and 'O' */
+           do {
+               cval = (cval * 8) + (*cp - '0');
+           } while (*++cp && index(oct, *cp) && ++dcount < 3);
+       } else if ((cp[1] == 'x' || cp[1] == 'X') &&
+               cp[2] && (dp = index(hex, cp[2])) != 0) {
+           cp += 2;    /* move past backslash and 'X' */
+           do {
+               cval = (cval * 16) + ((int)(dp - hex) / 2);
+           } while (*++cp && (dp = index(hex, *cp)) != 0 && ++dcount < 2);
+       } else {                        /* C-style character escapes */
            switch (*++cp) {
            case '\\': cval = '\\'; break;
            case 'n': cval = '\n'; break;
@@ -795,16 +813,12 @@ char *tp;
            case 'r': cval = '\r'; break;
            default: cval = *cp;
            }
-           cp++;
-       } else if (*cp == '^' && cp[1]) { /* expand control-character syntax */
-           cval = (*++cp & 0x1f);
-           cp++;
-       } else
-           cval = *cp++;
+           ++cp;
+       }
 
        if (meta)
            cval |= 0x80;
-       *tp++ = cval;
+       *tp++ = (char)cval;
     }
     *tp = '\0';
 }