]> granicus.if.org Git - yasm/commitdiff
file.c (yasm_unescape_cstring): New function to unescape a string following
authorPeter Johnson <peter@tortall.net>
Tue, 21 Nov 2006 08:55:43 +0000 (08:55 -0000)
committerPeter Johnson <peter@tortall.net>
Tue, 21 Nov 2006 08:55:43 +0000 (08:55 -0000)
C-style escaping conventions.  Will update GAS parser to use this.

svn path=/trunk/yasm/; revision=1696

libyasm/file.c
libyasm/file.h
libyasm/tests/Makefile.inc
libyasm/tests/uncstring_test.c [new file with mode: 0644]

index 63f37b942f090627cb35a1cba5263f0b094d0634..4d45f86261b6ddda4112390e93ea5875c80794ba 100644 (file)
@@ -42,6 +42,7 @@
 #include "util.h"
 /*@unused@*/ RCSID("$Id$");
 
+#include "errwarn.h"
 #include "file.h"
 
 #define BSIZE  8192        /* Fill block size */
@@ -110,6 +111,71 @@ yasm_fill_helper(yasm_scanner *s, unsigned char **cursor,
     return first;
 }
 
+void
+yasm_unescape_cstring(unsigned char *str, size_t *len)
+{
+    unsigned char *s = str;
+    unsigned char *o = str;
+    unsigned char t[4];
+
+    while ((size_t)(s-str)<*len) {
+       if (*s == '\\' && (size_t)(&s[1]-str)<*len) {
+           s++;
+           switch (*s) {
+               case 'b': *o = '\b'; s++; break;
+               case 'f': *o = '\f'; s++; break;
+               case 'n': *o = '\n'; s++; break;
+               case 'r': *o = '\r'; s++; break;
+               case 't': *o = '\t'; s++; break;
+               case 'x':
+                   /* hex escape; grab last two digits */
+                   s++;
+                   while ((size_t)(&s[2]-str)<*len && isxdigit(s[0])
+                          && isxdigit(s[1]) && isxdigit(s[2]))
+                       s++;
+                   if ((size_t)(s-str)<*len && isxdigit(*s)) {
+                       t[0] = *s++;
+                       t[1] = '\0';
+                       t[2] = '\0';
+                       if ((size_t)(s-str)<*len && isxdigit(*s))
+                           t[1] = *s++;
+                       *o = (unsigned char)strtoul((char *)t, NULL, 16);
+                   } else
+                       *o = '\0';
+                   break;
+               default:
+                   if (isdigit(*s)) {
+                       int warn = 0;
+                       /* octal escape */
+                       if (*s > '7')
+                           warn = 1;
+                       *o = *s++ - '0';
+                       if ((size_t)(s-str)<*len && isdigit(*s)) {
+                           if (*s > '7')
+                               warn = 1;
+                           *o <<= 3;
+                           *o += *s++ - '0';
+                           if ((size_t)(s-str)<*len && isdigit(*s)) {
+                               if (*s > '7')
+                                   warn = 1;
+                               *o <<= 3;
+                               *o += *s++ - '0';
+                           }
+                       }
+                       if (warn)
+                           yasm_warn_set(YASM_WARN_GENERAL,
+                                         N_("octal value out of range"));
+                   } else
+                       *o = *s++;
+                   break;
+           }
+           o++;
+       } else
+           *o++ = *s++;
+    }
+    *len = o-str;
+}
+
 size_t
 yasm__splitpath_unix(const char *path, /*@out@*/ const char **tail)
 {
index 3569dd2f5c2c73bb644a60578f82c50b7bc56c34..e739cfd1a97465b0e1b43c9b8067844bccb11921 100644 (file)
@@ -69,6 +69,17 @@ int yasm_fill_helper
      size_t (*input_func) (void *d, unsigned char *buf, size_t max),
      void *input_func_data);
 
