/* * Copyright (C) 2006 Dan Carpenter. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt */ /* * Miscellaneous helper functions. */ #include #include #include "allocate.h" #include "smatch.h" #include "smatch_extra.h" #include "smatch_slist.h" #define VAR_LEN 512 char *alloc_string(const char *str) { char *tmp; if (!str) return NULL; tmp = malloc(strlen(str) + 1); strcpy(tmp, str); return tmp; } char *alloc_string_newline(const char *str) { char *tmp; int len; if (!str) return NULL; len = strlen(str); tmp = malloc(len + 2); snprintf(tmp, len + 2, "%s\n", str); return tmp; } void free_string(char *str) { free(str); } void remove_parens(char *str) { char *src, *dst; dst = src = str; while (*src != '\0') { if (*src == '(' || *src == ')') { src++; continue; } *dst++ = *src++; } *dst = *src; } struct smatch_state *alloc_state_num(int num) { struct smatch_state *state; static char buff[256]; state = __alloc_smatch_state(0); snprintf(buff, 255, "%d", num); buff[255] = '\0'; state->name = alloc_string(buff); state->data = INT_PTR(num); return state; } struct smatch_state *alloc_state_str(const char *name) { struct smatch_state *state; state = __alloc_smatch_state(0); state->name = alloc_string(name); return state; } struct smatch_state *merge_str_state(struct smatch_state *s1, struct smatch_state *s2) { if (!s1->name || !s2->name) return &merged; if (strcmp(s1->name, s2->name) == 0) return s1; return &merged; } struct smatch_state *alloc_state_expr(struct expression *expr) { struct smatch_state *state; char *name; expr = strip_expr(expr); name = expr_to_str(expr); if (!name) return NULL; state = __alloc_smatch_state(0); state->name = alloc_sname(name); free_string(name); state->data = expr; return state; } void append(char *dest, const char *data, int buff_len) { strncat(dest, data, buff_len - strlen(dest) - 1); } /* * If you have "foo(a, b, 1);" then use * get_argument_from_call_expr(expr, 0) to return the expression for * a. Yes, it does start counting from 0. */ struct expression *get_argument_from_call_expr(struct expression_list *args, int num) { struct expression *expr; int i = 0; if (!args) return NULL; FOR_EACH_PTR(args, expr) { if (i == num) return expr; i++; } END_FOR_EACH_PTR(expr); return NULL; } struct expression *get_array_expr(struct expression *expr) { struct expression *parent; struct symbol *type; if (expr->type != EXPR_BINOP || expr->op != '+') return NULL; type = get_type(expr->left); if (!type) return NULL; if (type->type == SYM_ARRAY) return expr->left; if (type->type != SYM_PTR) return NULL; parent = expr_get_parent_expr(expr); if (!parent) /* Sometimes we haven't set up the ->parent yet. FIXME!! */ return expr->left; if (parent->type == EXPR_PREOP && parent->op == '*') return expr->left; return NULL; } static void __get_variable_from_expr(struct symbol **sym_ptr, char *buf, struct expression *expr, int len, int *complicated) { if (!expr) { /* can't happen on valid code */ *complicated = 1; return; } switch (expr->type) { case EXPR_DEREF: { struct expression *deref; int op; deref = expr->deref; op = deref->op; if (deref->type == EXPR_PREOP && op == '*') { struct expression *unop = strip_expr(deref->unop); if (unop->type == EXPR_PREOP && unop->op == '&') { deref = unop->unop; op = '.'; } else { if (!is_pointer(deref) && !is_pointer(deref->unop)) op = '.'; deref = deref->unop; } } __get_variable_from_expr(sym_ptr, buf, deref, len, complicated); if (op == '*') append(buf, "->", len); else append(buf, ".", len); if (expr->member) append(buf, expr->member->name, len); else append(buf, "unknown_member", len); return; } case EXPR_SYMBOL: if (expr->symbol_name) append(buf, expr->symbol_name->name, len); if (sym_ptr) { if (*sym_ptr) *complicated = 1; *sym_ptr = expr->symbol; } return; case EXPR_PREOP: { const char *tmp; if (get_expression_statement(expr)) { *complicated = 2; return; } if (expr->op == '(') { if (expr->unop->type != EXPR_SYMBOL) append(buf, "(", len); } else if (expr->op != '*' || !get_array_expr(expr->unop)) { tmp = show_special(expr->op); append(buf, tmp, len); } __get_variable_from_expr(sym_ptr, buf, expr->unop, len, complicated); if (expr->op == '(' && expr->unop->type != EXPR_SYMBOL) append(buf, ")", len); if (expr->op == SPECIAL_DECREMENT || expr->op == SPECIAL_INCREMENT) *complicated = 1; return; } case EXPR_POSTOP: { const char *tmp; __get_variable_from_expr(sym_ptr, buf, expr->unop, len, complicated); tmp = show_special(expr->op); append(buf, tmp, len); if (expr->op == SPECIAL_DECREMENT || expr->op == SPECIAL_INCREMENT) *complicated = 1; return; } case EXPR_ASSIGNMENT: case EXPR_COMPARE: case EXPR_LOGICAL: case EXPR_BINOP: { char tmp[10]; struct expression *array_expr; *complicated = 1; array_expr = get_array_expr(expr); if (array_expr) { __get_variable_from_expr(sym_ptr, buf, array_expr, len, complicated); append(buf, "[", len); } else { __get_variable_from_expr(sym_ptr, buf, expr->left, len, complicated); snprintf(tmp, sizeof(tmp), " %s ", show_special(expr->op)); append(buf, tmp, len); } __get_variable_from_expr(NULL, buf, expr->right, len, complicated); if (array_expr) append(buf, "]", len); return; } case EXPR_VALUE: { sval_t sval = {}; char tmp[25]; *complicated = 1; if (!get_value(expr, &sval)) return; snprintf(tmp, 25, "%s", sval_to_numstr(sval)); append(buf, tmp, len); return; } case EXPR_FVALUE: { sval_t sval = {}; char tmp[25]; *complicated = 1; if (!get_value(expr, &sval)) return; snprintf(tmp, 25, "%s", sval_to_numstr(sval)); append(buf, tmp, len); return; } case EXPR_STRING: append(buf, "\"", len); if (expr->string) append(buf, expr->string->data, len); append(buf, "\"", len); return; case EXPR_CALL: { struct expression *tmp; int i; *complicated = 1; __get_variable_from_expr(NULL, buf, expr->fn, len, complicated); append(buf, "(", len); i = 0; FOR_EACH_PTR(expr->args, tmp) { if (i++) append(buf, ", ", len); __get_variable_from_expr(NULL, buf, tmp, len, complicated); } END_FOR_EACH_PTR(tmp); append(buf, ")", len); return; } case EXPR_CAST: case EXPR_FORCE_CAST: __get_variable_from_expr(sym_ptr, buf, expr->cast_expression, len, complicated); return; case EXPR_SIZEOF: { sval_t sval; int size; char tmp[25]; if (expr->cast_type && get_base_type(expr->cast_type)) { size = type_bytes(get_base_type(expr->cast_type)); snprintf(tmp, 25, "%d", size); append(buf, tmp, len); } else if (get_value(expr, &sval)) { snprintf(tmp, 25, "%s", sval_to_str(sval)); append(buf, tmp, len); } return; } case EXPR_IDENTIFIER: *complicated = 1; if (expr->expr_ident) append(buf, expr->expr_ident->name, len); return; case EXPR_SELECT: case EXPR_CONDITIONAL: *complicated = 1; append(buf, "(", len); __get_variable_from_expr(NULL, buf, expr->conditional, len, complicated); append(buf, ") ?", len); if (expr->cond_true) __get_variable_from_expr(NULL, buf, expr->cond_true, len, complicated); append(buf, ":", len); __get_variable_from_expr(NULL, buf, expr->cond_false, len, complicated); return; default: { char tmp[64]; snprintf(tmp, sizeof(tmp), "$expr_%p(%d)", expr, expr->type); append(buf, tmp, len); *complicated = 1; } return; } } struct expr_str_cache_results { struct expression *expr; char str[VAR_LEN]; struct symbol *sym; int complicated; }; static void get_variable_from_expr(struct symbol **sym_ptr, char *buf, struct expression *expr, int len, int *complicated) { static struct expr_str_cache_results cached[8]; struct symbol *tmp_sym = NULL; static int idx; int i; for (i = 0; i < ARRAY_SIZE(cached); i++) { if (expr == cached[i].expr) { strncpy(buf, cached[i].str, len); if (sym_ptr) *sym_ptr = cached[i].sym; *complicated = cached[i].complicated; return; } } __get_variable_from_expr(&tmp_sym, buf, expr, len, complicated); if (sym_ptr) *sym_ptr = tmp_sym; if (expr->smatch_flags & Tmp) return; cached[idx].expr = expr; strncpy(cached[idx].str, buf, VAR_LEN); cached[idx].sym = tmp_sym; cached[idx].complicated = *complicated; idx = (idx + 1) % ARRAY_SIZE(cached); } /* * This is returns a stylized "c looking" representation of the * variable name. * * It uses the same buffer every time so you have to save the result * yourself if you want to keep it. * */ char *expr_to_str_sym(struct expression *expr, struct symbol **sym_ptr) { static char var_name[VAR_LEN]; int complicated = 0; if (sym_ptr) *sym_ptr = NULL; var_name[0] = '\0'; if (!expr) return NULL; get_variable_from_expr(sym_ptr, var_name, expr, sizeof(var_name), &complicated); if (complicated < 2) return alloc_string(var_name); else return NULL; } char *expr_to_str(struct expression *expr) { return expr_to_str_sym(expr, NULL); } /* * get_variable_from_expr_simple() only returns simple variables. * If it's a complicated variable like a->foo[x] instead of just 'a->foo' * then it returns NULL. */ char *expr_to_var_sym(struct expression *expr, struct symbol **sym_ptr) { static char var_name[VAR_LEN]; int complicated = 0; if (sym_ptr) *sym_ptr = NULL; var_name[0] = '\0'; if (!expr) return NULL; expr = strip_expr(expr); get_variable_from_expr(sym_ptr, var_name, expr, sizeof(var_name), &complicated); if (complicated) { if (sym_ptr) *sym_ptr = NULL; return NULL; } return alloc_string(var_name); } char *expr_to_var(struct expression *expr) { return expr_to_var_sym(expr, NULL); } struct symbol *expr_to_sym(struct expression *expr) { struct symbol *sym; char *name; name = expr_to_var_sym(expr, &sym); free_string(name); return sym; } int get_complication_score(struct expression *expr) { expr = strip_expr(expr); /* * Don't forget to keep get_complication_score() and store_all_links() * in sync. * */ if (!expr) return 990; switch (expr->type) { case EXPR_CALL: return 991; case EXPR_COMPARE: case EXPR_BINOP: return get_complication_score(expr->left) + get_complication_score(expr->right); case EXPR_SYMBOL: return 1; case EXPR_PREOP: if (expr->op == '*' || expr->op == '(') return get_complication_score(expr->unop); return 993; case EXPR_DEREF: return get_complication_score(expr->deref); case EXPR_VALUE: case EXPR_SIZEOF: return 0; default: return 994; } } struct expression *reorder_expr_alphabetically(struct expression *expr) { struct expression *ret; char *left, *right; if (expr->type != EXPR_BINOP) return expr; if (expr->op != '+' && expr->op != '*') return expr; left = expr_to_var(expr->left); right = expr_to_var(expr->right); ret = expr; if (!left || !right) goto free; if (strcmp(left, right) <= 0) goto free; ret = binop_expression(expr->right, expr->op, expr->left); free: free_string(left); free_string(right); return ret; } char *expr_to_chunk_helper(struct expression *expr, struct symbol **sym, struct var_sym_list **vsl) { struct var_sym_list *tmp_vsl; char *name; struct symbol *tmp; int score; if (vsl) *vsl = NULL; if (sym) *sym = NULL; expr = strip_parens(expr); if (!expr) return NULL; name = expr_to_var_sym(expr, &tmp); if (name && tmp) { if (sym) *sym = tmp; if (vsl) add_var_sym(vsl, name, tmp); return name; } free_string(name); score = get_complication_score(expr); if (score <= 0 || score > 2) return NULL; tmp_vsl = expr_to_vsl(expr); if (vsl) { *vsl = tmp_vsl; if (!*vsl) return NULL; } if (sym) { if (ptr_list_size((struct ptr_list *)tmp_vsl) == 1) { struct var_sym *vs; vs = first_ptr_list((struct ptr_list *)tmp_vsl); *sym = vs->sym; } } expr = reorder_expr_alphabetically(expr); return expr_to_str(expr); } char *expr_to_known_chunk_sym(struct expression *expr, struct symbol **sym) { return expr_to_chunk_helper(expr, sym, NULL); } char *expr_to_chunk_sym_vsl(struct expression *expr, struct symbol **sym, struct var_sym_list **vsl) { return expr_to_chunk_helper(expr, sym, vsl); } int sym_name_is(const char *name, struct expression *expr) { if (!expr) return 0; if (expr->type != EXPR_SYMBOL) return 0; if (!strcmp(expr->symbol_name->name, name)) return 1; return 0; } int expr_is_zero(struct expression *expr) { sval_t sval; if (get_value(expr, &sval) && sval.value == 0) return 1; return 0; } int is_array(struct expression *expr) { struct symbol *type; expr = strip_expr(expr); if (!expr) return 0; if (expr->type == EXPR_PREOP && expr->op == '*') { expr = strip_expr(expr->unop); if (!expr) return 0; if (expr->type == EXPR_BINOP && expr->op == '+') return 1; } if (expr->type != EXPR_BINOP || expr->op != '+') return 0; type = get_type(expr->left); if (!type || type->type != SYM_ARRAY) return 0; return 1; } struct expression *get_array_base(struct expression *expr) { if (!is_array(expr)) return NULL; expr = strip_expr(expr); if (expr->type == EXPR_PREOP && expr->op == '*') expr = strip_expr(expr->unop); if (expr->type != EXPR_BINOP || expr->op != '+') return NULL; return strip_parens(expr->left); } struct expression *get_array_offset(struct expression *expr) { if (!is_array(expr)) return NULL; expr = strip_expr(expr); if (expr->type == EXPR_PREOP && expr->op == '*') expr = strip_expr(expr->unop); if (expr->type != EXPR_BINOP || expr->op != '+') return NULL; return strip_parens(expr->right); } const char *show_state(struct smatch_state *state) { if (!state) return NULL; return state->name; } struct statement *get_expression_statement(struct expression *expr) { /* What are those things called? if (({....; ret;})) { ...*/ if (expr->type != EXPR_PREOP) return NULL; if (expr->op != '(') return NULL; if (!expr->unop) return NULL; if (expr->unop->type != EXPR_STATEMENT) return NULL; if (expr->unop->statement->type != STMT_COMPOUND) return NULL; return expr->unop->statement; } struct expression *strip_parens(struct expression *expr) { if (!expr) return NULL; if (expr->type == EXPR_PREOP) { if (!expr->unop) return expr; /* parsing invalid code */ if (expr->op == '(' && expr->unop->type == EXPR_STATEMENT && expr->unop->statement->type == STMT_COMPOUND) return expr; if (expr->op == '(') return strip_parens(expr->unop); } return expr; } static struct expression *strip_expr_helper(struct expression *expr, bool set_parent) { if (!expr) return NULL; switch (expr->type) { case EXPR_FORCE_CAST: case EXPR_CAST: if (set_parent) expr_set_parent_expr(expr->cast_expression, expr); if (!expr->cast_expression) return expr; return strip_expr_helper(expr->cast_expression, set_parent); case EXPR_PREOP: { struct expression *unop; if (!expr->unop) /* parsing invalid code */ return expr; if (set_parent) expr_set_parent_expr(expr->unop, expr); if (expr->op == '(' && expr->unop->type == EXPR_STATEMENT && expr->unop->statement->type == STMT_COMPOUND) return expr; unop = strip_expr_helper(expr->unop, set_parent); if (expr->op == '*' && unop && unop->type == EXPR_PREOP && unop->op == '&') { struct symbol *type = get_type(unop->unop); if (type && type->type == SYM_ARRAY) return expr; return strip_expr_helper(unop->unop, set_parent); } if (expr->op == '(') return unop; return expr; } case EXPR_CONDITIONAL: if (known_condition_true(expr->conditional)) { if (expr->cond_true) { if (set_parent) expr_set_parent_expr(expr->cond_true, expr); return strip_expr_helper(expr->cond_true, set_parent); } if (set_parent) expr_set_parent_expr(expr->conditional, expr); return strip_expr_helper(expr->conditional, set_parent); } if (known_condition_false(expr->conditional)) { if (set_parent) expr_set_parent_expr(expr->cond_false, expr); return strip_expr_helper(expr->cond_false, set_parent); } return expr; case EXPR_CALL: if (sym_name_is("__builtin_expect", expr->fn) || sym_name_is("__builtin_bswap16", expr->fn) || sym_name_is("__builtin_bswap32", expr->fn) || sym_name_is("__builtin_bswap64", expr->fn)) { expr = get_argument_from_call_expr(expr->args, 0); return strip_expr_helper(expr, set_parent); } return expr; } return expr; } struct expression *strip_expr(struct expression *expr) { return strip_expr_helper(expr, false); } struct expression *strip_expr_set_parent(struct expression *expr) { return strip_expr_helper(expr, true); } static void delete_state_tracker(struct tracker *t) { delete_state(t->owner, t->name, t->sym); __free_tracker(t); } void scoped_state(int my_id, const char *name, struct symbol *sym) { struct tracker *t; t = alloc_tracker(my_id, name, sym); add_scope_hook((scope_hook *)&delete_state_tracker, t); } int is_error_return(struct expression *expr) { struct symbol *cur_func = cur_func_sym; struct range_list *rl; sval_t sval; if (!expr) return 0; if (cur_func->type != SYM_NODE) return 0; cur_func = get_base_type(cur_func); if (cur_func->type != SYM_FN) return 0; cur_func = get_base_type(cur_func); if (cur_func == &void_ctype) return 0; if (option_project == PROJ_KERNEL && get_implied_rl(expr, &rl) && rl_type(rl) == &int_ctype && sval_is_negative(rl_min(rl)) && rl_max(rl).value == -1) return 1; if (!get_implied_value(expr, &sval)) return 0; if (sval.value < 0) return 1; if (cur_func->type == SYM_PTR && sval.value == 0) return 1; return 0; } int getting_address(struct expression *expr) { int deref_count = 0; while ((expr = expr_get_parent_expr(expr))) { if (expr->type == EXPR_PREOP && expr->op == '*') { /* &foo->bar->baz dereferences "foo->bar" */ if (deref_count == 0) deref_count++; return false; } if (expr->type == EXPR_PREOP && expr->op == '&') return true; } return false; } int get_struct_and_member(struct expression *expr, const char **type, const char **member) { struct symbol *sym; expr = strip_expr(expr); if (expr->type != EXPR_DEREF) return 0; if (!expr->member) return 0; sym = get_type(expr->deref); if (!sym) return 0; if (sym->type == SYM_UNION) return 0; if (!sym->ident) return 0; *type = sym->ident->name; *member = expr->member->name; return 1; } char *get_member_name(struct expression *expr) { char buf[256]; struct symbol *sym; expr = strip_expr(expr); if (!expr || expr->type != EXPR_DEREF) return NULL; if (!expr->member) return NULL; sym = get_type(expr->deref); if (!sym) return NULL; if (sym->type == SYM_UNION) { snprintf(buf, sizeof(buf), "(union %s)->%s", sym->ident ? sym->ident->name : "anonymous", expr->member->name); return alloc_string(buf); } if (!sym->ident) { struct expression *deref; char *full, *outer; int len; /* * If we're in an anonymous struct then maybe we can find an * outer struct name to use as a name. This code should be * recursive and cleaner. I am not very proud of it. * */ deref = expr->deref; if (deref->type != EXPR_DEREF || !deref->member) return NULL; sym = get_type(deref->deref); if (!sym || sym->type != SYM_STRUCT || !sym->ident) return NULL; full = expr_to_str(expr); if (!full) return NULL; deref = deref->deref; if (deref->type == EXPR_PREOP && deref->op == '*') deref = deref->unop; outer = expr_to_str(deref); if (!outer) { free_string(full); return NULL; } len = strlen(outer); if (strncmp(outer, full, len) != 0) { free_string(full); free_string(outer); return NULL; } if (full[len] == '-' && full[len + 1] == '>') len += 2; if (full[len] == '.') len++; snprintf(buf, sizeof(buf), "(struct %s)->%s", sym->ident->name, full + len); free_string(outer); free_string(full); return alloc_string(buf); } snprintf(buf, sizeof(buf), "(struct %s)->%s", sym->ident->name, expr->member->name); return alloc_string(buf); } int cmp_pos(struct position pos1, struct position pos2) { /* the stream position is ... */ if (pos1.stream > pos2.stream) return -1; if (pos1.stream < pos2.stream) return 1; if (pos1.line < pos2.line) return -1; if (pos1.line > pos2.line) return 1; if (pos1.pos < pos2.pos) return -1; if (pos1.pos > pos2.pos) return 1; return 0; } int positions_eq(struct position pos1, struct position pos2) { if (pos1.line != pos2.line) return 0; if (pos1.pos != pos2.pos) return 0; if (pos1.stream != pos2.stream) return 0; return 1; } struct statement *get_current_statement(void) { struct statement *prev, *tmp; prev = last_ptr_list((struct ptr_list *)big_statement_stack); if (!prev || !get_macro_name(prev->pos)) return prev; FOR_EACH_PTR_REVERSE(big_statement_stack, tmp) { if (positions_eq(tmp->pos, prev->pos)) continue; if (prev->pos.line > tmp->pos.line) return prev; return tmp; } END_FOR_EACH_PTR_REVERSE(tmp); return prev; } struct statement *get_prev_statement(void) { struct statement *tmp; int i; i = 0; FOR_EACH_PTR_REVERSE(big_statement_stack, tmp) { if (i++ == 1) return tmp; } END_FOR_EACH_PTR_REVERSE(tmp); return NULL; } struct expression *get_last_expr_from_expression_stmt(struct expression *expr) { struct statement *stmt; struct statement *last_stmt; while (expr->type == EXPR_PREOP && expr->op == '(') expr = expr->unop; if (expr->type != EXPR_STATEMENT) return NULL; stmt = expr->statement; if (!stmt) return NULL; if (stmt->type == STMT_COMPOUND) { last_stmt = last_ptr_list((struct ptr_list *)stmt->stmts); if (!last_stmt) return NULL; if (last_stmt->type == STMT_LABEL) last_stmt = last_stmt->label_statement; if (last_stmt->type != STMT_EXPRESSION) return NULL; return last_stmt->expression; } if (stmt->type == STMT_EXPRESSION) return stmt->expression; return NULL; } int get_param_num_from_sym(struct symbol *sym) { struct symbol *tmp; int i; if (!sym) return UNKNOWN_SCOPE; if (sym->ctype.modifiers & MOD_TOPLEVEL) { if (sym->ctype.modifiers & MOD_STATIC) return FILE_SCOPE; return GLOBAL_SCOPE; } if (!cur_func_sym) { if (!parse_error) { sm_msg("warn: internal. problem with scope: %s", sym->ident ? sym->ident->name : ""); } return GLOBAL_SCOPE; } i = 0; FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, tmp) { if (tmp == sym) return i; i++; } END_FOR_EACH_PTR(tmp); return LOCAL_SCOPE; } int get_param_num(struct expression *expr) { struct symbol *sym; char *name; if (!cur_func_sym) return UNKNOWN_SCOPE; name = expr_to_var_sym(expr, &sym); free_string(name); if (!sym) return UNKNOWN_SCOPE; return get_param_num_from_sym(sym); } struct symbol *get_param_sym_from_num(int num) { struct symbol *sym; int i; if (!cur_func_sym) return NULL; i = 0; FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, sym) { if (i++ == num) return sym; } END_FOR_EACH_PTR(sym); return NULL; } int ms_since(struct timeval *start) { struct timeval end; double diff; gettimeofday(&end, NULL); diff = (end.tv_sec - start->tv_sec) * 1000.0; diff += (end.tv_usec - start->tv_usec) / 1000.0; return (int)diff; } int parent_is_gone_var_sym(const char *name, struct symbol *sym) { if (!name || !sym) return 0; if (parent_is_null_var_sym(name, sym) || parent_is_free_var_sym(name, sym)) return 1; return 0; } int parent_is_gone(struct expression *expr) { struct symbol *sym; char *var; int ret = 0; expr = strip_expr(expr); var = expr_to_var_sym(expr, &sym); if (!var || !sym) goto free; ret = parent_is_gone_var_sym(var, sym); free: free_string(var); return ret; } int invert_op(int op) { switch (op) { case '*': return '/'; case '/': return '*'; case '+': return '-'; case '-': return '+'; case SPECIAL_LEFTSHIFT: return SPECIAL_RIGHTSHIFT; case SPECIAL_RIGHTSHIFT: return SPECIAL_LEFTSHIFT; } return 0; } int op_remove_assign(int op) { switch (op) { case SPECIAL_ADD_ASSIGN: return '+'; case SPECIAL_SUB_ASSIGN: return '-'; case SPECIAL_MUL_ASSIGN: return '*'; case SPECIAL_DIV_ASSIGN: return '/'; case SPECIAL_MOD_ASSIGN: return '%'; case SPECIAL_AND_ASSIGN: return '&'; case SPECIAL_OR_ASSIGN: return '|'; case SPECIAL_XOR_ASSIGN: return '^'; case SPECIAL_SHL_ASSIGN: return SPECIAL_LEFTSHIFT; case SPECIAL_SHR_ASSIGN: return SPECIAL_RIGHTSHIFT; default: return op; } } int expr_equiv(struct expression *one, struct expression *two) { struct symbol *one_sym = NULL; struct symbol *two_sym = NULL; char *one_name = NULL; char *two_name = NULL; int ret = 0; if (!one || !two) return 0; if (one->type != two->type) return 0; if (is_fake_call(one) || is_fake_call(two)) return 0; one_name = expr_to_str_sym(one, &one_sym); if (!one_name) goto free; two_name = expr_to_str_sym(two, &two_sym); if (!two_name) goto free; if (one_sym != two_sym) goto free; /* * This is a terrible hack because expr_to_str() sometimes gives up in * the middle and just returns what it has. If you see a () you know * the string is bogus. */ if (strstr(one_name, "()")) goto free; if (strcmp(one_name, two_name) == 0) ret = 1; free: free_string(one_name); free_string(two_name); return ret; } void push_int(struct int_stack **stack, int num) { int *munged; /* * Just put the int on directly instead of a pointer to the int. * Shift it to the left because Sparse uses the last two bits. * This is sort of a dirty hack, yes. */ munged = INT_PTR(num << 2); add_ptr_list(stack, munged); } int pop_int(struct int_stack **stack) { int *num; num = last_ptr_list((struct ptr_list *)*stack); delete_ptr_list_last((struct ptr_list **)stack); return PTR_INT(num) >> 2; }