Show invalid escape sequences
[platform/upstream/libxkbcommon.git] / src / xkbcomp / scanner.c
index aef9a62..57babbb 100644 (file)
  * DEALINGS IN THE SOFTWARE.
  */
 
+#include "config.h"
+
 #include "xkbcomp-priv.h"
 #include "parser-priv.h"
 #include "scanner-utils.h"
 
-static void
-scanner_log(enum xkb_log_level level, struct scanner *s, const char *msg)
-{
-    xkb_log_cond_level(s->ctx, level, "%s:%d:%d: %s\n", s->file_name,
-                       s->token_line, s->token_column, msg);
-}
-
-int
-scanner_error(struct scanner *s, const char *msg)
-{
-    scanner_log(XKB_LOG_LEVEL_ERROR, s, msg);
-    return ERROR_TOK;
-}
-
-void
-scanner_warn(struct scanner *s, const char *msg)
-{
-    scanner_log(XKB_LOG_LEVEL_WARNING, s, msg);
-}
-
 static bool
 number(struct scanner *s, int64_t *out, int *out_tok)
 {
@@ -52,14 +34,14 @@ number(struct scanner *s, int64_t *out, int *out_tok)
     const char *start = s->s + s->pos;
     char *end;
 
-    if (lit(s, "0x")) {
-        while (isxdigit(peek(s))) next(s);
+    if (scanner_lit(s, "0x")) {
+        while (is_xdigit(scanner_peek(s))) scanner_next(s);
         is_hex = true;
     }
     else {
-        while (isdigit(peek(s))) next(s);
-        is_float = chr(s, '.');
-        while (isdigit(peek(s))) next(s);
+        while (is_digit(scanner_peek(s))) scanner_next(s);
+        is_float = scanner_chr(s, '.');
+        while (is_digit(scanner_peek(s))) scanner_next(s);
     }
     if (s->s + s->pos == start)
         return false;
@@ -68,6 +50,8 @@ number(struct scanner *s, int64_t *out, int *out_tok)
     if (is_hex)
         *out = strtoul(start, &end, 16);
     else if (is_float)
+        /* The parser currently just ignores floats, so the cast is
+         * fine - the value doesn't matter. */
         *out = strtod(start, &end);
     else
         *out = strtoul(start, &end, 10);
@@ -85,16 +69,16 @@ _xkbcommon_lex(YYSTYPE *yylval, struct scanner *s)
 
 skip_more_whitespace_and_comments:
     /* Skip spaces. */
-    while (isspace(peek(s))) next(s);
+    while (is_space(scanner_peek(s))) scanner_next(s);
 
     /* Skip comments. */
-    if (lit(s, "//") || chr(s, '#')) {
-        while (!eof(s) && !eol(s)) next(s);
+    if (scanner_lit(s, "//") || scanner_chr(s, '#')) {
+        scanner_skip_to_eol(s);
         goto skip_more_whitespace_and_comments;
     }
 
     /* See if we're done. */
-    if (eof(s)) return END_OF_FILE;
+    if (scanner_eof(s)) return END_OF_FILE;
 
     /* New token. */
     s->token_line = s->line;
@@ -102,89 +86,111 @@ skip_more_whitespace_and_comments:
     s->buf_pos = 0;
 
     /* String literal. */
-    if (chr(s, '\"')) {
-        while (!eof(s) && !eol(s) && peek(s) != '\"') {
-            if (chr(s, '\\')) {
+    if (scanner_chr(s, '\"')) {
+        while (!scanner_eof(s) && !scanner_eol(s) && scanner_peek(s) != '\"') {
+            if (scanner_chr(s, '\\')) {
                 uint8_t o;
-                if      (chr(s, '\\')) buf_append(s, '\\');
-                else if (chr(s, 'n'))  buf_append(s, '\n');
-                else if (chr(s, 't'))  buf_append(s, '\t');
-                else if (chr(s, 'r'))  buf_append(s, '\r');
-                else if (chr(s, 'b'))  buf_append(s, '\b');
-                else if (chr(s, 'f'))  buf_append(s, '\f');
-                else if (chr(s, 'v'))  buf_append(s, '\v');
-                else if (chr(s, 'e'))  buf_append(s, '\033');
-                else if (oct(s, &o))   buf_append(s, (char) o);
+                size_t start_pos = s->pos;
+                if      (scanner_chr(s, '\\')) scanner_buf_append(s, '\\');
+                else if (scanner_chr(s, 'n'))  scanner_buf_append(s, '\n');
+                else if (scanner_chr(s, 't'))  scanner_buf_append(s, '\t');
+                else if (scanner_chr(s, 'r'))  scanner_buf_append(s, '\r');
+                else if (scanner_chr(s, 'b'))  scanner_buf_append(s, '\b');
+                else if (scanner_chr(s, 'f'))  scanner_buf_append(s, '\f');
+                else if (scanner_chr(s, 'v'))  scanner_buf_append(s, '\v');
+                else if (scanner_chr(s, 'e'))  scanner_buf_append(s, '\033');
+                else if (scanner_oct(s, &o) && is_valid_char((char) o))
+                    scanner_buf_append(s, (char) o);
+                else if (s->pos > start_pos)
+                    scanner_warn_with_code(s,
+                        XKB_WARNING_INVALID_ESCAPE_SEQUENCE,
+                        "invalid octal escape sequence (%.*s) in string literal",
+                        (int) (s->pos - start_pos + 1), &s->s[start_pos - 1]);
+                    /* Ignore. */
                 else {
-                    scanner_warn(s, "unknown escape sequence in string literal");
+                    scanner_warn_with_code(s,
+                        XKB_WARNING_UNKNOWN_CHAR_ESCAPE_SEQUENCE,
+                        "unknown escape sequence (\\%c) in string literal",
+                        scanner_peek(s));
                     /* Ignore. */
                 }
             } else {
-                buf_append(s, next(s));
+                scanner_buf_append(s, scanner_next(s));
             }
         }
-        if (!buf_append(s, '\0') || !chr(s, '\"'))
-            return scanner_error(s, "unterminated string literal");
+        if (!scanner_buf_append(s, '\0') || !scanner_chr(s, '\"')) {
+            scanner_err(s, "unterminated string literal");
+            return ERROR_TOK;
+        }
         yylval->str = strdup(s->buf);
         if (!yylval->str)
-            return scanner_error(s, "scanner out of memory");
+            return ERROR_TOK;
         return STRING;
     }
 
     /* Key name literal. */
-    if (chr(s, '<')) {
-        while (isgraph(peek(s)) && peek(s) != '>')
-            buf_append(s, next(s));
-        if (!buf_append(s, '\0') || !chr(s, '>'))
-            return scanner_error(s, "unterminated key name literal");
+    if (scanner_chr(s, '<')) {
+        while (is_graph(scanner_peek(s)) && scanner_peek(s) != '>')
+            scanner_buf_append(s, scanner_next(s));
+        if (!scanner_buf_append(s, '\0') || !scanner_chr(s, '>')) {
+            scanner_err(s, "unterminated key name literal");
+            return ERROR_TOK;
+        }
         /* Empty key name literals are allowed. */
-        yylval->sval = xkb_atom_intern(s->ctx, s->buf, s->buf_pos - 1);
+        yylval->atom = xkb_atom_intern(s->ctx, s->buf, s->buf_pos - 1);
         return KEYNAME;
     }
 
     /* Operators and punctuation. */
-    if (chr(s, ';')) return SEMI;
-    if (chr(s, '{')) return OBRACE;
-    if (chr(s, '}')) return CBRACE;
-    if (chr(s, '=')) return EQUALS;
-    if (chr(s, '[')) return OBRACKET;
-    if (chr(s, ']')) return CBRACKET;
-    if (chr(s, '(')) return OPAREN;
-    if (chr(s, ')')) return CPAREN;
-    if (chr(s, '.')) return DOT;
-    if (chr(s, ',')) return COMMA;
-    if (chr(s, '+')) return PLUS;
-    if (chr(s, '-')) return MINUS;
-    if (chr(s, '*')) return TIMES;
-    if (chr(s, '!')) return EXCLAM;
-    if (chr(s, '~')) return INVERT;
+    if (scanner_chr(s, ';')) return SEMI;
+    if (scanner_chr(s, '{')) return OBRACE;
+    if (scanner_chr(s, '}')) return CBRACE;
+    if (scanner_chr(s, '=')) return EQUALS;
+    if (scanner_chr(s, '[')) return OBRACKET;
+    if (scanner_chr(s, ']')) return CBRACKET;
+    if (scanner_chr(s, '(')) return OPAREN;
+    if (scanner_chr(s, ')')) return CPAREN;
+    if (scanner_chr(s, '.')) return DOT;
+    if (scanner_chr(s, ',')) return COMMA;
+    if (scanner_chr(s, '+')) return PLUS;
+    if (scanner_chr(s, '-')) return MINUS;
+    if (scanner_chr(s, '*')) return TIMES;
+    if (scanner_chr(s, '/')) return DIVIDE;
+    if (scanner_chr(s, '!')) return EXCLAM;
+    if (scanner_chr(s, '~')) return INVERT;
 
     /* Identifier. */
-    if (isalpha(peek(s)) || peek(s) == '_') {
+    if (is_alpha(scanner_peek(s)) || scanner_peek(s) == '_') {
         s->buf_pos = 0;
-        while (isalnum(peek(s)) || peek(s) == '_')
-            buf_append(s, next(s));
-        if (!buf_append(s, '\0'))
-            return scanner_error(s, "identifier too long");
+        while (is_alnum(scanner_peek(s)) || scanner_peek(s) == '_')
+            scanner_buf_append(s, scanner_next(s));
+        if (!scanner_buf_append(s, '\0')) {
+            scanner_err(s, "identifier too long");
+            return ERROR_TOK;
+        }
 
         /* Keyword. */
-        tok = keyword_to_token(s->buf);
-        if ((int) tok != -1) return tok;
+        tok = keyword_to_token(s->buf, s->buf_pos - 1);
+        if (tok != -1) return tok;
 
         yylval->str = strdup(s->buf);
         if (!yylval->str)
-            return scanner_error(s, "scanner out of memory");
+            return ERROR_TOK;
         return IDENT;
     }
 
     /* Number literal (hexadecimal / decimal / float). */
     if (number(s, &yylval->num, &tok)) {
-        if (tok == ERROR_TOK)
-            return scanner_error(s, "malformed number literal");
+        if (tok == ERROR_TOK) {
+            scanner_err_with_code(s, XKB_ERROR_MALFORMED_NUMBER_LITERAL,
+                                  "malformed number literal");
+            return ERROR_TOK;
+        }
         return tok;
     }
 
-    return scanner_error(s, "unrecognized token");
+    scanner_err(s, "unrecognized token");
+    return ERROR_TOK;
 }
 
 XkbFile *
@@ -192,7 +198,7 @@ XkbParseString(struct xkb_context *ctx, const char *string, size_t len,
                const char *file_name, const char *map)
 {
     struct scanner scanner;
-    scanner_init(&scanner, ctx, string, len, file_name);
+    scanner_init(&scanner, ctx, string, len, file_name, NULL);
     return parse(ctx, &scanner, map);
 }
 
@@ -202,12 +208,13 @@ XkbParseFile(struct xkb_context *ctx, FILE *file,
 {
     bool ok;
     XkbFile *xkb_file;
-    const char *string;
+    char *string;
     size_t size;
 
     ok = map_file(file, &string, &size);
     if (!ok) {
-        log_err(ctx, "Couldn't read XKB file %s: %s\n",
+        log_err(ctx, XKB_LOG_MESSAGE_NO_ID,
+                "Couldn't read XKB file %s: %s\n",
                 file_name, strerror(errno));
         return NULL;
     }