Print this page
new smatch

@@ -47,10 +47,11 @@
 #include "scope.h"
 
 static struct ident_list *macros;       // only needed for -dD
 static int false_nesting = 0;
 static int counter_macro = 0;           // __COUNTER__ expansion
+static int include_level = 0;
 
 #define INCLUDEPATHS 300
 const char *includepath[INCLUDEPATHS+1] = {
         "",
         "/usr/include",

@@ -145,53 +146,100 @@
 
         sparse_error(token->pos, "expected preprocessor identifier");
         return 0;
 }
 
-static void replace_with_defined(struct token *token)
+static void replace_with_bool(struct token *token, bool val)
 {
         static const char *string[] = { "0", "1" };
-        int defined = token_defined(token);
 
         token_type(token) = TOKEN_NUMBER;
-        token->number = string[defined];
+        token->number = string[val];
 }
 
-static int expand_one_symbol(struct token **list)
+static void replace_with_defined(struct token *token)
 {
-        struct token *token = *list;
-        struct symbol *sym;
-        static char buffer[12]; /* __DATE__: 3 + ' ' + 2 + ' ' + 4 + '\0' */
-        static time_t t = 0;
+        replace_with_bool(token, token_defined(token));
+}
 
-        if (token->pos.noexpand)
-                return 1;
+static void replace_with_has_builtin(struct token *token)
+{
+        struct symbol *sym = lookup_symbol(token->ident, NS_SYMBOL);
+        replace_with_bool(token, sym && sym->builtin);
+}
 
-        sym = lookup_macro(token->ident);
-        if (sym) {
-                store_macro_pos(token);
-                sym->used_in = file_scope;
-                return expand(list, sym);
-        }
-        if (token->ident == &__LINE___ident) {
+static void replace_with_has_attribute(struct token *token)
+{
+        struct symbol *sym = lookup_symbol(token->ident, NS_KEYWORD);
+        replace_with_bool(token, sym && sym->op && sym->op->attribute);
+}
+
+static void expand_line(struct token *token)
+{
                 replace_with_integer(token, token->pos.line);
-        } else if (token->ident == &__FILE___ident) {
+}
+
+static void expand_file(struct token *token)
+{
                 replace_with_string(token, stream_name(token->pos.stream));
-        } else if (token->ident == &__DATE___ident) {
+}
+
+static void expand_basefile(struct token *token)
+{
+        replace_with_string(token, base_filename);
+}
+
+static time_t t = 0;
+static void expand_date(struct token *token)
+{
+        static char buffer[12]; /* __DATE__: 3 + ' ' + 2 + ' ' + 4 + '\0' */
+
                 if (!t)
                         time(&t);
                 strftime(buffer, 12, "%b %e %Y", localtime(&t));
                 replace_with_string(token, buffer);
-        } else if (token->ident == &__TIME___ident) {
+}
+
+static void expand_time(struct token *token)
+{
+        static char buffer[9]; /* __TIME__: 2 + ':' + 2 + ':' + 2 + '\0' */
+
                 if (!t)
                         time(&t);
                 strftime(buffer, 9, "%T", localtime(&t));
                 replace_with_string(token, buffer);
-        } else if (token->ident == &__COUNTER___ident) {
+}
+
+static void expand_counter(struct token *token)
+{
                 replace_with_integer(token, counter_macro++);
-        }
+}
+
+static void expand_include_level(struct token *token)
+{
+        replace_with_integer(token, include_level - 1);
+}
+
+static int expand_one_symbol(struct token **list)
+{
+        struct token *token = *list;
+        struct symbol *sym;
+
+        if (token->pos.noexpand)
         return 1;
+
+        sym = lookup_macro(token->ident);
+        if (!sym)
+                return 1;
+        store_macro_pos(token);
+        if (sym->expander) {
+                sym->expander(token);
+                return 1;
+        } else {
+                sym->used_in = file_scope;
+                return expand(list, sym);
+        }
 }
 
 static inline struct token *scan_next(struct token **where)
 {
         struct token *token = *where;

@@ -512,17 +560,14 @@
         case TOKEN_IDENT:
                 left->ident = built_in_ident(buffer);
                 left->pos.noexpand = 0;
                 return 1;
 
-        case TOKEN_NUMBER: {
-                char *number = __alloc_bytes(strlen(buffer) + 1);
-                memcpy(number, buffer, strlen(buffer) + 1);
+        case TOKEN_NUMBER:
                 token_type(left) = TOKEN_NUMBER;        /* could be . + num */
-                left->number = number;
+                left->number = xstrdup(buffer);
                 return 1;
-        }
 
         case TOKEN_SPECIAL:
                 if (buffer[2] && buffer[3])
                         break;
                 for (n = SPECIAL_BASE; n < SPECIAL_ARG_SEPARATOR; n++) {

@@ -849,10 +894,14 @@
                 stream->path = path;
         }
         includepath[0] = path;
 }
 
