From 159f377293504adbea2a21769be8f00a02d2b94f Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Tue, 21 Nov 2006 08:55:43 +0000 Subject: [PATCH] file.c (yasm_unescape_cstring): New function to unescape a string following C-style escaping conventions. Will update GAS parser to use this. svn path=/trunk/yasm/; revision=1696 --- libyasm/file.c | 66 +++++++++++++ libyasm/file.h | 11 +++ libyasm/tests/Makefile.inc | 4 + libyasm/tests/uncstring_test.c | 163 +++++++++++++++++++++++++++++++++ 4 files changed, 244 insertions(+) create mode 100644 libyasm/tests/uncstring_test.c diff --git a/libyasm/file.c b/libyasm/file.c index 63f37b94..4d45f862 100644 --- a/libyasm/file.c +++ b/libyasm/file.c @@ -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) { diff --git a/libyasm/file.h b/libyasm/file.h index 3569dd2f..e739cfd1 100644 --- a/libyasm/file.h +++ b/libyasm/file.h @@ -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 diff --git a/libyasm/tests/Makefile.inc b/libyasm/tests/Makefile.inc index 353c3464..13431f42 100644 --- a/libyasm/tests/Makefile.inc +++ b/libyasm/tests/Makefile.inc @@ -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 index 00000000..22ca3fc3 --- /dev/null +++ b/libyasm/tests/uncstring_test.c @@ -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 +#endif + +#ifdef STDC_HEADERS +# include +# include +#endif + +#include + +#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; i0 ? '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; +} -- 2.40.0