+/** Unescape a string with C-style escapes.  Handles b, f, n, r, t, and hex
+ * and octal escapes.  String is updated in-place.
+ * Edge cases:
+ * - hex escapes: reads as many hex digits as possible, takes last 2 as value.
+ * - oct escapes: takes up to 3 digits 0-9 and scales appropriately, with
+ *                warning.
+ * \param str          C-style string (updated in place)
+ * \param len          length of string (updated with new length)
+ */
+void yasm_unescape_cstring(unsigned char *str, size_t *len);
+
 /** Split a UNIX pathname into head (directory) and tail (base filename)
  * portions.
  * \internal
index 353c3464464e6b916873ffed9c705c120f77f92b..13431f42cf5d1ac2e7d48de451841aa325df7eb7 100644 (file)
@@ -5,6 +5,7 @@ TESTS += floatnum_test
 TESTS += leb128_test
 TESTS += splitpath_test
 TESTS += combpath_test
+TESTS += uncstring_test
 TESTS += libyasm/tests/libyasm_test.sh
 
 EXTRA_DIST += libyasm/tests/libyasm_test.sh
@@ -67,6 +68,7 @@ check_PROGRAMS += floatnum_test
 check_PROGRAMS += leb128_test
 check_PROGRAMS += splitpath_test
 check_PROGRAMS += combpath_test
+check_PROGRAMS += uncstring_test
 
 bitvect_test_SOURCES  = libyasm/tests/bitvect_test.c
 bitvect_test_LDADD = libyasm.a $(INTLLIBS)
@@ -83,3 +85,5 @@ splitpath_test_LDADD = libyasm.a $(INTLLIBS)
 combpath_test_SOURCES  = libyasm/tests/combpath_test.c
 combpath_test_LDADD = libyasm.a $(INTLLIBS)
 
+uncstring_test_SOURCES  = libyasm/tests/uncstring_test.c
+uncstring_test_LDADD = libyasm.a $(INTLLIBS)
diff --git a/libyasm/tests/uncstring_test.c b/libyasm/tests/uncstring_test.c
new file mode 100644 (file)
index 0000000..22ca3fc
--- /dev/null
@@ -0,0 +1,163 @@
+/* $Id$
+ *
+ *  Copyright (C) 2006  Peter Johnson
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#endif
+
+#include <stdio.h>
+
+#include "util.h"
+#include "libyasm/errwarn.h"
+#include "libyasm/file.h"
+
+typedef struct Test_Entry {
+    /* input string */
+    const char *input;
+
+    /* input length */
+    size_t in_len;
+
+    /* correct output string */
+    const char *result;
+
+    /* correct output length */
+    size_t result_len;
+
+    /* expected warning, if any */
+    const char *warn;
+} Test_Entry;
+
+static Test_Entry tests[] = {
+    {"noescape", 8, "noescape", 8, NULL},
+    {"noescape2", 10, "noescape2", 10, NULL},  /* includes trailing zero */
+    {"\\\\\\b\\f\\n\\r\\t\\\"", 14, "\\\b\f\n\r\t\"", 7, NULL},
+    {"\\a", 2, "a", 1, NULL},
+    /* hex tests */
+    {"\\x", 2, "\x00", 1, NULL},
+    {"\\x12", 4, "\x12", 1, NULL},
+    {"\\x1234", 6, "\x34", 1, NULL},
+    {"\\xg", 3, "\x00g", 2, NULL},
+    {"\\xaga", 5, "\x0aga", 3, NULL},
+    {"\\xaag", 5, "\xaag", 2, NULL},
+    {"\\xaaa", 5, "\xaa", 1, NULL},
+    {"\\x55559", 7, "\x59", 1, NULL},
+
+    /* oct tests */
+    {"\\778", 4, "\000", 1, "octal value out of range"},
+    {"\\779", 4, "\001", 1, "octal value out of range"},
+    {"\\1x", 3, "\001x", 2, NULL},
+    {"\\7779", 5, "\xff" "9", 2, NULL},
+    {"\\7999", 5, "\x11" "9", 2, "octal value out of range"},
+    {"\\77a", 4, "\077a", 2, NULL},
+    {"\\5555555", 8, "\x6d" "5555", 5, NULL},
+    {"\\9999", 5, "\x91" "9", 2, "octal value out of range"},
+};
+
+static char failed[1000];
+static char failmsg[100];
+
+static int
+run_test(Test_Entry *test)
+{
+    char str[256];
+    size_t len;
+    yasm_warn_class wclass;
+    char *wstr;
+
+    strncpy(str, test->input, test->in_len);
+    len = test->in_len;
+
+    yasm_unescape_cstring((unsigned char *)str, &len);
+    if (len != test->result_len) {
+       sprintf(failmsg,
+               "unescape_cstring(\"%s\", %lu) bad output len: expected %lu, got %lu!",
+               test->input, (unsigned long)test->in_len,
+               (unsigned long)test->result_len, (unsigned long)len);
+       return 1;
+    }
+
+    if (strncmp(str, test->result, len) != 0) {
+       sprintf(failmsg,
+               "unescape_cstring(\"%s\", %lu) bad output: expected \"%s\", got \"%s\"!",
+               test->input, (unsigned long)test->in_len, test->result, str);
+       return 1;
+    }
+
+    yasm_warn_fetch(&wclass, &wstr);
+    if (wstr != NULL && test->warn == NULL) {
+       sprintf(failmsg,
+               "unescape_cstring(\"%s\", %lu) unexpected warning: %s!",
+               test->input, (unsigned long)test->in_len, wstr);
+       return 1;
+    }
+    if (wstr == NULL && test->warn != NULL) {
+       sprintf(failmsg,
+               "unescape_cstring(\"%s\", %lu) expected warning: %s, did not get it!",
+               test->input, (unsigned long)test->in_len, test->warn);
+       return 1;
+    }
+    if (wstr && test->warn && strcmp(wstr, test->warn) != 0) {
+       sprintf(failmsg,
+               "unescape_cstring(\"%s\", %lu) expected warning: %s, got %s!",
+               test->input, (unsigned long)test->in_len, test->warn, wstr);
+       return 1;
+    }
+    yasm_xfree(wstr);
+
+    return 0;
+}
+
+int
+main(void)
+{
+    int nf = 0;
+    int numtests = sizeof(tests)/sizeof(Test_Entry);
+    int i;
+
+    yasm_errwarn_initialize();
+
+    failed[0] = '\0';
+    printf("Test uncstring_test: ");
+    for (i=0; i<numtests; i++) {
+       int fail = run_test(&tests[i]);
+       printf("%c", fail>0 ? 'F':'.');
+       fflush(stdout);
+       if (fail)
+           sprintf(failed, "%s ** F: %s\n", failed, failmsg);
+       nf += fail;
+    }
+
+    printf(" +%d-%d/%d %d%%\n%s",
+          numtests-nf, nf, numtests, 100*(numtests-nf)/numtests, failed);
+
+    yasm_errwarn_cleanup();
+    return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}