} \
strcpy(yylval->strval, yytext); \
} while(0)
+
+static char *to_octal(char *dst, unsigned val)
+{
+ unsigned i;
+
+ for (i = 0; i < 3; i++) {
+ *dst++ = '0' + ((val >> 6) & 7u);
+ val <<= 3;
+ }
+
+ return dst;
+}
+
+/*
+ * Convert a single character to a C-style character constant, including quote
+ * characters. At most 7 bytes are written to the buffer for the longest
+ * octal encoding, e.g., '\177'
+ */
+static void to_readable_ch(char *dst, char c)
+{
+ unsigned char uc = c;
+ unsigned i;
+ char esc;
+
+ /*
+ * The 7 standard C control characters are contiguous in ASCII,
+ * permitting a simple and compact lookup table; separating their
+ * handling from backslash and quote characters hopefully allows
+ * the compiler to recognize that.
+ */
+ switch (c) {
+ case '\a': i = 0; break;
+ case '\b': i = 1; break;
+ case '\t': i = 2; break;
+ case '\n': i = 3; break;
+ case '\v': i = 4; break;
+ case '\f': i = 5; break;
+ case '\r': i = 6; break;
+ default: i = 7; break;
+ }
+ esc = "abtnvfr"[i];
+
+ /* Otherwise printable characters that should still be escaped. */
+ switch (c) {
+ case '\\': case '\'': esc = c; break;
+ }
+
+ *dst++ = '\'';
+ if (esc) {
+ *dst++ = '\\';
+ *dst++ = esc;
+ } else if (isprint(uc)) {
+ *dst++ = c;
+ } else {
+ *dst++ = '\\';
+ dst = to_octal(dst, uc);
+ }
+ *dst++ = '\'';
+ *dst++ = 0;
+}
+
%}
%s ENGLISH
[[:space:]]+
. {
- char buf[5] = { yytext[0] };
- unsigned char c = buf[0];
-
- if (!isprint(c) || c == '\\' || c == '\'') {
- /* Encode nonprinting characters with C-style escapes */
- buf[0] = '\\';
- switch (c) {
- case '\a': buf[1] = 'a'; break;
- case '\b': buf[1] = 'b'; break;
- case '\f': buf[1] = 'f'; break;
- case '\n': buf[1] = 'n'; break;
- case '\r': buf[1] = 'r'; break;
- case '\t': buf[1] = 't'; break;
- case '\v': buf[1] = 'v'; break;
- case '\\': buf[1] = '\\'; break;
- case '\'': buf[1] = '\''; break;
- default:
- buf[1] = '0' + ((c >> 6) & 3);
- buf[2] = '0' + ((c >> 3) & 7);
- buf[3] = '0' + ((c >> 0) & 7);
- }
- }
+ char buf[8];
- cdecl__err(CDECL_ENOPARSE, _("syntax error, unexpected '%s'"), buf);
+ to_readable_ch(buf, yytext[0]);
+ cdecl__err(CDECL_ENOPARSE, _("syntax error, unexpected %s"), buf);
return T_LEX_ERROR;
}