diff --git a/btree.c b/btree.c index f1f2ada..55b3f91 100644 --- a/btree.c +++ b/btree.c @@ -500,6 +500,18 @@ int btree_put(btree_t bt, const void *key, const void *data) check_btree(bt); + /* Special case: cursor overwrite */ + if (!key) { + if (bt->slot[0] < 0) { + fprintf(stderr, "btree: put at invalid cursor\n"); + return -1; + } + + memcpy(PAGE_DATA(bt->path[0], bt->slot[0]), data, + def->data_size); + return 1; + } + /* Find a path down the tree that leads to the page which should * contain this datum (though the page might be too big to hold it). */ diff --git a/btree.h b/btree.h index 3a28a8c..abd1f7b 100644 --- a/btree.h +++ b/btree.h @@ -49,6 +49,8 @@ void btree_clear(btree_t bt); /* Add or update a record in a B+Tree. Any existing data for the key will * be overwritten. * + * Specifying a NULL key causes the cursor value to be overwritten. + * * Returns 1 if the key already existed, 0 if a new key was inserted, * or -1 if an error occurs (failed memory allocation). */ diff --git a/mspdebug.man b/mspdebug.man index 9b3158a..4aa471b 100644 --- a/mspdebug.man +++ b/mspdebug.man @@ -214,6 +214,11 @@ by MSPDebug, and all symbols are saved as type \fBt\fR. Search for symbols. If a regular expression is given, then all symbols matching the expression are printed. If no expression is specified, then the entire symbol table is listed. +.IP "\fBsym rename\fR \fIregex\fR \fIstring\fR" +Rename symbols by searching for those matching the given regular +expression and substituting the given string for the matched portion. The +symbols renamed are displayed, as well as a total count of all symbols +renamed. .SH ADDRESS EXPRESSIONS Any command which accepts a memory address, length or register value as an argument may be given an address expression. An address diff --git a/stab.c b/stab.c index a60c98c..d55c4e3 100644 --- a/stab.c +++ b/stab.c @@ -169,86 +169,6 @@ int stab_set(const char *name, int value) return 0; } -static int stab_get(const char *name, u_int16_t *value) -{ - struct sym_key skey; - - sym_key_init(&skey, name); - if (btree_get(sym_table, &skey, value)) - return -1; - - return 0; -} - -static int stab_del(const char *name) -{ - struct sym_key skey; - u_int16_t value; - struct addr_key akey; - - sym_key_init(&skey, name); - if (btree_get(sym_table, &skey, &value)) { - fprintf(stderr, "stab: can't delete nonexistent symbol: %s\n", - name); - return -1; - } - - addr_key_init(&akey, value, name); - btree_delete(sym_table, &skey); - btree_delete(addr_table, &akey); - - return 0; -} - -static int stab_enum(stab_callback_t cb) -{ - struct sym_key skey; - u_int16_t value; - int ret; - int count = 0; - - ret = btree_select(sym_table, NULL, BTREE_FIRST, &skey, &value); - while (!ret) { - if (cb && cb(skey.name, value) < 0) - return -1; - count++; - ret = btree_select(sym_table, NULL, BTREE_NEXT, &skey, &value); - } - - return count; -} - -static int stab_re_search(const char *regex, stab_callback_t cb) -{ - struct sym_key skey; - u_int16_t value; - int ret; - int count = 0; - regex_t preg; - - if (regcomp(&preg, regex, REG_EXTENDED | REG_NOSUB)) { - fprintf(stderr, "stab: failed to compile: %s\n", regex); - return -1; - } - - ret = btree_select(sym_table, NULL, BTREE_FIRST, &skey, &value); - while (!ret) { - if (!regexec(&preg, skey.name, 0, NULL, 0)) { - if (cb && cb(skey.name, value) < 0) { - regfree(&preg); - return -1; - } - - count++; - } - - ret = btree_select(sym_table, NULL, BTREE_NEXT, &skey, &value); - } - - regfree(&preg); - return count; -} - int stab_nearest(u_int16_t addr, char *ret_name, int max_len, u_int16_t *ret_offset) { @@ -304,22 +224,12 @@ static int cmd_sym_load_add(int clear, char **arg) return result; } -static FILE *savemap_out; - -static int savemap_write(const char *name, u_int16_t value) -{ - if (fprintf(savemap_out, "%08x t %s\n", value, name) < 0) { - fprintf(stderr, "sym: error writing symbols: %s\n", - strerror(errno)); - return -1; - } - - return 0; -} - static int cmd_sym_savemap(char **arg) { char *fname = get_arg(arg); + int ret; + struct addr_key akey; + FILE *savemap_out; if (!fname) { fprintf(stderr, "sym: filename required to save map\n"); @@ -333,8 +243,13 @@ static int cmd_sym_savemap(char **arg) return -1; } - if (stab_enum(savemap_write) < 0) - return -1; + ret = btree_select(addr_table, NULL, BTREE_FIRST, + &akey, NULL); + while (!ret) { + fprintf(savemap_out, "0x%04x: %s\n", akey.addr, akey.name); + ret = btree_select(addr_table, NULL, BTREE_NEXT, + &akey, NULL); + } if (fclose(savemap_out) < 0) { fprintf(stderr, "sym: error closing %s: %s\n", fname, @@ -346,9 +261,226 @@ static int cmd_sym_savemap(char **arg) return 0; } -static int printsym(const char *name, u_int16_t value) +static int cmd_sym_find(char **arg) { - printf("0x%04x: %s\n", value, name); + char *expr = get_arg(arg); + int ret; + struct sym_key skey; + u_int16_t value; + regex_t preg; + + if (!expr) { + struct addr_key akey; + + ret = btree_select(addr_table, NULL, BTREE_FIRST, + &akey, NULL); + while (!ret) { + printf("0x%04x: %s\n", akey.addr, akey.name); + ret = btree_select(addr_table, NULL, BTREE_NEXT, + &akey, NULL); + } + + return 0; + } + + if (regcomp(&preg, expr, REG_EXTENDED | REG_NOSUB)) { + fprintf(stderr, "stab: failed to compile: %s\n", expr); + return -1; + } + + ret = btree_select(sym_table, NULL, BTREE_FIRST, &skey, &value); + while (!ret) { + if (!regexec(&preg, skey.name, 0, NULL, 0)) + printf("0x%04x: %s\n", value, skey.name); + ret = btree_select(sym_table, NULL, BTREE_NEXT, &skey, &value); + } + + regfree(&preg); + return 0; +} + +struct rename_record { + char old_name[64]; + int start, end; +}; + +struct rename_record *renames; +static int renames_cap; +static int renames_len; + +static void renames_free(void) +{ + if (!renames) + return; + + free(renames); + renames = NULL; + renames_cap = 0; + renames_len = 0; +} + +static int renames_push(const char *old_name, + int start, int end) +{ + struct rename_record *r; + + if (renames_len + 1 > renames_cap) { + int new_cap = renames_cap * 2; + struct rename_record *new_array; + + if (!new_cap) + new_cap = 64; + + new_array = realloc(renames, new_cap * sizeof(renames[0])); + if (!new_array) { + perror("sym: can't allocate memory for renaming"); + return -1; + } + + renames = new_array; + renames_cap = new_cap; + } + + r = &renames[renames_len++]; + strncpy(r->old_name, old_name, sizeof(r->old_name)); + r->old_name[sizeof(r->old_name) - 1] = 0; + r->start = start; + r->end = end; + + return 0; +} + +static int renames_do(const char *replace) +{ + int i; + int count = 0; + + for (i = 0; i < renames_len; i++) { + struct rename_record *r = &renames[i]; + char new_name[128]; + int len = r->start; + struct sym_key skey; + struct addr_key akey; + u_int16_t addr; + u_int16_t new_addr; + + if (len + 1 > sizeof(new_name)) + len = sizeof(new_name) - 1; + + memcpy(new_name, r->old_name, len); + snprintf(new_name + len, + sizeof(new_name) - len, + "%s%s", replace, r->old_name + r->end); + + printf("%s -> %s\n", r->old_name, new_name); + + /* Step 1: delete old symbol, record address */ + sym_key_init(&skey, r->old_name); + if (!btree_get(sym_table, &skey, &new_addr)) { + addr_key_init(&akey, new_addr, r->old_name); + if (btree_delete(addr_table, &akey)) + fprintf(stderr, "sym: warning: " + "no reverse symbol: %s\n", + r->old_name); + } else { + fprintf(stderr, "sym: warning: " + "no forward symbol: %s\n", + r->old_name); + } + btree_delete(sym_table, &skey); + + /* Step 2: overwrite new name */ + sym_key_init(&skey, new_name); + if (!btree_get(sym_table, &skey, &addr)) { + addr_key_init(&akey, addr, new_name); + btree_delete(addr_table, &akey); + } + + if (btree_put(sym_table, &skey, &new_addr) < 0) + return -1; + + addr_key_init(&akey, new_addr, new_name); + if (btree_put(addr_table, &akey, NULL) < 0) + return -1; + + count++; + } + + if (count) + modify_set(MODIFY_SYMS); + + printf("%d symbols renamed\n", count); + return 0; +} + +static int cmd_sym_rename(char **arg) +{ + const char *expr = get_arg(arg); + const char *replace = get_arg(arg); + regex_t preg; + struct addr_key akey; + int ret; + + if (!(expr && replace)) { + fprintf(stderr, "sym: expected pattern and replacement\n"); + return -1; + } + + if (regcomp(&preg, expr, REG_EXTENDED)) { + fprintf(stderr, "sym: failed to compile: %s\n", expr); + return -1; + } + + ret = btree_select(addr_table, NULL, BTREE_FIRST, &akey, NULL); + while (!ret) { + regmatch_t pmatch; + + if (!regexec(&preg, akey.name, 1, &pmatch, 0) && + pmatch.rm_so >= 0 && pmatch.rm_eo > pmatch.rm_so && + renames_push(akey.name, pmatch.rm_so, + pmatch.rm_eo) < 0) + goto fail; + + ret = btree_select(addr_table, NULL, BTREE_NEXT, + &akey, NULL); + } + + regfree(&preg); + ret = renames_do(replace); + renames_free(); + return ret; + + fail: + regfree(&preg); + renames_free(); + return -1; +} + +static int cmd_sym_del(char **arg) +{ + char *name = get_arg(arg); + struct sym_key skey; + u_int16_t value; + struct addr_key akey; + + if (!name) { + fprintf(stderr, "sym: need a name to delete " + "symbol table entries\n"); + return -1; + } + + sym_key_init(&skey, name); + if (btree_get(sym_table, &skey, &value)) { + fprintf(stderr, "sym: can't delete nonexistent symbol: %s\n", + name); + return -1; + } + + addr_key_init(&akey, value, name); + btree_delete(sym_table, &skey); + btree_delete(addr_table, &akey); + + modify_set(MODIFY_SYMS); return 0; } @@ -394,40 +526,18 @@ static int cmd_sym(char **arg) return 0; } - if (!strcasecmp(subcmd, "del")) { - char *name = get_arg(arg); - - if (!name) { - fprintf(stderr, "sym: need a name to delete " - "symbol table entries\n"); - return -1; - } - - if (stab_del(name) < 0) - return -1; - - modify_set(MODIFY_SYMS); - return 0; - } - + if (!strcasecmp(subcmd, "del")) + return cmd_sym_del(arg); if (!strcasecmp(subcmd, "import")) return cmd_sym_load_add(1, arg); if (!strcasecmp(subcmd, "import+")) return cmd_sym_load_add(0, arg); - if (!strcasecmp(subcmd, "export")) return cmd_sym_savemap(arg); - - if (!strcasecmp(subcmd, "find")) { - char *expr = get_arg(arg); - - if (!expr) { - stab_enum(printsym); - return 0; - } - - return stab_re_search(expr, printsym) < 0 ? -1 : 0; - } + if (!strcasecmp(subcmd, "rename")) + return cmd_sym_rename(arg); + if (!strcasecmp(subcmd, "find")) + return cmd_sym_find(arg); fprintf(stderr, "sym: unknown subcommand: %s\n", subcmd); return -1; @@ -451,16 +561,20 @@ static struct command command_sym = { " Save the current symbols to a BSD-style symbol file.\n" "sym find \n" " Search for symbols by regular expression.\n" + "sym rename \n" + " Replace every occurance of a pattern in symbol names.\n" }; static int lookup_token(const char *name, int *value) { - u_int16_t val; + struct sym_key skey; + u_int16_t addr; - if (stab_get(name, &val) < 0) + sym_key_init(&skey, name); + if (btree_get(sym_table, &skey, &addr)) return -1; - *value = val; + *value = addr; return 0; }