+#ifndef PATH_MAX
+#define PATH_MAX 4096   // for Hurd where it's not defined
+#endif
+
 static int try_include(const char *path, const char *filename, int flen, struct token **where, const char **next_path)
 {
         int fd;
         int plen = strlen(path);
         static char fullname[PATH_MAX];

@@ -865,12 +914,11 @@
         memcpy(fullname+plen, filename, flen);
         if (already_tokenized(fullname))
                 return 1;
         fd = open(fullname, O_RDONLY);
         if (fd >= 0) {
-                char * streamname = __alloc_bytes(plen + flen);
-                memcpy(streamname, fullname, plen + flen);
+                char *streamname = xmemdup(fullname, plen + flen);
                 *where = tokenize(streamname, fd, *where, next_path);
                 close(fd);
                 return 1;
         }
         return 0;

@@ -910,17 +958,18 @@
         dp = opendir(".");
         if (!dp)
                 return NULL;
 
         if (!getcwd(cwd, sizeof(cwd)))
-                return NULL;
+                goto close;
 
         while ((entry = readdir(dp))) {
                 lstat(entry->d_name, &statbuf);
 
                 if (strcmp(entry->d_name, look_for) == 0) {
                         snprintf(buf, sizeof(buf), "%s/%s", cwd, entry->d_name);
+                        closedir(dp);
                         return buf;
                 }
 
                 if (S_ISDIR(statbuf.st_mode)) {
                         /* Found a directory, but ignore . and .. */

@@ -930,14 +979,17 @@
                                 continue;
 
                         chdir(entry->d_name);
                         ret = find_include("", look_for);
                         chdir("..");
-                        if (ret)
+                        if (ret) {
+                                closedir(dp);
                                 return ret;
                 }
         }
+        }
+close:
         closedir(dp);
 
         return NULL;
 }
 

