6564909bda388f31ecd5107dced31816d2d550fb
[smatch.git] / smatch_db.c
blob6564909bda388f31ecd5107dced31816d2d550fb
1 /*
2 * smatch/smatch_db.c
4 * Copyright (C) 2010 Dan Carpenter.
6 * Licensed under the Open Software License version 1.1
8 */
10 #include <string.h>
11 #include <errno.h>
12 #include <sqlite3.h>
13 #include "smatch.h"
14 #include "smatch_slist.h"
15 #include "smatch_extra.h"
17 #define sql_insert(table, values...) \
18 do { \
19 if (option_info) { \
20 sm_prefix(); \
21 sm_printf("SQL: insert into " #table " values (" values); \
22 sm_printf(");\n"); \
23 } \
24 } while (0)
26 static sqlite3 *db;
28 struct def_callback {
29 int hook_type;
30 void (*callback)(const char *name, struct symbol *sym, char *key, char *value);
32 ALLOCATOR(def_callback, "definition db hook callbacks");
33 DECLARE_PTR_LIST(callback_list, struct def_callback);
34 static struct callback_list *callbacks;
36 struct member_info_callback {
37 int owner;
38 void (*callback)(char *fn, char *global_static, int param, char *printed_name, struct smatch_state *state);
40 ALLOCATOR(member_info_callback, "caller_info callbacks");
41 DECLARE_PTR_LIST(member_info_cb_list, struct member_info_callback);
42 static struct member_info_cb_list *member_callbacks;
44 struct returned_state_callback {
45 void (*callback)(int return_id, char *return_ranges, struct expression *return_expr, struct state_list *slist);
47 ALLOCATOR(returned_state_callback, "returned state callbacks");
48 DECLARE_PTR_LIST(returned_state_cb_list, struct returned_state_callback);
49 static struct returned_state_cb_list *returned_state_callbacks;
51 struct returned_member_callback {
52 int owner;
53 void (*callback)(int return_id, char *return_ranges, char *printed_name, struct smatch_state *state);
55 ALLOCATOR(returned_member_callback, "returned member callbacks");
56 DECLARE_PTR_LIST(returned_member_cb_list, struct returned_member_callback);
57 static struct returned_member_cb_list *returned_member_callbacks;
59 struct call_implies_callback {
60 int type;
61 void (*callback)(struct expression *arg, char *value);
63 ALLOCATOR(call_implies_callback, "call_implies callbacks");
64 DECLARE_PTR_LIST(call_implies_cb_list, struct call_implies_callback);
65 static struct call_implies_cb_list *call_implies_cb_list;
67 void sql_exec(int (*callback)(void*, int, char**, char**), const char *sql)
69 char *err = NULL;
70 int rc;
72 if (option_no_db || !db)
73 return;
75 rc = sqlite3_exec(db, sql, callback, 0, &err);
76 if (rc != SQLITE_OK) {
77 fprintf(stderr, "SQL error #2: %s\n", err);
78 fprintf(stderr, "SQL: '%s'\n", sql);
82 void sql_insert_return_states(int return_id, const char *return_ranges,
83 int type, int param, const char *key, const char *value)
85 sql_insert(return_states, "'%s', '%s', %d, '%s', %d, %d, %d, '%s', '%s'",
86 get_filename(), get_function(), return_id, return_ranges,
87 fn_static(), type, param, key, value);
90 void sql_insert_function_ptr(const char *fn, const char *struct_name)
92 sql_insert(function_ptr, "'%s', '%s', '%s'", get_filename(), fn,
93 struct_name);
96 void sql_insert_return_values(const char *return_values)
98 sql_insert(return_values, "'%s', '%s', %d, '%s'", get_filename(),
99 get_function(), fn_static(), return_values);
102 void sql_insert_call_implies(int type, int param, int value)
104 sql_insert(call_implies, "'%s', '%s', %d, %d, %d, %d", get_filename(),
105 get_function(), fn_static(), type, param, value);
108 void add_definition_db_callback(void (*callback)(const char *name, struct symbol *sym, char *key, char *value), int type)
110 struct def_callback *def_callback = __alloc_def_callback(0);
112 def_callback->hook_type = type;
113 def_callback->callback = callback;
114 add_ptr_list(&callbacks, def_callback);
118 * These call backs are used when the --info option is turned on to print struct
119 * member information. For example foo->bar could have a state in
120 * smatch_extra.c and also check_user.c.
122 void add_member_info_callback(int owner, void (*callback)(char *fn, char *global_static, int param, char *printed_name, struct smatch_state *state))
124 struct member_info_callback *member_callback = __alloc_member_info_callback(0);
126 member_callback->owner = owner;
127 member_callback->callback = callback;
128 add_ptr_list(&member_callbacks, member_callback);
131 void add_returned_state_callback(void (*fn)(int return_id, char *return_ranges, struct expression *returned_expr, struct state_list *slist))
133 struct returned_state_callback *callback = __alloc_returned_state_callback(0);
135 callback->callback = fn;
136 add_ptr_list(&returned_state_callbacks, callback);
139 void add_returned_member_callback(int owner, void (*callback)(int return_id, char *return_ranges, char *printed_name, struct smatch_state *state))
141 struct returned_member_callback *member_callback = __alloc_returned_member_callback(0);
143 member_callback->owner = owner;
144 member_callback->callback = callback;
145 add_ptr_list(&returned_member_callbacks, member_callback);
148 void add_db_fn_call_callback(int type, void (*callback)(struct expression *arg, char *value))
150 struct call_implies_callback *cb = __alloc_call_implies_callback(0);
152 cb->type = type;
153 cb->callback = callback;
154 add_ptr_list(&call_implies_cb_list, cb);
157 static struct symbol *return_type;
158 static struct range_list *return_range_list;
159 static int db_return_callback(void *unused, int argc, char **argv, char **azColName)
161 if (argc != 1)
162 return 0;
163 if (option_debug)
164 sm_msg("return type %d", type_positive_bits(return_type));
165 str_to_rl(return_type, argv[0], &return_range_list);
166 return 0;
169 struct range_list *db_return_vals(struct expression *expr)
171 struct symbol *sym;
172 static char sql_filter[1024];
174 if (expr->type != EXPR_CALL)
175 return NULL;
176 if (expr->fn->type != EXPR_SYMBOL)
177 return NULL;
178 return_type = get_type(expr);
179 if (!return_type)
180 return NULL;
181 sym = expr->fn->symbol;
182 if (!sym)
183 return NULL;
185 if (sym->ctype.modifiers & MOD_STATIC) {
186 snprintf(sql_filter, 1024, "file = '%s' and function = '%s';",
187 get_filename(), sym->ident->name);
188 } else {
189 snprintf(sql_filter, 1024, "function = '%s' and static = 0;",
190 sym->ident->name);
193 return_range_list = NULL;
194 run_sql(db_return_callback, "select return from return_values where %s",
195 sql_filter);
196 return return_range_list;
199 static void match_call_hack(struct expression *expr)
201 char *name;
204 * we just want to record something in the database so that if we have
205 * two calls like: frob(4); frob(some_unkown); then on the receiving
206 * side we know that sometimes frob is called with unknown parameters.
209 name = get_fnptr_name(expr->fn);
210 if (!name)
211 return;
212 sm_msg("info: call_marker '%s' %s", name, is_static(expr->fn) ? "static" : "global");
213 free_string(name);
216 static void print_struct_members(char *fn, char *global_static, struct expression *expr, int param, struct state_list *slist,
217 void (*callback)(char *fn, char *global_static, int param, char *printed_name, struct smatch_state *state))
219 struct sm_state *sm;
220 char *name;
221 struct symbol *sym;
222 int len;
223 char printed_name[256];
224 int is_address = 0;
226 expr = strip_expr(expr);
227 if (expr->type == EXPR_PREOP && expr->op == '&') {
228 expr = strip_expr(expr->unop);
229 is_address = 1;
232 name = expr_to_var_sym(expr, &sym);
233 if (!name || !sym)
234 goto free;
236 len = strlen(name);
237 FOR_EACH_PTR(slist, sm) {
238 if (sm->sym != sym)
239 continue;
240 if (strncmp(name, sm->name, len) || sm->name[len] == '\0')
241 continue;
242 if (is_address)
243 snprintf(printed_name, sizeof(printed_name), "$$->%s", sm->name + len + 1);
244 else
245 snprintf(printed_name, sizeof(printed_name), "$$%s", sm->name + len);
246 callback(fn, global_static, param, printed_name, sm->state);
247 } END_FOR_EACH_PTR(sm);
248 free:
249 free_string(name);
252 static void match_call_info(struct expression *expr)
254 struct member_info_callback *cb;
255 struct expression *arg;
256 struct state_list *slist;
257 char *name;
258 int i;
259 char *gs;
261 name = get_fnptr_name(expr->fn);
262 if (!name)
263 return;
265 if (is_static(expr->fn))
266 gs = (char *)"static";
267 else
268 gs = (char *)"global";
270 FOR_EACH_PTR(member_callbacks, cb) {
271 slist = get_all_states(cb->owner);
272 i = 0;
273 FOR_EACH_PTR(expr->args, arg) {
274 print_struct_members(name, gs, arg, i, slist, cb->callback);
275 i++;
276 } END_FOR_EACH_PTR(arg);
277 free_slist(&slist);
278 } END_FOR_EACH_PTR(cb);
280 free_string(name);
283 static int get_param(int param, char **name, struct symbol **sym)
285 struct symbol *arg;
286 int i;
288 i = 0;
289 FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, arg) {
291 * this is a temporary hack to work around a bug (I think in sparse?)
292 * 2.6.37-rc1:fs/reiserfs/journal.o
293 * If there is a function definition without parameter name found
294 * after a function implementation then it causes a crash.
295 * int foo() {}
296 * int bar(char *);
298 if (arg->ident->name < (char *)100)
299 continue;
300 if (i == param && arg->ident->name) {
301 *name = arg->ident->name;
302 *sym = arg;
303 return TRUE;
305 i++;
306 } END_FOR_EACH_PTR(arg);
308 return FALSE;
311 static struct state_list *final_states;
312 static int prev_func_id = -1;
313 static int db_callback(void *unused, int argc, char **argv, char **azColName)
315 int func_id;
316 long type;
317 long param;
318 char *name = NULL;
319 struct symbol *sym = NULL;
320 struct def_callback *def_callback;
322 if (argc != 5)
323 return 0;
325 func_id = atoi(argv[0]);
326 errno = 0;
327 type = strtol(argv[1], NULL, 10);
328 param = strtol(argv[2], NULL, 10);
329 if (errno)
330 return 0;
332 if (prev_func_id == -1)
333 prev_func_id = func_id;
334 if (func_id != prev_func_id) {
335 merge_slist(&final_states, __pop_fake_cur_slist());
336 __push_fake_cur_slist();
337 __unnullify_path();
338 prev_func_id = func_id;
341 if (type == INTERNAL)
342 return 0;
343 if (param >= 0 && !get_param(param, &name, &sym))
344 return 0;
346 FOR_EACH_PTR(callbacks, def_callback) {
347 if (def_callback->hook_type == type)
348 def_callback->callback(name, sym, argv[3], argv[4]);
349 } END_FOR_EACH_PTR(def_callback);
351 return 0;
354 static void get_direct_callers(struct symbol *sym)
356 char sql_filter[1024];
358 if (sym->ctype.modifiers & MOD_STATIC) {
359 snprintf(sql_filter, 1024,
360 "file = '%s' and function = '%s' order by function_id;",
361 get_filename(), sym->ident->name);
362 } else {
363 snprintf(sql_filter, 1024,
364 "function = '%s' and static = 0 order by function_id;",
365 sym->ident->name);
368 run_sql(db_callback, "select function_id, type, parameter, key, value from caller_info"
369 " where %s", sql_filter);
372 static char *ptr_name;
373 static int get_ptr_name(void *unused, int argc, char **argv, char **azColName)
375 if (!ptr_name)
376 ptr_name = alloc_string(argv[0]);
377 return 0;
380 static void get_function_pointer_callers(struct symbol *sym)
382 char sql_filter[1024];
384 if (sym->ctype.modifiers & MOD_STATIC) {
385 snprintf(sql_filter, 1024, "file = '%s' and function = '%s';",
386 get_filename(), sym->ident->name);
387 } else {
388 snprintf(sql_filter, 1024, "function = '%s';",
389 sym->ident->name);
392 ptr_name = NULL;
393 run_sql(get_ptr_name, "select ptr from function_ptr where %s", sql_filter);
394 if (!ptr_name)
395 return;
397 run_sql(db_callback, "select function_id, type, parameter, key, value from caller_info"
398 " where function = '%s' order by function_id", ptr_name);
400 free_string(ptr_name);
403 static void match_data_from_db(struct symbol *sym)
405 struct sm_state *sm;
407 if (!sym || !sym->ident || !sym->ident->name)
408 return;
410 __push_fake_cur_slist();
411 __unnullify_path();
412 prev_func_id = -1;
414 get_direct_callers(sym);
415 get_function_pointer_callers(sym);
417 merge_slist(&final_states, __pop_fake_cur_slist());
419 FOR_EACH_PTR(final_states, sm) {
420 __set_sm(sm);
421 } END_FOR_EACH_PTR(sm);
423 free_slist(&final_states);
426 static void match_function_assign(struct expression *expr)
428 struct expression *right = expr->right;
429 struct symbol *sym;
430 char *fn_name;
431 char *ptr_name;
433 if (right->type == EXPR_PREOP && right->op == '&')
434 right = strip_expr(right->unop);
435 if (right->type != EXPR_SYMBOL)
436 return;
437 sym = get_type(right);
438 if (!sym || sym->type != SYM_FN)
439 return;
441 fn_name = expr_to_var(right);
442 ptr_name = get_fnptr_name(expr->left);
443 if (!fn_name || !ptr_name)
444 goto free;
446 sql_insert_function_ptr(fn_name, ptr_name);
448 free:
449 free_string(fn_name);
450 free_string(ptr_name);
453 static struct expression *call_implies_call_expr;
454 static int call_implies_callbacks(void *unused, int argc, char **argv, char **azColName)
456 struct call_implies_callback *cb;
457 struct expression *arg = NULL;
458 int type;
459 int param;
461 if (argc != 4)
462 return 0;
464 type = atoi(argv[1]);
465 param = atoi(argv[2]);
467 FOR_EACH_PTR(call_implies_cb_list, cb) {
468 if (cb->type != type)
469 continue;
470 if (param != -1) {
471 arg = get_argument_from_call_expr(call_implies_call_expr->args, param);
472 if (!arg)
473 continue;
475 cb->callback(arg, argv[3]);
476 } END_FOR_EACH_PTR(cb);
478 return 0;
481 static void match_call_implies(struct expression *expr)
483 struct symbol *sym;
484 static char sql_filter[1024];
486 if (expr->fn->type != EXPR_SYMBOL)
487 return;
488 sym = expr->fn->symbol;
489 if (!sym)
490 return;
492 if (sym->ctype.modifiers & MOD_STATIC) {
493 snprintf(sql_filter, 1024, "file = '%s' and function = '%s';",
494 get_filename(), sym->ident->name);
495 } else {
496 snprintf(sql_filter, 1024, "function = '%s' and static = 0;",
497 sym->ident->name);
500 call_implies_call_expr = expr;
501 run_sql(call_implies_callbacks,
502 "select function, type, parameter, value from call_implies where %s",
503 sql_filter);
504 return;
507 static void print_initializer_list(struct expression_list *expr_list,
508 struct symbol *struct_type)
510 struct expression *expr;
511 struct symbol *base_type;
512 char struct_name[256];
514 FOR_EACH_PTR(expr_list, expr) {
515 if (expr->type == EXPR_INDEX && expr->idx_expression && expr->idx_expression->type == EXPR_INITIALIZER) {
516 print_initializer_list(expr->idx_expression->expr_list, struct_type);
517 continue;
519 if (expr->type != EXPR_IDENTIFIER)
520 continue;
521 if (!expr->expr_ident)
522 continue;
523 if (!expr->ident_expression || !expr->ident_expression->symbol_name)
524 continue;
525 base_type = get_type(expr->ident_expression);
526 if (!base_type || base_type->type != SYM_FN)
527 continue;
528 snprintf(struct_name, sizeof(struct_name), "(struct %s)->%s",
529 struct_type->ident->name, expr->expr_ident->name);
530 sql_insert_function_ptr(expr->ident_expression->symbol_name->name,
531 struct_name);
532 } END_FOR_EACH_PTR(expr);
535 static void global_variable(struct symbol *sym)
537 struct symbol *struct_type;
539 if (!sym->ident)
540 return;
541 if (!sym->initializer || sym->initializer->type != EXPR_INITIALIZER)
542 return;
543 struct_type = get_base_type(sym);
544 if (!struct_type)
545 return;
546 if (struct_type->type == SYM_ARRAY) {
547 struct_type = get_base_type(struct_type);
548 if (!struct_type)
549 return;
551 if (struct_type->type != SYM_STRUCT || !struct_type->ident)
552 return;
553 print_initializer_list(sym->initializer->expr_list, struct_type);
556 static void match_return_info(int return_id, char *return_ranges, struct expression *expr, struct state_list *slist)
558 sql_insert_return_states(return_id, return_ranges, INTERNAL, -1, "", "");
561 static int return_id;
562 static void match_function_def(struct symbol *sym)
564 return_id = 0;
567 static void call_return_state_hooks_compare(struct expression *expr)
569 struct returned_state_callback *cb;
570 struct state_list *slist;
571 char *return_ranges;
572 int final_pass_orig = final_pass;
574 __push_fake_cur_slist();
576 final_pass = 0;
577 __split_whole_condition(expr);
578 final_pass = final_pass_orig;
580 return_ranges = alloc_sname("1");
582 return_id++;
583 slist = __get_cur_slist();
584 FOR_EACH_PTR(returned_state_callbacks, cb) {
585 cb->callback(return_id, return_ranges, expr, slist);
586 } END_FOR_EACH_PTR(cb);
588 __push_true_states();
589 __use_false_states();
591 return_ranges = alloc_sname("0");;
592 return_id++;
593 slist = __get_cur_slist();
594 FOR_EACH_PTR(returned_state_callbacks, cb) {
595 cb->callback(return_id, return_ranges, expr, slist);
596 } END_FOR_EACH_PTR(cb);
598 __merge_true_states();
599 __pop_fake_cur_slist();
602 static int call_return_state_hooks_split_possible(struct expression *expr)
604 struct returned_state_callback *cb;
605 struct state_list *slist;
606 struct range_list *rl;
607 char *return_ranges;
608 struct sm_state *sm;
609 struct sm_state *tmp;
610 int ret = 0;
611 int nr_possible, nr_states;
613 sm = get_sm_state_expr(SMATCH_EXTRA, expr);
614 if (!sm || !sm->merged)
615 return 0;
617 /* bail if it gets too complicated */
618 nr_possible = ptr_list_size((struct ptr_list *)sm->possible);
619 nr_states = ptr_list_size((struct ptr_list *)__get_cur_slist());
620 if (nr_possible >= 100)
621 return 0;
622 if (nr_states * nr_possible >= 1000)
623 return 0;
625 FOR_EACH_PTR(sm->possible, tmp) {
626 if (tmp->merged)
627 continue;
629 ret = 1;
630 __push_fake_cur_slist();
632 overwrite_states_using_pool(tmp);
634 rl = cast_rl(cur_func_return_type(), estate_rl(tmp->state));
635 return_ranges = show_rl(rl);
637 return_id++;
638 slist = __get_cur_slist();
639 FOR_EACH_PTR(returned_state_callbacks, cb) {
640 cb->callback(return_id, return_ranges, expr, slist);
641 } END_FOR_EACH_PTR(cb);
643 __pop_fake_cur_slist();
644 } END_FOR_EACH_PTR(tmp);
646 return ret;
649 static void call_return_state_hooks(struct expression *expr)
651 struct returned_state_callback *cb;
652 struct state_list *slist;
653 struct range_list *rl;
654 char *return_ranges;
655 int nr_states;
657 expr = strip_expr(expr);
659 if (!expr) {
660 return_ranges = alloc_sname("");
661 } else if (is_condition(expr)) {
662 call_return_state_hooks_compare(expr);
663 return;
664 } else if (call_return_state_hooks_split_possible(expr)) {
665 return;
666 } else if (get_implied_rl(expr, &rl)) {
667 rl = cast_rl(cur_func_return_type(), rl);
668 return_ranges = show_rl(rl);
669 } else {
670 rl = alloc_whole_rl(cur_func_return_type());
671 return_ranges = show_rl(rl);
674 return_id++;
675 slist = __get_cur_slist();
676 nr_states = ptr_list_size((struct ptr_list *)__get_cur_slist());
677 FOR_EACH_PTR(returned_state_callbacks, cb) {
678 if (nr_states < 10000)
679 cb->callback(return_id, return_ranges, expr, slist);
680 else
681 cb->callback(return_id, return_ranges, expr, NULL);
682 } END_FOR_EACH_PTR(cb);
685 static void print_returned_struct_members(int return_id, char *return_ranges, struct expression *expr, struct state_list *slist)
687 struct returned_member_callback *cb;
688 struct state_list *my_slist;
689 struct sm_state *sm;
690 struct symbol *type;
691 char *name;
692 char member_name[256];
693 int len;
695 type = get_type(expr);
696 if (!type || type->type != SYM_PTR)
697 return;
698 type = get_real_base_type(type);
699 if (!type || type->type != SYM_STRUCT)
700 return;
701 name = expr_to_var(expr);
702 if (!name)
703 return;
705 member_name[sizeof(member_name) - 1] = '\0';
706 strcpy(member_name, "$$");
708 len = strlen(name);
709 FOR_EACH_PTR(returned_member_callbacks, cb) {
710 my_slist = get_all_states_slist(cb->owner, slist);
711 FOR_EACH_PTR(my_slist, sm) {
712 if (strncmp(sm->name, name, len) != 0)
713 continue;
714 if (strncmp(sm->name + len, "->", 2) != 0)
715 continue;
716 strncpy(member_name + 2, sm->name + len, sizeof(member_name) - 2);
717 cb->callback(return_id, return_ranges, member_name, sm->state);
718 } END_FOR_EACH_PTR(sm);
719 free_slist(&my_slist);
720 } END_FOR_EACH_PTR(cb);
722 free_string(name);
725 static void match_end_func_info(struct symbol *sym)
727 if (__path_is_null())
728 return;
729 call_return_state_hooks(NULL);
732 void open_smatch_db(void)
734 #ifdef SQLITE_OPEN_READONLY
735 int rc;
737 if (option_no_db)
738 return;
740 rc = sqlite3_open_v2("smatch_db.sqlite", &db, SQLITE_OPEN_READONLY, NULL);
741 if (rc != SQLITE_OK) {
742 option_no_db = 1;
743 return;
745 return;
746 #else
747 option_no_db = 1;
748 return;
749 #endif
752 void register_definition_db_callbacks(int id)
754 add_hook(&match_function_def, FUNC_DEF_HOOK);
756 if (option_info) {
757 add_hook(&match_call_info, FUNCTION_CALL_HOOK);
758 add_hook(&match_call_hack, FUNCTION_CALL_HOOK);
759 add_hook(&match_function_assign, ASSIGNMENT_HOOK);
760 add_hook(&match_function_assign, GLOBAL_ASSIGNMENT_HOOK);
761 add_hook(&global_variable, BASE_HOOK);
762 add_hook(&global_variable, DECLARATION_HOOK);
763 add_returned_state_callback(match_return_info);
764 add_returned_state_callback(print_returned_struct_members);
765 add_hook(&call_return_state_hooks, RETURN_HOOK);
766 add_hook(&match_end_func_info, END_FUNC_HOOK);
769 if (option_no_db)
770 return;
772 add_hook(&match_data_from_db, FUNC_DEF_HOOK);
773 add_hook(&match_call_implies, FUNCTION_CALL_HOOK);
776 char *get_variable_from_key(struct expression *arg, char *key, struct symbol **sym)
778 char buf[256];
779 char *tmp;
781 if (strcmp(key, "$$") == 0)
782 return expr_to_var_sym(arg, sym);
784 if (strcmp(key, "*$$") == 0) {
785 if (arg->type == EXPR_PREOP && arg->op == '&') {
786 arg = strip_expr(arg->unop);
787 return expr_to_var_sym(arg, sym);
788 } else {
789 tmp = expr_to_var_sym(arg, sym);
790 if (!tmp)
791 return NULL;
792 snprintf(buf, sizeof(buf), "*%s", tmp);
793 free_string(tmp);
794 return alloc_string(buf);
798 if (arg->type == EXPR_PREOP && arg->op == '&') {
799 arg = strip_expr(arg->unop);
800 tmp = expr_to_var_sym(arg, sym);
801 if (!tmp)
802 return NULL;
803 snprintf(buf, sizeof(buf), "%s.%s", tmp, key + 4);
804 return alloc_string(buf);
807 tmp = expr_to_var_sym(arg, sym);
808 if (!tmp)
809 return NULL;
810 snprintf(buf, sizeof(buf), "%s%s", tmp, key + 2);
811 free_string(tmp);
812 return alloc_string(buf);