@@ -980,12 +1032,17 @@
 {
         char cwd[PATH_MAX];
         char dir_part[PATH_MAX];
         const char *file_part;
         const char *include_name;
+        static int cnt;
         int len;
 
+        /* Avoid guessing includes recursively. */
+        if (cnt++ > 1000)
+                return;
+
         if (!filename || filename[0] == '\0')
                 return;
 
         file_part = filename;
         while ((filename = strchr(filename, '/'))) {

@@ -1418,44 +1475,20 @@
 Earg:
         sparse_error(token->pos, "too many instances of argument in body");
         return NULL;
 }
 
-static int do_handle_define(struct stream *stream, struct token **line, struct token *token, int attr)
+static int do_define(struct position pos, struct token *token, struct ident *name,
+                     struct token *arglist, struct token *expansion, int attr)
 {
-        struct token *arglist, *expansion;
-        struct token *left = token->next;
         struct symbol *sym;
-        struct ident *name;
-        int ret;
+        int ret = 1;
 
-        if (token_type(left) != TOKEN_IDENT) {
-                sparse_error(token->pos, "expected identifier to 'define'");
-                return 1;
-        }
-
-        name = left->ident;
-
-        arglist = NULL;
-        expansion = left->next;
-        if (!expansion->pos.whitespace) {
-                if (match_op(expansion, '(')) {
-                        arglist = expansion;
-                        expansion = parse_arguments(expansion);
-                        if (!expansion)
-                                return 1;
-                } else if (!eof_token(expansion)) {
-                        warning(expansion->pos,
-                                "no whitespace before object-like macro body");
-                }
-        }
-
         expansion = parse_expansion(expansion, arglist, name);
         if (!expansion)
                 return 1;
 
-        ret = 1;
         sym = lookup_symbol(name, NS_MACRO | NS_UNDEF);
         if (sym) {
                 int clean;
 
                 if (attr < sym->attr)

@@ -1466,38 +1499,107 @@
                 if (token_list_different(sym->expansion, expansion) ||
                     token_list_different(sym->arglist, arglist)) {
                         ret = 0;
                         if ((clean && attr == SYM_ATTR_NORMAL)
                                         || sym->used_in == file_scope) {
-                                warning(left->pos, "preprocessor token %.*s redefined",
+                                warning(pos, "preprocessor token %.*s redefined",
                                                 name->len, name->name);
                                 info(sym->pos, "this was the original definition");
                         }
                 } else if (clean)
                         goto out;
         }
 
         if (!sym || sym->scope != file_scope) {
-                sym = alloc_symbol(left->pos, SYM_NODE);
+                sym = alloc_symbol(pos, SYM_NODE);
                 bind_symbol(sym, name, NS_MACRO);
                 add_ident(&macros, name);
                 ret = 0;
         }
 
         if (!ret) {
                 sym->expansion = expansion;
                 sym->arglist = arglist;
-                __free_token(token);    /* Free the "define" token, but not the rest of the line */
+                if (token) /* Free the "define" token, but not the rest of the line */
+                        __free_token(token);
         }
 
         sym->namespace = NS_MACRO;
         sym->used_in = NULL;
         sym->attr = attr;
 out:
         return ret;
 }
 
+///
+// predefine a macro with a printf-formatted value
+// @name: the name of the macro
+// @weak: 0/1 for a normal or a weak define
+// @fmt: the printf format followed by it's arguments.
+//
+// The type of the value is automatically infered:
+// TOKEN_NUMBER if it starts by a digit, TOKEN_IDENT otherwise.
+// If @fmt is null or empty, the macro is defined with an empty definition.
+void predefine(const char *name, int weak, const char *fmt, ...)
+{
+        struct ident *ident = built_in_ident(name);
+        struct token *value = &eof_token_entry;
+        int attr = weak ? SYM_ATTR_WEAK : SYM_ATTR_NORMAL;
+
+        if (fmt && fmt[0]) {
+                static char buf[256];
+                va_list ap;
+
+                va_start(ap, fmt);
+                vsnprintf(buf, sizeof(buf), fmt, ap);
+                va_end(ap);
+
+                value = __alloc_token(0);
+                if (isdigit(buf[0])) {
+                        token_type(value) = TOKEN_NUMBER;
+                        value->number = xstrdup(buf);
+                } else {
+                        token_type(value) = TOKEN_IDENT;
+                        value->ident = built_in_ident(buf);
+                }
+                value->pos.whitespace = 1;
+                value->next = &eof_token_entry;
+        }
+
+        do_define(value->pos, NULL, ident, NULL, value, attr);
+}
+
+static int do_handle_define(struct stream *stream, struct token **line, struct token *token, int attr)
+{
+        struct token *arglist, *expansion;
+        struct token *left = token->next;
+        struct ident *name;
+
+        if (token_type(left) != TOKEN_IDENT) {
+                sparse_error(token->pos, "expected identifier to 'define'");
+                return 1;
+        }
+
+        name = left->ident;
+
+        arglist = NULL;
+        expansion = left->next;
+        if (!expansion->pos.whitespace) {
+                if (match_op(expansion, '(')) {
+                        arglist = expansion;
+                        expansion = parse_arguments(expansion);
+                        if (!expansion)
+                                return 1;
+                } else if (!eof_token(expansion)) {
+                        warning(expansion->pos,
+                                "no whitespace before object-like macro body");
+                }
+        }
+
+        return do_define(left->pos, token, name, arglist, expansion, attr);
+}
+
 static int handle_define(struct stream *stream, struct token **line, struct token *token)
 {
         return do_handle_define(stream, line, token, SYM_ATTR_NORMAL);
 }
 

@@ -1550,17 +1652,17 @@
 static int handle_strong_undef(struct stream *stream, struct token **line, struct token *token)
 {
         return do_handle_undef(stream, line, token, SYM_ATTR_STRONG);
 }
 
-static int preprocessor_if(struct stream *stream, struct token *token, int true)
+static int preprocessor_if(struct stream *stream, struct token *token, int cond)
 {
         token_type(token) = false_nesting ? TOKEN_SKIP_GROUPS : TOKEN_IF;
         free_preprocessor_line(token->next);
         token->next = stream->top_if;
         stream->top_if = token;
-        if (false_nesting || true != 1)
+        if (false_nesting || cond != 1)
                 false_nesting++;
         return 0;
 }
 
 static int handle_ifdef(struct stream *stream, struct token **line, struct token *token)

@@ -1624,10 +1726,18 @@
                                 break;
                         if (p->ident == &defined_ident) {
                                 state = 1;
                                 beginning = list;
                                 break;
+                        } else if (p->ident == &__has_builtin_ident) {
+                                state = 4;
+                                beginning = list;
+                                break;
+                        } else if (p->ident == &__has_attribute_ident) {
+                                state = 6;
+                                beginning = list;
+                                break;
                         }
                         if (!expand_one_symbol(list))
                                 continue;
                         if (token_type(p) != TOKEN_IDENT)
                                 break;

@@ -1654,11 +1764,43 @@
                         state = 0;
                         if (!match_op(p, ')'))
                                 sparse_error(p->pos, "missing ')' after \"defined\"");
                         *list = p->next;
                         continue;
+
+                // __has_builtin(x) or __has_attribute(x)
+                case 4: case 6:
+                        if (match_op(p, '(')) {
+                                state++;
+                        } else {
+                                sparse_error(p->pos, "missing '(' after \"__has_%s\"",
+                                        state == 4 ? "builtin" : "attribute");
+                                state = 0;
                 }
+                        *beginning = p;
+                        break;
+                case 5: case 7:
+                        if (token_type(p) != TOKEN_IDENT) {
+                                sparse_error(p->pos, "identifier expected");
+                                state = 0;
+                                break;
+                        }
+                        if (!match_op(p->next, ')'))
+                                sparse_error(p->pos, "missing ')' after \"__has_%s\"",
+                                        state == 5 ? "builtin" : "attribute");
+                        if (state == 5)
+                                replace_with_has_builtin(p);
+                        else
+                                replace_with_has_attribute(p);
+                        state = 8;
+                        *beginning = p;
+                        break;
+                case 8:
+                        state = 0;
+                        *list = p->next;
+                        continue;
+                }
                 list = &p->next;
         }
 
         p = constant_expression(*where, &expr);
         if (!eof_token(p))

@@ -1967,13 +2109,10 @@
 static int handle_line(struct stream *stream, struct token **line, struct token *token)
 {
         return 1;
 }
 
-/*
- * Ignore "#ident".
- */
 static int handle_ident(struct stream *stream, struct token **line, struct token *token)
 {
         return 1;
 }
 

@@ -2019,10 +2158,22 @@
                 { "else",       handle_else },
                 { "endif",      handle_endif },
                 { "if",         handle_if },
                 { "elif",       handle_elif },
         };
+        static struct {
+                const char *name;
+                void (*expander)(struct token *);
+        } dynamic[] = {
+                { "__LINE__",           expand_line },
+                { "__FILE__",           expand_file },
+                { "__BASE_FILE__",      expand_basefile },
+                { "__DATE__",           expand_date },
+                { "__TIME__",           expand_time },
+                { "__COUNTER__",        expand_counter },
+                { "__INCLUDE_LEVEL__",  expand_include_level },
+        };
 
         for (i = 0; i < ARRAY_SIZE(normal); i++) {
                 struct symbol *sym;
                 sym = create_symbol(stream, normal[i].name, SYM_PREPROCESSOR, NS_PREPROCESSOR);
                 sym->handler = normal[i].handler;

@@ -2032,10 +2183,15 @@
                 struct symbol *sym;
                 sym = create_symbol(stream, special[i].name, SYM_PREPROCESSOR, NS_PREPROCESSOR);
                 sym->handler = special[i].handler;
                 sym->normal = 0;
         }
+        for (i = 0; i < ARRAY_SIZE(dynamic); i++) {
+                struct symbol *sym;
+                sym = create_symbol(stream, dynamic[i].name, SYM_NODE, NS_MACRO);
+                sym->expander = dynamic[i].expander;
+        }
 
         counter_macro = 0;
 }
 
 static void handle_preprocessor_line(struct stream *stream, struct token **line, struct token *start)

@@ -2113,13 +2269,15 @@
                                 false_nesting = 0;
                         }
                         if (!stream->dirty)
                                 stream->constant = CONSTANT_FILE_YES;
                         *list = next->next;
+                        include_level--;
                         continue;
                 case TOKEN_STREAMBEGIN:
                         *list = next->next;
+                        include_level++;
                         continue;
 
                 default:
                         dirty_stream(stream);
                         if (false_nesting) {

@@ -2177,10 +2335,16 @@
         preprocessing = 0;
 
         return token;
 }
 
+static int is_VA_ARGS_token(struct token *token)
+{
+        return (token_type(token) == TOKEN_IDENT) &&
+                (token->ident == &__VA_ARGS___ident);
+}
+
 static void dump_macro(struct symbol *sym)
 {
         int nargs = sym->arglist ? sym->arglist->count.normal : 0;
         struct token *args[nargs];
         struct token *token;

@@ -2192,31 +2356,38 @@
                 int narg = 0;
                 putchar('(');
                 for (; !eof_token(token); token = token->next) {
                         if (token_type(token) == TOKEN_ARG_COUNT)
                                 continue;
+                        if (is_VA_ARGS_token(token))
+                                printf("%s...", sep);
+                        else
                         printf("%s%s", sep, show_token(token));
                         args[narg++] = token;
-                        sep = ", ";
+                        sep = ",";
                 }
                 putchar(')');
         }
-        putchar(' ');
 
         token = sym->expansion;
-        while (!eof_token(token)) {
+        while (token_type(token) != TOKEN_UNTAINT) {
                 struct token *next = token->next;
+                if (token->pos.whitespace)
+                        putchar(' ');
                 switch (token_type(token)) {
-                case TOKEN_UNTAINT:
+                case TOKEN_CONCAT:
+                        printf("##");
                         break;
+                case TOKEN_STR_ARGUMENT:
+                        printf("#");
+                        /* fall-through */
+                case TOKEN_QUOTED_ARGUMENT:
                 case TOKEN_MACRO_ARGUMENT:
                         token = args[token->argnum];
                         /* fall-through */
                 default:
                         printf("%s", show_token(token));
-                        if (next->pos.whitespace)
-                                putchar(' ');
                 }
                 token = next;
         }
         putchar('\n');
 }