Add: Mark API (#5313)
* Add: Bookmark API * Fix: missing licenses + project migration * Fix: migration * Fix: formatting * Fix: migrate info in cmd/project test * Rename: Bookmark to Mark * Remove: bookmark * Fix: function type comment * Fix: code coverage * Add: tests, docs, ownership type annotations - Added test/db/cmd/cmd_mark test - Improved API documentation with \brief, \param, \return and proper grammar - Added RZ_BORROW / RZ_OWN annotations to relevant API functions * Fix: missed annotation * Fix: type annotations + update meson.build * Fix: License in `cmd_mark.yaml` * `serialize_mark.c`: add NULL checks
This commit is contained in:
parent
a77afb0a29
commit
06061654db
27 changed files with 2497 additions and 17 deletions
146
librz/core/cmark.c
Normal file
146
librz/core/cmark.c
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
// SPDX-FileCopyrightText: 2025 PremadeS <emadsohail001@gmail.com>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <rz_types_base.h>
|
||||
#include <rz_util/rz_str.h>
|
||||
#include <rz_cmd.h>
|
||||
#include <rz_mark.h>
|
||||
#include <rz_util.h>
|
||||
#include <rz_cons.h>
|
||||
|
||||
struct print_mark_t {
|
||||
RzMark *b;
|
||||
PJ *pj;
|
||||
RzTable *tbl;
|
||||
bool in_range;
|
||||
ut64 range_from;
|
||||
ut64 range_to;
|
||||
};
|
||||
|
||||
static bool print_mark_name(RzMarkItem *bm, void *user) {
|
||||
struct print_mark_t *u = (struct print_mark_t *)user;
|
||||
if (u->in_range &&
|
||||
!(RZ_BETWEEN(u->range_from, bm->from, u->range_to) || RZ_BETWEEN(u->range_from, bm->to, u->range_to) ||
|
||||
RZ_BETWEEN(bm->from, u->range_from, bm->to) || RZ_BETWEEN(bm->from, u->range_to, bm->to))) {
|
||||
return true;
|
||||
}
|
||||
rz_cons_printf("%s\n", bm->name);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool print_mark_json(RzMarkItem *bm, void *user) {
|
||||
struct print_mark_t *u = (struct print_mark_t *)user;
|
||||
if (u->in_range &&
|
||||
!(RZ_BETWEEN(u->range_from, bm->from, u->range_to) || RZ_BETWEEN(u->range_from, bm->to, u->range_to) ||
|
||||
RZ_BETWEEN(bm->from, u->range_from, bm->to) || RZ_BETWEEN(bm->from, u->range_to, bm->to))) {
|
||||
return true;
|
||||
}
|
||||
const char *realname = RZ_STR_ISEMPTY(bm->realname) ? bm->name : bm->realname;
|
||||
pj_o(u->pj);
|
||||
pj_kn(u->pj, "from", bm->from);
|
||||
pj_kn(u->pj, "to", bm->to);
|
||||
pj_ks(u->pj, "name", bm->name);
|
||||
pj_ks(u->pj, "realname", realname);
|
||||
pj_ki(u->pj, "size", (bm->to >= bm->from) ? (bm->to - bm->from) : 0);
|
||||
if (bm->comment) {
|
||||
pj_ks(u->pj, "comment", bm->comment);
|
||||
}
|
||||
pj_end(u->pj);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool print_mark_range_name(RzMarkItem *bm, void *user) {
|
||||
struct print_mark_t *u = (struct print_mark_t *)user;
|
||||
if (u->in_range &&
|
||||
!(RZ_BETWEEN(u->range_from, bm->from, u->range_to) || RZ_BETWEEN(u->range_from, bm->to, u->range_to) ||
|
||||
RZ_BETWEEN(bm->from, u->range_from, bm->to) || RZ_BETWEEN(bm->from, u->range_to, bm->to))) {
|
||||
return true;
|
||||
}
|
||||
rz_cons_printf("[0x%08" PFMT64x " - 0x%08" PFMT64x "] %s\n",
|
||||
bm->from, bm->to, bm->name);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool print_mark_rizin(RzMarkItem *bm, void *user) {
|
||||
struct print_mark_t *u = (struct print_mark_t *)user;
|
||||
if (u->in_range &&
|
||||
!(RZ_BETWEEN(u->range_from, bm->from, u->range_to) || RZ_BETWEEN(u->range_from, bm->to, u->range_to) ||
|
||||
RZ_BETWEEN(bm->from, u->range_from, bm->to) || RZ_BETWEEN(bm->from, u->range_to, bm->to))) {
|
||||
return true;
|
||||
}
|
||||
char *comment_b64 = NULL, *tmp = NULL;
|
||||
if (RZ_STR_ISNOTEMPTY(bm->comment)) {
|
||||
comment_b64 = rz_base64_encode_dyn((const ut8 *)bm->comment, strlen(bm->comment));
|
||||
// prefix the armored string with "base64:"
|
||||
if (comment_b64) {
|
||||
tmp = rz_str_newf("base64:%s", comment_b64);
|
||||
free(comment_b64);
|
||||
comment_b64 = tmp;
|
||||
}
|
||||
}
|
||||
ut64 size = (bm->to >= bm->from) ? (bm->to - bm->from) : 0;
|
||||
rz_cons_printf("[0x%08" PFMT64x " - 0x%08" PFMT64x "] %s %" PFMT64u " %s\n",
|
||||
bm->from, bm->to, bm->name, size, rz_str_get_null(comment_b64));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool print_mark_table(RzMarkItem *bm, void *user) {
|
||||
struct print_mark_t *u = (struct print_mark_t *)user;
|
||||
if (u->in_range &&
|
||||
!(RZ_BETWEEN(u->range_from, bm->from, u->range_to) || RZ_BETWEEN(u->range_from, bm->to, u->range_to) ||
|
||||
RZ_BETWEEN(bm->from, u->range_from, bm->to) || RZ_BETWEEN(bm->from, u->range_to, bm->to))) {
|
||||
return true;
|
||||
}
|
||||
if (!RZ_STR_ISEMPTY(bm->name)) {
|
||||
const char *realname = RZ_STR_ISEMPTY(bm->realname) ? bm->name : bm->realname;
|
||||
rz_table_add_rowf(u->tbl, "XXss", bm->from, bm->to, bm->name, realname);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void mark_print(RzMark *b, RzCmdStateOutput *state,
|
||||
ut64 range_from, ut64 range_to, bool in_range) {
|
||||
rz_return_if_fail(b);
|
||||
struct print_mark_t u = {
|
||||
.b = b,
|
||||
.in_range = in_range,
|
||||
.range_from = range_from,
|
||||
.range_to = range_to
|
||||
};
|
||||
|
||||
switch (state->mode) {
|
||||
case RZ_OUTPUT_MODE_QUIET:
|
||||
rz_mark_foreach(b, print_mark_name, &u);
|
||||
break;
|
||||
case RZ_OUTPUT_MODE_STANDARD:
|
||||
rz_mark_foreach(b, print_mark_range_name, &u);
|
||||
break;
|
||||
case RZ_OUTPUT_MODE_JSON:
|
||||
u.pj = state->d.pj;
|
||||
pj_a(state->d.pj);
|
||||
rz_mark_foreach(b, print_mark_json, &u);
|
||||
pj_end(state->d.pj);
|
||||
break;
|
||||
case RZ_OUTPUT_MODE_RIZIN:
|
||||
rz_mark_foreach(b, print_mark_rizin, &u);
|
||||
break;
|
||||
case RZ_OUTPUT_MODE_TABLE:
|
||||
u.tbl = state->d.t;
|
||||
rz_cmd_state_output_set_columnsf(state, "XXss", "from", "to", "name", "realname");
|
||||
rz_mark_foreach(b, print_mark_table, &u);
|
||||
break;
|
||||
default:
|
||||
rz_warn_if_reached();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
RZ_IPI void rz_core_mark_print(RzMark *b, RzCmdStateOutput *state) {
|
||||
mark_print(b, state, UT64_MAX, UT64_MAX, false);
|
||||
}
|
||||
|
||||
RZ_IPI void rz_core_mark_range_print(RzMark *b, RzCmdStateOutput *state,
|
||||
ut64 range_from, ut64 range_to) {
|
||||
mark_print(b, state, range_from, range_to, true);
|
||||
}
|
||||
336
librz/core/cmd/cmd_mark.c
Normal file
336
librz/core/cmd/cmd_mark.c
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
// SPDX-FileCopyrightText: 2009-2025 PremadeS <emadsohail001@gmail.com>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <stddef.h>
|
||||
#include <rz_cons.h>
|
||||
#include <rz_core.h>
|
||||
#include <../core_private.h>
|
||||
#include <rz_mark.h>
|
||||
#include <rz_cmd.h>
|
||||
#include <rz_flag.h>
|
||||
#include <rz_util/rz_log.h>
|
||||
|
||||
static bool mark_set_comment(RzMarkItem *item, const char *comment) {
|
||||
if (!strncmp(comment, "base64:", 7)) {
|
||||
const char *b64str = comment + 7;
|
||||
char *dec = (char *)rz_base64_decode_dyn(b64str, -1);
|
||||
if (!dec) {
|
||||
RZ_LOG_ERROR("Failed to decode base64-encoded string: \"%s\"\n", b64str);
|
||||
return false;
|
||||
}
|
||||
rz_mark_item_set_comment(item, dec);
|
||||
free(dec);
|
||||
} else {
|
||||
rz_mark_item_set_comment(item, comment);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
struct mark_to_mark_t {
|
||||
ut64 next;
|
||||
ut64 offset;
|
||||
};
|
||||
|
||||
static bool mark_to_mark_foreach(RzMarkItem *bi, void *user) {
|
||||
struct mark_to_mark_t *u = (struct mark_to_mark_t *)user;
|
||||
if (bi->from < u->next && bi->from > u->offset) {
|
||||
u->next = bi->from;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int mark_to_mark(RzCore *core, const char *glob) {
|
||||
rz_return_val_if_fail(core, 0);
|
||||
rz_return_val_if_fail(glob, 0);
|
||||
glob = rz_str_trim_head_ro(glob);
|
||||
struct mark_to_mark_t u = { .next = UT64_MAX, .offset = core->offset };
|
||||
rz_mark_foreach_glob(core->marks, glob, mark_to_mark_foreach, &u);
|
||||
if (u.next != UT64_MAX && u.next > core->offset) {
|
||||
return u.next - core->offset;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mark_add_flag(RzFlag *f, RzMarkItem *bi) {
|
||||
if (!f || !bi) {
|
||||
return;
|
||||
}
|
||||
RzSpace *prev_space = rz_flag_space_cur(f);
|
||||
RzSpace *mark_space = rz_flag_space_get(f, "marks");
|
||||
if (!mark_space) {
|
||||
mark_space = rz_flag_space_set(f, "marks");
|
||||
} else {
|
||||
rz_flag_space_set(f, "marks");
|
||||
}
|
||||
|
||||
rz_flag_set(f, bi->name, bi->from, (ut32)(bi->to - bi->from));
|
||||
if (prev_space) {
|
||||
rz_flag_space_set(f, prev_space->name);
|
||||
}
|
||||
}
|
||||
|
||||
RZ_IPI RzCmdStatus rz_mark_add_handler(RzCore *core, int argc, const char **argv) {
|
||||
RzMarkItem *item;
|
||||
bool addMark = true;
|
||||
ut64 end = rz_num_math(core->num, argv[2]);
|
||||
if ((item = rz_mark_get_at(core->marks, core->offset))) {
|
||||
RZ_LOG_ERROR("Cannot create mark \"%s\" from 0x%" PFMT64x " to 0x%" PFMT64x " because there is already \"%s\" mark\n", argv[1], core->offset, end, item->name);
|
||||
addMark = false;
|
||||
}
|
||||
|
||||
if (end < core->offset) {
|
||||
RZ_LOG_ERROR("Ending offset (0x%" PFMT64x ") must be greater than or equal to starting offset (0x%" PFMT64x ")\n",
|
||||
end, core->offset);
|
||||
return RZ_CMD_STATUS_ERROR;
|
||||
}
|
||||
|
||||
if (addMark) {
|
||||
item = rz_mark_set(core->marks, argv[1], core->offset, end);
|
||||
if (item) {
|
||||
mark_add_flag(core->flags, item);
|
||||
}
|
||||
}
|
||||
if (!item) {
|
||||
RZ_LOG_ERROR(
|
||||
"Cannot create mark \"%s\" from 0x%" PFMT64x " to 0x%" PFMT64x "\n", argv[1], core->offset, end);
|
||||
return RZ_CMD_STATUS_ERROR;
|
||||
}
|
||||
if (argc > 3) {
|
||||
return bool2status(mark_set_comment(item, argv[3]));
|
||||
}
|
||||
return RZ_CMD_STATUS_OK;
|
||||
}
|
||||
|
||||
RZ_IPI RzCmdStatus rz_mark_append_handler(RzCore *core, int argc, const char **argv) {
|
||||
ut64 end = rz_num_math(core->num, argv[2]);
|
||||
RzMarkItem *item = rz_mark_set(core->marks, argv[1], core->offset, end);
|
||||
if (!item) {
|
||||
RZ_LOG_ERROR(
|
||||
"Cannot create mark \"%s\" from 0x%" PFMT64x " to 0x%" PFMT64x "\n", argv[1], core->offset, end);
|
||||
return RZ_CMD_STATUS_ERROR;
|
||||
}
|
||||
mark_add_flag(core->flags, item);
|
||||
if (argc > 3) {
|
||||
return bool2status(mark_set_comment(item, argv[3]));
|
||||
}
|
||||
return RZ_CMD_STATUS_OK;
|
||||
}
|
||||
|
||||
RZ_IPI RzCmdStatus rz_mark_remove_handler(RzCore *core, int argc, const char **argv) {
|
||||
if (argc < 2) {
|
||||
RzList *list = rz_mark_get_all_off(core->marks, core->offset);
|
||||
RzListIter *it;
|
||||
RzMarkItem *bi;
|
||||
rz_list_foreach (list, it, bi) {
|
||||
RzFlagItem *fi = rz_flag_get(core->flags, bi->name);
|
||||
rz_flag_unset(core->flags, fi);
|
||||
}
|
||||
rz_mark_unset_all_off(core->marks, core->offset);
|
||||
return RZ_CMD_STATUS_OK;
|
||||
}
|
||||
if (rz_mark_unset_glob(core->marks, argv[1]) < 0) {
|
||||
return RZ_CMD_STATUS_ERROR;
|
||||
}
|
||||
RzFlagItem *fi = rz_flag_get(core->flags, argv[1]);
|
||||
if (fi) {
|
||||
rz_flag_unset(core->flags, fi);
|
||||
}
|
||||
return RZ_CMD_STATUS_OK;
|
||||
}
|
||||
|
||||
RZ_IPI RzCmdStatus rz_mark_list_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
|
||||
rz_core_mark_print(core->marks, state);
|
||||
return RZ_CMD_STATUS_OK;
|
||||
}
|
||||
|
||||
RZ_IPI RzCmdStatus rz_mark_remove_all_handler(RzCore *core, int argc, const char **argv) {
|
||||
RzList *list = rz_mark_all_list(core->marks);
|
||||
RzListIter *it;
|
||||
RzMarkItem *bi;
|
||||
rz_list_foreach (list, it, bi) {
|
||||
RzFlagItem *fi = rz_flag_get(core->flags, bi->name);
|
||||
if (fi) {
|
||||
rz_flag_unset(core->flags, fi);
|
||||
}
|
||||
}
|
||||
rz_list_free(list);
|
||||
rz_mark_unset_all(core->marks);
|
||||
return RZ_CMD_STATUS_OK;
|
||||
}
|
||||
|
||||
RZ_IPI RzCmdStatus rz_mark_color_handler(RzCore *core, int argc, const char **argv) {
|
||||
RzMarkItem *bi = rz_mark_get(core->marks, argv[1]);
|
||||
if (!bi) {
|
||||
RZ_LOG_ERROR("Cannot find the mark '%s'\n", argv[1]);
|
||||
return RZ_CMD_STATUS_ERROR;
|
||||
}
|
||||
if (argc < 3) {
|
||||
if (bi->color) {
|
||||
rz_cons_println(bi->color);
|
||||
}
|
||||
return RZ_CMD_STATUS_OK;
|
||||
}
|
||||
const char *ret = rz_mark_item_set_color(bi, argv[2]);
|
||||
if (ret) {
|
||||
rz_cons_println(ret);
|
||||
}
|
||||
return RZ_CMD_STATUS_OK;
|
||||
}
|
||||
|
||||
RZ_IPI RzCmdStatus rz_mark_list_at_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
|
||||
rz_core_mark_range_print(core->marks, state, core->offset, core->offset + 1);
|
||||
return RZ_CMD_STATUS_OK;
|
||||
}
|
||||
|
||||
RZ_IPI RzCmdStatus rz_mark_comment_handler(RzCore *core, int argc, const char **argv) {
|
||||
RzMarkItem *item;
|
||||
if (argc > 2) {
|
||||
item = rz_mark_get(core->marks, argv[1]);
|
||||
if (!item) {
|
||||
RZ_LOG_ERROR("Cannot find mark with name '%s'\n", argv[1]);
|
||||
return RZ_CMD_STATUS_ERROR;
|
||||
}
|
||||
return bool2status(mark_set_comment(item, argv[2]));
|
||||
} else {
|
||||
item = rz_mark_get(core->marks, argv[1]);
|
||||
if (!item) {
|
||||
RZ_LOG_ERROR("Cannot find mark\n");
|
||||
return RZ_CMD_STATUS_ERROR;
|
||||
} else if (item->comment) {
|
||||
rz_cons_println(item->comment);
|
||||
}
|
||||
}
|
||||
return RZ_CMD_STATUS_OK;
|
||||
}
|
||||
|
||||
RZ_IPI RzCmdStatus rz_mark_rename_handler(RzCore *core, int argc, const char **argv) {
|
||||
RzMarkItem *item = rz_mark_get(core->marks, argv[1]);
|
||||
if (!item) {
|
||||
RZ_LOG_ERROR("Cannot find matching mark\n");
|
||||
return RZ_CMD_STATUS_ERROR;
|
||||
}
|
||||
if (!rz_mark_rename(core->marks, item, argv[2])) {
|
||||
RZ_LOG_ERROR("Invalid new mark name\n");
|
||||
return RZ_CMD_STATUS_ERROR;
|
||||
}
|
||||
RzFlagItem *fi = rz_flag_get(core->flags, argv[1]);
|
||||
rz_flag_rename(core->flags, fi, argv[2]);
|
||||
return RZ_CMD_STATUS_OK;
|
||||
}
|
||||
|
||||
RZ_IPI RzCmdStatus rz_mark_realname_handler(RzCore *core, int argc, const char **argv) {
|
||||
RzMarkItem *item = rz_mark_get(core->marks, argv[1]);
|
||||
if (!item) {
|
||||
RZ_LOG_ERROR("Cannot find mark\n");
|
||||
return RZ_CMD_STATUS_ERROR;
|
||||
}
|
||||
if (argc < 3) {
|
||||
rz_cons_printf("%s\n", item->realname);
|
||||
} else {
|
||||
rz_mark_item_set_realname(item, argv[2]);
|
||||
RzFlagItem *fi = rz_flag_get(core->flags, item->name);
|
||||
rz_flag_item_set_realname(fi, argv[2]);
|
||||
}
|
||||
return RZ_CMD_STATUS_OK;
|
||||
}
|
||||
|
||||
RZ_IPI RzCmdStatus rz_mark_move_handler(RzCore *core, int argc, const char **argv) {
|
||||
RzMarkItem *item = rz_mark_get(core->marks, argv[1]);
|
||||
if (!item) {
|
||||
RZ_LOG_ERROR("Cannot find matching mark\n");
|
||||
return RZ_CMD_STATUS_OK;
|
||||
}
|
||||
ut64 new_start = rz_num_math(core->num, argv[2]);
|
||||
ut64 new_end = rz_num_math(core->num, argv[3]);
|
||||
RzMarkItem *new_item = rz_mark_set(core->marks, item->name, new_start, new_end);
|
||||
if (!new_item) {
|
||||
RZ_LOG_ERROR(
|
||||
"Cannot move mark \"%s\" to 0x%" PFMT64x " - 0x%" PFMT64x "\n", argv[1], new_start, new_end);
|
||||
return RZ_CMD_STATUS_ERROR;
|
||||
}
|
||||
rz_flag_set(core->flags, item->name, new_start, (ut32)(new_end - new_start));
|
||||
return RZ_CMD_STATUS_OK;
|
||||
}
|
||||
|
||||
RZ_IPI RzCmdStatus rz_mark_distance_handler(RzCore *core, int argc, const char **argv) {
|
||||
rz_cons_printf("%d\n", mark_to_mark(core, argv[1]));
|
||||
return RZ_CMD_STATUS_OK;
|
||||
}
|
||||
|
||||
RZ_IPI RzCmdStatus rz_core_mark_describe(RzCore *core, ut64 addr, RzMarkItem *b, RzCmdStateOutput *state) {
|
||||
ut64 size = (b->to >= b->from) ? (b->to - b->from) : 0;
|
||||
PJ *pj = state->d.pj;
|
||||
rz_cmd_state_output_set_columnsf(state, "ssXXds", "name", "realname",
|
||||
"from", "to", "size", "comment");
|
||||
switch (state->mode) {
|
||||
case RZ_OUTPUT_MODE_JSON:
|
||||
pj_o(pj);
|
||||
pj_ks(pj, "name", b->name);
|
||||
pj_kn(pj, "from", b->from);
|
||||
pj_kn(pj, "to", b->to);
|
||||
pj_kn(pj, "size", size);
|
||||
if (b->comment) {
|
||||
pj_ks(pj, "comment", b->comment);
|
||||
}
|
||||
if (b->realname) {
|
||||
pj_ks(pj, "realname", b->realname);
|
||||
}
|
||||
pj_end(pj);
|
||||
break;
|
||||
|
||||
case RZ_OUTPUT_MODE_TABLE: {
|
||||
ut64 size = (b->to >= b->from) ? (b->to - b->from) : 0;
|
||||
const char *name = b->realname ? b->realname : b->name;
|
||||
if (addr >= b->from && addr <= b->to) {
|
||||
rz_table_add_rowf(state->d.t, "ssXXds", b->name, name, b->from, b->to, size, b->comment);
|
||||
} else if (addr < b->from) {
|
||||
const char *descr_name = rz_str_newf("%s - %d", name, (int)(b->from - addr));
|
||||
rz_table_add_rowf(state->d.t, "ssXXds", b->name, descr_name, b->from, b->to, size, b->comment);
|
||||
} else {
|
||||
const char *descr_name = rz_str_newf("%s + %d", name, (int)(addr - b->to));
|
||||
rz_table_add_rowf(state->d.t, "ssXXds", b->name, descr_name, b->from, b->to, size, b->comment);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RZ_OUTPUT_MODE_STANDARD: {
|
||||
const char *name = b->realname ? b->realname : b->name;
|
||||
if (addr >= b->from && addr <= b->to) {
|
||||
rz_cons_printf("[0x%08" PFMT64x " - 0x%08" PFMT64x "] %s\n", b->from, b->to, name);
|
||||
} else if (addr < b->from) {
|
||||
rz_cons_printf("[0x%08" PFMT64x " - 0x%08" PFMT64x "] %s - %d\n", b->from, b->to, name, (int)(b->from - addr));
|
||||
} else {
|
||||
rz_cons_printf("[0x%08" PFMT64x " - 0x%08" PFMT64x "] %s + %d\n", b->from, b->to, name, (int)(addr - b->to));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
rz_warn_if_reached();
|
||||
break;
|
||||
}
|
||||
return RZ_CMD_STATUS_OK;
|
||||
}
|
||||
|
||||
RZ_IPI RzCmdStatus rz_mark_describe_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
|
||||
RzMarkItem *b;
|
||||
if (argc > 1) {
|
||||
b = rz_mark_get(core->marks, argv[1]);
|
||||
} else {
|
||||
b = rz_mark_get_at(core->marks, core->offset);
|
||||
}
|
||||
if (!b) {
|
||||
RZ_LOG_ERROR("Cannot find mark\n");
|
||||
return RZ_CMD_STATUS_ERROR;
|
||||
}
|
||||
rz_core_mark_describe(core, core->offset, b, state);
|
||||
return RZ_CMD_STATUS_OK;
|
||||
}
|
||||
|
||||
RZ_IPI RzCmdStatus rz_mark_range_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
|
||||
if (argc > 1) {
|
||||
ut64 size = rz_num_math(core->num, argv[1]);
|
||||
rz_core_mark_range_print(core->marks, state, core->offset, core->offset + size);
|
||||
} else {
|
||||
rz_core_mark_range_print(core->marks, state, core->offset, core->offset + core->blocksize);
|
||||
}
|
||||
return RZ_CMD_STATUS_OK;
|
||||
}
|
||||
|
|
@ -72,6 +72,7 @@ static const RzCmdDescDetail cmd_debug_inject_opcode_details[2];
|
|||
static const RzCmdDescDetail cmd_debug_inject_assembly_details[2];
|
||||
static const RzCmdDescDetail cmd_debug_inject_syscall_details[2];
|
||||
static const RzCmdDescDetail eval_getset_details[2];
|
||||
static const RzCmdDescDetail mark_describe_details[2];
|
||||
static const RzCmdDescDetail egg_config_details[2];
|
||||
static const RzCmdDescDetail history_list_or_exec_details[2];
|
||||
static const RzCmdDescDetail cmd_info_query_details[2];
|
||||
|
|
@ -583,6 +584,17 @@ static const RzCmdDescArg flag_tag_search_args[2];
|
|||
static const RzCmdDescArg flag_zone_add_args[2];
|
||||
static const RzCmdDescArg flag_zone_remove_args[2];
|
||||
static const RzCmdDescArg flag_hexdump_args[2];
|
||||
static const RzCmdDescArg mark_add_args[4];
|
||||
static const RzCmdDescArg mark_append_args[4];
|
||||
static const RzCmdDescArg mark_remove_args[2];
|
||||
static const RzCmdDescArg mark_color_args[3];
|
||||
static const RzCmdDescArg mark_comment_args[3];
|
||||
static const RzCmdDescArg mark_rename_args[3];
|
||||
static const RzCmdDescArg mark_realname_args[3];
|
||||
static const RzCmdDescArg mark_move_args[4];
|
||||
static const RzCmdDescArg mark_distance_args[2];
|
||||
static const RzCmdDescArg mark_describe_args[2];
|
||||
static const RzCmdDescArg mark_range_args[2];
|
||||
static const RzCmdDescArg flirt_create_args[2];
|
||||
static const RzCmdDescArg flirt_dump_args[2];
|
||||
static const RzCmdDescArg flirt_scan_args[2];
|
||||
|
|
@ -12457,6 +12469,262 @@ static const RzCmdDescHelp flag_hexdump_help = {
|
|||
.args = flag_hexdump_args,
|
||||
};
|
||||
|
||||
static const RzCmdDescHelp m_help = {
|
||||
.summary = "Manage marks",
|
||||
};
|
||||
static const RzCmdDescArg mark_add_args[] = {
|
||||
{
|
||||
.name = "name",
|
||||
.type = RZ_CMD_ARG_TYPE_STRING,
|
||||
|
||||
},
|
||||
{
|
||||
.name = "end",
|
||||
.type = RZ_CMD_ARG_TYPE_NUM,
|
||||
|
||||
},
|
||||
{
|
||||
.name = "comment",
|
||||
.type = RZ_CMD_ARG_TYPE_STRING,
|
||||
.flags = RZ_CMD_ARG_FLAG_LAST,
|
||||
.optional = true,
|
||||
|
||||
},
|
||||
{ 0 },
|
||||
};
|
||||
static const RzCmdDescHelp mark_add_help = {
|
||||
.summary = "Adds a mark if there are no existing marks.",
|
||||
.description = "Adds the mark to the current offset only if no mark covers this offset already",
|
||||
.args = mark_add_args,
|
||||
};
|
||||
|
||||
static const RzCmdDescArg mark_append_args[] = {
|
||||
{
|
||||
.name = "name",
|
||||
.type = RZ_CMD_ARG_TYPE_STRING,
|
||||
|
||||
},
|
||||
{
|
||||
.name = "end",
|
||||
.type = RZ_CMD_ARG_TYPE_NUM,
|
||||
|
||||
},
|
||||
{
|
||||
.name = "comment",
|
||||
.type = RZ_CMD_ARG_TYPE_STRING,
|
||||
.flags = RZ_CMD_ARG_FLAG_LAST,
|
||||
.optional = true,
|
||||
|
||||
},
|
||||
{ 0 },
|
||||
};
|
||||
static const RzCmdDescHelp mark_append_help = {
|
||||
.summary = "Add mark.",
|
||||
.description = "Adds a mark at the current offset, replacing any existing mark with the same name.",
|
||||
.args = mark_append_args,
|
||||
};
|
||||
|
||||
static const RzCmdDescArg mark_remove_args[] = {
|
||||
{
|
||||
.name = "regex_pattern",
|
||||
.type = RZ_CMD_ARG_TYPE_STRING,
|
||||
.flags = RZ_CMD_ARG_FLAG_LAST,
|
||||
.optional = true,
|
||||
|
||||
},
|
||||
{ 0 },
|
||||
};
|
||||
static const RzCmdDescHelp mark_remove_help = {
|
||||
.summary = "Remove mark.",
|
||||
.description = "Removes marks by name. If <regex_pattern> is given, only marks whose name matches the regex are removed. Otherwise, all marks covering the current offset are removed.",
|
||||
.args = mark_remove_args,
|
||||
};
|
||||
|
||||
static const RzCmdDescArg mark_remove_all_args[] = {
|
||||
{ 0 },
|
||||
};
|
||||
static const RzCmdDescHelp mark_remove_all_help = {
|
||||
.summary = "Remove all marks.",
|
||||
.args = mark_remove_all_args,
|
||||
};
|
||||
|
||||
static const RzCmdDescArg mark_list_args[] = {
|
||||
{ 0 },
|
||||
};
|
||||
static const RzCmdDescHelp mark_list_help = {
|
||||
.summary = "List all marks.",
|
||||
.args = mark_list_args,
|
||||
};
|
||||
|
||||
static const RzCmdDescArg mark_list_at_args[] = {
|
||||
{ 0 },
|
||||
};
|
||||
static const RzCmdDescHelp mark_list_at_help = {
|
||||
.summary = "List all marks containing the current offset.",
|
||||
.args = mark_list_at_args,
|
||||
};
|
||||
|
||||
static const RzCmdDescArg mark_color_args[] = {
|
||||
{
|
||||
.name = "name",
|
||||
.type = RZ_CMD_ARG_TYPE_FLAG,
|
||||
|
||||
},
|
||||
{
|
||||
.name = "color",
|
||||
.type = RZ_CMD_ARG_TYPE_STRING,
|
||||
.flags = RZ_CMD_ARG_FLAG_LAST,
|
||||
.optional = true,
|
||||
|
||||
},
|
||||
{ 0 },
|
||||
};
|
||||
static const RzCmdDescHelp mark_color_help = {
|
||||
.summary = "Set a color for the given mark / Show the color for the given mark.",
|
||||
.args = mark_color_args,
|
||||
};
|
||||
|
||||
static const RzCmdDescArg mark_comment_args[] = {
|
||||
{
|
||||
.name = "name",
|
||||
.type = RZ_CMD_ARG_TYPE_FLAG,
|
||||
|
||||
},
|
||||
{
|
||||
.name = "comment",
|
||||
.type = RZ_CMD_ARG_TYPE_STRING,
|
||||
.flags = RZ_CMD_ARG_FLAG_LAST,
|
||||
.optional = true,
|
||||
|
||||
},
|
||||
{ 0 },
|
||||
};
|
||||
static const RzCmdDescHelp mark_comment_help = {
|
||||
.summary = "Set a comment for the given mark. If no comment is given, it shows the current one for the mark.",
|
||||
.args = mark_comment_args,
|
||||
};
|
||||
|
||||
static const RzCmdDescArg mark_rename_args[] = {
|
||||
{
|
||||
.name = "old",
|
||||
.type = RZ_CMD_ARG_TYPE_FLAG,
|
||||
|
||||
},
|
||||
{
|
||||
.name = "new",
|
||||
.type = RZ_CMD_ARG_TYPE_STRING,
|
||||
.flags = RZ_CMD_ARG_FLAG_LAST,
|
||||
|
||||
},
|
||||
{ 0 },
|
||||
};
|
||||
static const RzCmdDescHelp mark_rename_help = {
|
||||
.summary = "Rename mark.",
|
||||
.args = mark_rename_args,
|
||||
};
|
||||
|
||||
static const RzCmdDescArg mark_realname_args[] = {
|
||||
{
|
||||
.name = "name",
|
||||
.type = RZ_CMD_ARG_TYPE_FLAG,
|
||||
|
||||
},
|
||||
{
|
||||
.name = "realname",
|
||||
.type = RZ_CMD_ARG_TYPE_STRING,
|
||||
.flags = RZ_CMD_ARG_FLAG_LAST,
|
||||
.optional = true,
|
||||
|
||||
},
|
||||
{ 0 },
|
||||
};
|
||||
static const RzCmdDescHelp mark_realname_help = {
|
||||
.summary = "Show the realname of the mark / Set the realname of the mark.",
|
||||
.description = "Each mark has two identifiers. <name> is the unique, escaped identifier used internally by Rizin. <realname> is the original, unescaped name, kept as given by the user.",
|
||||
.args = mark_realname_args,
|
||||
};
|
||||
|
||||
static const RzCmdDescArg mark_move_args[] = {
|
||||
{
|
||||
.name = "name",
|
||||
.type = RZ_CMD_ARG_TYPE_FLAG,
|
||||
|
||||
},
|
||||
{
|
||||
.name = "new_start",
|
||||
.type = RZ_CMD_ARG_TYPE_RZNUM,
|
||||
|
||||
},
|
||||
{
|
||||
.name = "new_end",
|
||||
.type = RZ_CMD_ARG_TYPE_RZNUM,
|
||||
.flags = RZ_CMD_ARG_FLAG_LAST,
|
||||
|
||||
},
|
||||
{ 0 },
|
||||
};
|
||||
static const RzCmdDescHelp mark_move_help = {
|
||||
.summary = "Move a mark to new a location.",
|
||||
.args = mark_move_args,
|
||||
};
|
||||
|
||||
static const RzCmdDescArg mark_distance_args[] = {
|
||||
{
|
||||
.name = "regex_pattern",
|
||||
.type = RZ_CMD_ARG_TYPE_STRING,
|
||||
.flags = RZ_CMD_ARG_FLAG_LAST,
|
||||
|
||||
},
|
||||
{ 0 },
|
||||
};
|
||||
static const RzCmdDescHelp mark_distance_help = {
|
||||
.summary = "Distance in bytes to reach the starting of mark from the current offset.",
|
||||
.args = mark_distance_args,
|
||||
};
|
||||
|
||||
static const RzCmdDescHelp md_help = {
|
||||
.summary = "Describe mark.",
|
||||
};
|
||||
static const RzCmdDescDetailEntry mark_describe_Usage_space_example_detail_entries[] = {
|
||||
{ .text = "md", .arg_str = NULL, .comment = "Describe all marks containing the current offset" },
|
||||
{ .text = "md foo", .arg_str = NULL, .comment = "Describe the mark named 'foo' and show distance from current offset to reach 'foo' in bytes" },
|
||||
{ 0 },
|
||||
};
|
||||
static const RzCmdDescDetail mark_describe_details[] = {
|
||||
{ .name = "Usage example", .entries = mark_describe_Usage_space_example_detail_entries },
|
||||
{ 0 },
|
||||
};
|
||||
static const RzCmdDescArg mark_describe_args[] = {
|
||||
{
|
||||
.name = "name",
|
||||
.type = RZ_CMD_ARG_TYPE_FLAG,
|
||||
.optional = true,
|
||||
|
||||
},
|
||||
{ 0 },
|
||||
};
|
||||
static const RzCmdDescHelp mark_describe_help = {
|
||||
.summary = "Describe marks at the current offset or a named mark with distance from the offset.",
|
||||
.description = "Displays information about marks. Without arguments, it describes all marks that contain the current offset. With a <name> argument, it shows details of that mark and the distance in bytes from the current offset to reach the mark. If the mark begins after the current offset, it shows <name> - <bytes> (distance from current offset to start of the mark). If the mark ends before the current offset, it shows <name> + <bytes> (distance from current offset to the end of the mark).",
|
||||
.details = mark_describe_details,
|
||||
.args = mark_describe_args,
|
||||
};
|
||||
|
||||
static const RzCmdDescArg mark_range_args[] = {
|
||||
{
|
||||
.name = "size",
|
||||
.type = RZ_CMD_ARG_TYPE_RZNUM,
|
||||
.flags = RZ_CMD_ARG_FLAG_LAST,
|
||||
.optional = true,
|
||||
|
||||
},
|
||||
{ 0 },
|
||||
};
|
||||
static const RzCmdDescHelp mark_range_help = {
|
||||
.summary = "Show marks in the current block or in the next <size> bytes.",
|
||||
.args = mark_range_args,
|
||||
};
|
||||
|
||||
static const RzCmdDescHelp F_help = {
|
||||
.summary = "FLIRT signature management",
|
||||
};
|
||||
|
|
@ -23368,6 +23636,47 @@ RZ_IPI void rzshell_cmddescs_init(RzCore *core) {
|
|||
RzCmdDesc *flag_hexdump_cd = rz_cmd_desc_argv_new(core->rcmd, f_cd, "fx", rz_flag_hexdump_handler, &flag_hexdump_help);
|
||||
rz_warn_if_fail(flag_hexdump_cd);
|
||||
|
||||
RzCmdDesc *m_cd = rz_cmd_desc_group_new(core->rcmd, root_cd, "m", rz_mark_add_handler, &mark_add_help, &m_help);
|
||||
rz_warn_if_fail(m_cd);
|
||||
RzCmdDesc *mark_append_cd = rz_cmd_desc_argv_new(core->rcmd, m_cd, "m+", rz_mark_append_handler, &mark_append_help);
|
||||
rz_warn_if_fail(mark_append_cd);
|
||||
|
||||
RzCmdDesc *mark_remove_cd = rz_cmd_desc_argv_new(core->rcmd, m_cd, "m-", rz_mark_remove_handler, &mark_remove_help);
|
||||
rz_warn_if_fail(mark_remove_cd);
|
||||
|
||||
RzCmdDesc *mark_remove_all_cd = rz_cmd_desc_argv_new(core->rcmd, m_cd, "m-*", rz_mark_remove_all_handler, &mark_remove_all_help);
|
||||
rz_warn_if_fail(mark_remove_all_cd);
|
||||
|
||||
RzCmdDesc *mark_list_cd = rz_cmd_desc_argv_state_new(core->rcmd, m_cd, "ml", RZ_OUTPUT_MODE_STANDARD | RZ_OUTPUT_MODE_RIZIN | RZ_OUTPUT_MODE_QUIET | RZ_OUTPUT_MODE_JSON | RZ_OUTPUT_MODE_TABLE, rz_mark_list_handler, &mark_list_help);
|
||||
rz_warn_if_fail(mark_list_cd);
|
||||
|
||||
RzCmdDesc *mark_list_at_cd = rz_cmd_desc_argv_state_new(core->rcmd, m_cd, "ml.", RZ_OUTPUT_MODE_STANDARD | RZ_OUTPUT_MODE_RIZIN | RZ_OUTPUT_MODE_QUIET | RZ_OUTPUT_MODE_JSON | RZ_OUTPUT_MODE_TABLE, rz_mark_list_at_handler, &mark_list_at_help);
|
||||
rz_warn_if_fail(mark_list_at_cd);
|
||||
|
||||
RzCmdDesc *mark_color_cd = rz_cmd_desc_argv_new(core->rcmd, m_cd, "mc", rz_mark_color_handler, &mark_color_help);
|
||||
rz_warn_if_fail(mark_color_cd);
|
||||
|
||||
RzCmdDesc *mark_comment_cd = rz_cmd_desc_argv_new(core->rcmd, m_cd, "mC", rz_mark_comment_handler, &mark_comment_help);
|
||||
rz_warn_if_fail(mark_comment_cd);
|
||||
|
||||
RzCmdDesc *mark_rename_cd = rz_cmd_desc_argv_new(core->rcmd, m_cd, "mr", rz_mark_rename_handler, &mark_rename_help);
|
||||
rz_warn_if_fail(mark_rename_cd);
|
||||
|
||||
RzCmdDesc *mark_realname_cd = rz_cmd_desc_argv_new(core->rcmd, m_cd, "mN", rz_mark_realname_handler, &mark_realname_help);
|
||||
rz_warn_if_fail(mark_realname_cd);
|
||||
|
||||
RzCmdDesc *mark_move_cd = rz_cmd_desc_argv_new(core->rcmd, m_cd, "mm", rz_mark_move_handler, &mark_move_help);
|
||||
rz_warn_if_fail(mark_move_cd);
|
||||
|
||||
RzCmdDesc *mark_distance_cd = rz_cmd_desc_argv_new(core->rcmd, m_cd, "mf", rz_mark_distance_handler, &mark_distance_help);
|
||||
rz_warn_if_fail(mark_distance_cd);
|
||||
|
||||
RzCmdDesc *md_cd = rz_cmd_desc_group_state_new(core->rcmd, m_cd, "md", RZ_OUTPUT_MODE_STANDARD | RZ_OUTPUT_MODE_TABLE | RZ_OUTPUT_MODE_JSON, rz_mark_describe_handler, &mark_describe_help, &md_help);
|
||||
rz_warn_if_fail(md_cd);
|
||||
|
||||
RzCmdDesc *mark_range_cd = rz_cmd_desc_argv_state_new(core->rcmd, m_cd, "mi", RZ_OUTPUT_MODE_STANDARD | RZ_OUTPUT_MODE_RIZIN | RZ_OUTPUT_MODE_QUIET | RZ_OUTPUT_MODE_JSON | RZ_OUTPUT_MODE_TABLE, rz_mark_range_handler, &mark_range_help);
|
||||
rz_warn_if_fail(mark_range_cd);
|
||||
|
||||
RzCmdDesc *F_cd = rz_cmd_desc_group_new(core->rcmd, root_cd, "F", NULL, NULL, &F_help);
|
||||
rz_warn_if_fail(F_cd);
|
||||
RzCmdDesc *flirt_create_cd = rz_cmd_desc_argv_new(core->rcmd, F_cd, "Fc", rz_flirt_create_handler, &flirt_create_help);
|
||||
|
|
|
|||
|
|
@ -1603,6 +1603,34 @@ RZ_IPI RzCmdStatus rz_flag_zone_around_handler(RzCore *core, int argc, const cha
|
|||
RZ_IPI RzCmdStatus rz_flag_zone_list_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state);
|
||||
// "fx"
|
||||
RZ_IPI RzCmdStatus rz_flag_hexdump_handler(RzCore *core, int argc, const char **argv);
|
||||
// "m"
|
||||
RZ_IPI RzCmdStatus rz_mark_add_handler(RzCore *core, int argc, const char **argv);
|
||||
// "m+"
|
||||
RZ_IPI RzCmdStatus rz_mark_append_handler(RzCore *core, int argc, const char **argv);
|
||||
// "m-"
|
||||
RZ_IPI RzCmdStatus rz_mark_remove_handler(RzCore *core, int argc, const char **argv);
|
||||
// "m-*"
|
||||
RZ_IPI RzCmdStatus rz_mark_remove_all_handler(RzCore *core, int argc, const char **argv);
|
||||
// "ml"
|
||||
RZ_IPI RzCmdStatus rz_mark_list_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state);
|
||||
// "ml."
|
||||
RZ_IPI RzCmdStatus rz_mark_list_at_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state);
|
||||
// "mc"
|
||||
RZ_IPI RzCmdStatus rz_mark_color_handler(RzCore *core, int argc, const char **argv);
|
||||
// "mC"
|
||||
RZ_IPI RzCmdStatus rz_mark_comment_handler(RzCore *core, int argc, const char **argv);
|
||||
// "mr"
|
||||
RZ_IPI RzCmdStatus rz_mark_rename_handler(RzCore *core, int argc, const char **argv);
|
||||
// "mN"
|
||||
RZ_IPI RzCmdStatus rz_mark_realname_handler(RzCore *core, int argc, const char **argv);
|
||||
// "mm"
|
||||
RZ_IPI RzCmdStatus rz_mark_move_handler(RzCore *core, int argc, const char **argv);
|
||||
// "mf"
|
||||
RZ_IPI RzCmdStatus rz_mark_distance_handler(RzCore *core, int argc, const char **argv);
|
||||
// "md"
|
||||
RZ_IPI RzCmdStatus rz_mark_describe_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state);
|
||||
// "mi"
|
||||
RZ_IPI RzCmdStatus rz_mark_range_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state);
|
||||
// "Fc"
|
||||
RZ_IPI RzCmdStatus rz_flirt_create_handler(RzCore *core, int argc, const char **argv);
|
||||
// "Fd"
|
||||
|
|
|
|||
|
|
@ -235,6 +235,9 @@ commands:
|
|||
- name: f
|
||||
summary: Manage flags
|
||||
subcommands: cmd_flag
|
||||
- name: m
|
||||
summary: Manage marks
|
||||
subcommands: cmd_mark
|
||||
- name: F
|
||||
summary: FLIRT signature management
|
||||
subcommands: cmd_flirt
|
||||
|
|
|
|||
168
librz/core/cmd_descs/cmd_mark.yaml
Normal file
168
librz/core/cmd_descs/cmd_mark.yaml
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
# SPDX-FileCopyrightText: 2025 PremadeS <emadsohail001@gmail.com>
|
||||
# SPDX-License-Identifier: LGPL-3.0-only
|
||||
---
|
||||
name: cmd_mark
|
||||
commands:
|
||||
- name: m
|
||||
cname: mark_add
|
||||
summary: Adds a mark if there are no existing marks.
|
||||
description: >
|
||||
Adds the mark to the current offset only if
|
||||
no mark covers this offset already
|
||||
args:
|
||||
- name: name
|
||||
type: RZ_CMD_ARG_TYPE_STRING
|
||||
- name: end
|
||||
type: RZ_CMD_ARG_TYPE_NUM
|
||||
- name: comment
|
||||
type: RZ_CMD_ARG_TYPE_STRING
|
||||
optional: true
|
||||
- name: m+
|
||||
cname: mark_append
|
||||
summary: Add mark.
|
||||
description: >
|
||||
Adds a mark at the current offset, replacing any existing mark with the same name.
|
||||
args:
|
||||
- name: name
|
||||
type: RZ_CMD_ARG_TYPE_STRING
|
||||
- name: end
|
||||
type: RZ_CMD_ARG_TYPE_NUM
|
||||
- name: comment
|
||||
type: RZ_CMD_ARG_TYPE_STRING
|
||||
optional: true
|
||||
- name: m-
|
||||
cname: mark_remove
|
||||
summary: Remove mark.
|
||||
description: >
|
||||
Removes marks by name.
|
||||
If <regex_pattern> is given, only marks whose name matches the regex
|
||||
are removed. Otherwise, all marks covering the current offset are removed.
|
||||
args:
|
||||
- name: regex_pattern
|
||||
type: RZ_CMD_ARG_TYPE_STRING
|
||||
optional: true
|
||||
- name: m-*
|
||||
cname: mark_remove_all
|
||||
summary: Remove all marks.
|
||||
args: []
|
||||
- name: ml
|
||||
cname: mark_list
|
||||
summary: List all marks.
|
||||
type: RZ_CMD_DESC_TYPE_ARGV_STATE
|
||||
modes:
|
||||
- RZ_OUTPUT_MODE_STANDARD
|
||||
- RZ_OUTPUT_MODE_RIZIN
|
||||
- RZ_OUTPUT_MODE_QUIET
|
||||
- RZ_OUTPUT_MODE_JSON
|
||||
- RZ_OUTPUT_MODE_TABLE
|
||||
args: []
|
||||
- name: ml.
|
||||
cname: mark_list_at
|
||||
summary: List all marks containing the current offset.
|
||||
type: RZ_CMD_DESC_TYPE_ARGV_STATE
|
||||
modes:
|
||||
- RZ_OUTPUT_MODE_STANDARD
|
||||
- RZ_OUTPUT_MODE_RIZIN
|
||||
- RZ_OUTPUT_MODE_QUIET
|
||||
- RZ_OUTPUT_MODE_JSON
|
||||
- RZ_OUTPUT_MODE_TABLE
|
||||
args: []
|
||||
- name: mc
|
||||
cname: mark_color
|
||||
summary: Set a color for the given mark / Show the color for the given mark.
|
||||
args:
|
||||
- name: name
|
||||
type: RZ_CMD_ARG_TYPE_FLAG
|
||||
- name: color
|
||||
type: RZ_CMD_ARG_TYPE_STRING
|
||||
optional: true
|
||||
- name: mC
|
||||
cname: mark_comment
|
||||
summary: Set a comment for the given mark. If no comment is given, it shows the current one for the mark.
|
||||
args:
|
||||
- name: name
|
||||
type: RZ_CMD_ARG_TYPE_FLAG
|
||||
- name: comment
|
||||
type: RZ_CMD_ARG_TYPE_STRING
|
||||
optional: true
|
||||
- name: mr
|
||||
cname: mark_rename
|
||||
summary: Rename mark.
|
||||
args:
|
||||
- name: old
|
||||
type: RZ_CMD_ARG_TYPE_FLAG
|
||||
- name: new
|
||||
type: RZ_CMD_ARG_TYPE_STRING
|
||||
- name: mN
|
||||
cname: mark_realname
|
||||
summary: Show the realname of the mark / Set the realname of the mark.
|
||||
description: >
|
||||
Each mark has two identifiers.
|
||||
<name> is the unique, escaped identifier used internally by Rizin.
|
||||
<realname> is the original, unescaped name, kept as given by the user.
|
||||
args:
|
||||
- name: name
|
||||
type: RZ_CMD_ARG_TYPE_FLAG
|
||||
- name: realname
|
||||
type: RZ_CMD_ARG_TYPE_STRING
|
||||
optional: true
|
||||
- name: mm
|
||||
cname: mark_move
|
||||
summary: Move a mark to new a location.
|
||||
args:
|
||||
- name: name
|
||||
type: RZ_CMD_ARG_TYPE_FLAG
|
||||
- name: new_start
|
||||
type: RZ_CMD_ARG_TYPE_RZNUM
|
||||
- name: new_end
|
||||
type: RZ_CMD_ARG_TYPE_RZNUM
|
||||
- name: mf
|
||||
cname: mark_distance
|
||||
summary: Distance in bytes to reach the starting of mark from the current offset.
|
||||
args:
|
||||
- name: regex_pattern
|
||||
type: RZ_CMD_ARG_TYPE_STRING
|
||||
- name: md
|
||||
summary: Describe mark.
|
||||
subcommands:
|
||||
- name: md
|
||||
cname: mark_describe
|
||||
summary: Describe marks at the current offset or a named mark with distance from the offset.
|
||||
description: >
|
||||
Displays information about marks.
|
||||
Without arguments, it describes all marks that contain the current offset.
|
||||
With a <name> argument, it shows details of that mark and the distance in bytes
|
||||
from the current offset to reach the mark. If the mark begins after the
|
||||
current offset, it shows <name> - <bytes> (distance from current offset to
|
||||
start of the mark). If the mark ends before the current offset, it shows
|
||||
<name> + <bytes> (distance from current offset to the end of the mark).
|
||||
type: RZ_CMD_DESC_TYPE_ARGV_STATE
|
||||
modes:
|
||||
- RZ_OUTPUT_MODE_STANDARD
|
||||
- RZ_OUTPUT_MODE_TABLE
|
||||
- RZ_OUTPUT_MODE_JSON
|
||||
args:
|
||||
- name: name
|
||||
type: RZ_CMD_ARG_TYPE_FLAG
|
||||
optional: true
|
||||
details:
|
||||
- name: Usage example
|
||||
entries:
|
||||
- text: "md"
|
||||
comment: "Describe all marks containing the current offset"
|
||||
- text: "md foo"
|
||||
comment: "Describe the mark named 'foo' and show distance from current offset to reach 'foo' in bytes"
|
||||
- name: mi
|
||||
cname: mark_range
|
||||
summary: Show marks in the current block or in the next <size> bytes.
|
||||
type: RZ_CMD_DESC_TYPE_ARGV_STATE
|
||||
modes:
|
||||
- RZ_OUTPUT_MODE_STANDARD
|
||||
- RZ_OUTPUT_MODE_RIZIN
|
||||
- RZ_OUTPUT_MODE_QUIET
|
||||
- RZ_OUTPUT_MODE_JSON
|
||||
- RZ_OUTPUT_MODE_TABLE
|
||||
args:
|
||||
- name: size
|
||||
type: RZ_CMD_ARG_TYPE_RZNUM
|
||||
optional: true
|
||||
|
|
@ -3,6 +3,7 @@ cmd_descs_yaml = files(
|
|||
'cmd_alias.yaml',
|
||||
'cmd_analysis.yaml',
|
||||
'cmd_block.yaml',
|
||||
'cmd_mark.yaml',
|
||||
'cmd_cmp.yaml',
|
||||
'cmd_debug.yaml',
|
||||
'cmd_descs.yaml',
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
// SPDX-FileCopyrightText: 2009-2020 pancake <pancake@nopcode.org>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <rz_mark.h>
|
||||
#include <rz_util/rz_str.h>
|
||||
#include <rz_config.h>
|
||||
#include <rz_util/ht_sp.h>
|
||||
#include <rz_util/rz_regex.h>
|
||||
|
|
@ -955,10 +957,39 @@ RZ_API int rz_core_fgets(char *buf, int len, void *user) {
|
|||
return rz_str_ncpy(buf, ptr, len - 1);
|
||||
}
|
||||
|
||||
static void append_marks(RzStrBuf *sb, RzList /*<RzMarkItem *>*/ *list, ut64 addr, bool is_start) {
|
||||
RzListIter *it;
|
||||
RzMarkItem *bi;
|
||||
bool first = (rz_strbuf_length(sb) == 0);
|
||||
|
||||
rz_list_foreach (list, it, bi) {
|
||||
if ((is_start && addr == bi->from) || (!is_start && addr == bi->to)) {
|
||||
if (!first)
|
||||
rz_strbuf_append(sb, " ; ");
|
||||
rz_strbuf_appendf(sb, is_start ? "[s] %s [0x%" PFMT64x "-0x%" PFMT64x "]" : "[e] %s [0x%" PFMT64x "-0x%" PFMT64x "]",
|
||||
bi->name, bi->from, bi->to);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const char *rz_core_print_offname(void *p, ut64 addr) {
|
||||
RzCore *c = (RzCore *)p;
|
||||
RzFlagItem *item = rz_flag_get_i(c->flags, addr);
|
||||
return item ? item->name : NULL;
|
||||
RzStrBuf *sb = rz_strbuf_new("");
|
||||
RzList *all = rz_mark_get_all_off(c->marks, addr);
|
||||
|
||||
append_marks(sb, all, addr, true);
|
||||
append_marks(sb, all, addr, false);
|
||||
|
||||
rz_list_free(all);
|
||||
|
||||
if (rz_strbuf_length(sb) == 0) {
|
||||
RzFlagItem *item = rz_flag_get_i(c->flags, addr);
|
||||
if (item)
|
||||
rz_strbuf_set(sb, item->name);
|
||||
}
|
||||
|
||||
return rz_strbuf_drain(sb);
|
||||
}
|
||||
|
||||
static int rz_core_print_offsize(void *p, ut64 addr) {
|
||||
|
|
@ -1692,6 +1723,7 @@ RZ_API bool rz_core_init(RzCore *core) {
|
|||
core->io->ff = 1;
|
||||
core->search = rz_search_new(RZ_SEARCH_KEYWORD);
|
||||
core->flags = rz_flag_new();
|
||||
core->marks = rz_mark_new();
|
||||
core->graph = rz_agraph_new(rz_cons_canvas_new(1, 1));
|
||||
core->graph->need_reload_nodes = false;
|
||||
core->asmqjmps_size = RZ_CORE_ASMQJMPS_NUM;
|
||||
|
|
|
|||
|
|
@ -145,6 +145,10 @@ RZ_IPI void rz_core_debug_signal_print(RzDebug *dbg, RzCmdStateOutput *state);
|
|||
RZ_IPI RzCoreIOMapInfo *rz_core_io_map_info_new(RzCoreFile *cf, int perm_orig);
|
||||
RZ_IPI void rz_core_io_map_info_free(RzCoreIOMapInfo *info);
|
||||
|
||||
/* cmark.c */
|
||||
RZ_IPI void rz_core_mark_print(RzMark *b, RzCmdStateOutput *state);
|
||||
RZ_IPI void rz_core_mark_range_print(RzMark *b, RzCmdStateOutput *state, ut64 range_from, ut64 range_to);
|
||||
|
||||
/* cflag.c */
|
||||
RZ_IPI void rz_core_flag_print(RzFlag *f, RzCmdStateOutput *state);
|
||||
RZ_IPI void rz_core_flag_real_name_print(RzFlag *f, RzCmdStateOutput *state);
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ rz_core_sources = [
|
|||
'casm.c',
|
||||
'cautocmpl.c',
|
||||
'cbin.c',
|
||||
'cmark.c',
|
||||
'cbounds.c',
|
||||
'cconfig.c',
|
||||
'ccrypto.c',
|
||||
|
|
@ -75,6 +76,7 @@ rz_core_sources = [
|
|||
#'cmd/cmd_analysis.c',
|
||||
'cmd/cmd_api.c',
|
||||
'cmd/cmd_block.c',
|
||||
'cmd/cmd_mark.c',
|
||||
'cmd/cmd_cmp.c',
|
||||
#'cmd/cmd_debug.c',
|
||||
'cmd/cmd_egg.c',
|
||||
|
|
@ -162,6 +164,7 @@ rz_core_deps = [
|
|||
rz_debug_dep,
|
||||
rz_config_dep,
|
||||
rz_bin_dep,
|
||||
rz_mark_dep,
|
||||
platform_deps,
|
||||
dependency('rzgdb'),
|
||||
dependency('rzheap'),
|
||||
|
|
@ -203,6 +206,7 @@ modules += { 'rz_core': {
|
|||
'rz_lang',
|
||||
'rz_hash',
|
||||
'rz_flag',
|
||||
'rz_mark',
|
||||
'rz_egg',
|
||||
'rz_debug',
|
||||
'rz_crypto',
|
||||
|
|
|
|||
|
|
@ -701,6 +701,23 @@ RZ_API bool rz_project_migrate_v18_v19(RzProject *prj, RzSerializeResultInfo *re
|
|||
return true;
|
||||
}
|
||||
|
||||
// --
|
||||
// Migration 19 -> 20
|
||||
//
|
||||
// Changes from c6a7acf8492881938188fc97d782b6bf2957cce7:
|
||||
// Introduced /core/marks sub-db (Rzmark API)
|
||||
// This prepares the project schema for the new mark system.
|
||||
|
||||
RZ_API bool rz_project_migrate_v19_v20(RzProject *prj, RzSerializeResultInfo *res) {
|
||||
Sdb *core_db;
|
||||
RZ_SERIALIZE_SUB(prj, core_db, res, "core", return false;);
|
||||
Sdb *marks_db = sdb_ns(core_db, "marks", 1);
|
||||
if (!marks_db) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool (*const migrations[])(RzProject *prj, RzSerializeResultInfo *res) = {
|
||||
rz_project_migrate_v1_v2,
|
||||
rz_project_migrate_v2_v3,
|
||||
|
|
@ -720,6 +737,7 @@ static bool (*const migrations[])(RzProject *prj, RzSerializeResultInfo *res) =
|
|||
rz_project_migrate_v16_v17,
|
||||
rz_project_migrate_v17_v18,
|
||||
rz_project_migrate_v18_v19,
|
||||
rz_project_migrate_v19_v20,
|
||||
};
|
||||
|
||||
/// Migrate the given project to the current version in-place
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
* /
|
||||
* /config => see config.c
|
||||
* /flags => see flag.c
|
||||
* /marks => see mark.c
|
||||
* /analysis => see analysis.c
|
||||
* /file => see below
|
||||
* /seek => see serialize_core_seek.c
|
||||
|
|
@ -25,6 +26,7 @@ RZ_API void rz_serialize_core_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core,
|
|||
file_save(sdb_ns(db, "file", true), core, prj_file);
|
||||
rz_serialize_config_save(sdb_ns(db, "config", true), core->config);
|
||||
rz_serialize_flag_save(sdb_ns(db, "flags", true), core->flags);
|
||||
rz_serialize_mark_save(sdb_ns(db, "marks", true), core->marks);
|
||||
rz_serialize_analysis_save(sdb_ns(db, "analysis", true), core->analysis);
|
||||
rz_serialize_debug_save(sdb_ns(db, "debug", true), core->dbg);
|
||||
rz_serialize_core_seek_save(sdb_ns(db, "seek", true), core);
|
||||
|
|
@ -79,6 +81,7 @@ RZ_API bool rz_serialize_core_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core,
|
|||
}
|
||||
SUB("config", rz_serialize_config_load(subdb, core->config, config_exclude, res));
|
||||
SUB("flags", rz_serialize_flag_load(subdb, core->flags, res));
|
||||
SUB("marks", rz_serialize_mark_load(subdb, core->marks, res));
|
||||
SUB("analysis", rz_serialize_analysis_load(subdb, core->analysis, res));
|
||||
SUB("debug", rz_serialize_debug_load(subdb, core->dbg, res));
|
||||
SUB("seek", rz_serialize_core_seek_load(subdb, core, res));
|
||||
|
|
|
|||
|
|
@ -6,23 +6,23 @@
|
|||
RZ_IPI void rz_core_visual_mark_reset(RzCore *core) {
|
||||
size_t i;
|
||||
for (i = 0; i < UT8_MAX; i++) {
|
||||
core->marks[i] = UT64_MAX;
|
||||
core->vmarks[i] = UT64_MAX;
|
||||
}
|
||||
core->marks_init = true;
|
||||
core->vmarks_init = true;
|
||||
}
|
||||
|
||||
RZ_IPI bool rz_core_visual_mark_dump(RzCore *core) {
|
||||
size_t i;
|
||||
if (!core->marks_init) {
|
||||
if (!core->vmarks_init) {
|
||||
return false;
|
||||
}
|
||||
bool res = false;
|
||||
for (i = 0; i < UT8_MAX; i++) {
|
||||
if (core->marks[i] != UT64_MAX) {
|
||||
if (core->vmarks[i] != UT64_MAX) {
|
||||
if (i > ASCII_MAX) {
|
||||
rz_cons_printf("fV %zu 0x%" PFMT64x "\n", i - ASCII_MAX - 1, core->marks[i]);
|
||||
rz_cons_printf("fV %zu 0x%" PFMT64x "\n", i - ASCII_MAX - 1, core->vmarks[i]);
|
||||
} else {
|
||||
rz_cons_printf("fV %c 0x%" PFMT64x "\n", (char)i, core->marks[i]);
|
||||
rz_cons_printf("fV %c 0x%" PFMT64x "\n", (char)i, core->vmarks[i]);
|
||||
}
|
||||
res = true;
|
||||
}
|
||||
|
|
@ -31,17 +31,17 @@ RZ_IPI bool rz_core_visual_mark_dump(RzCore *core) {
|
|||
}
|
||||
|
||||
RZ_IPI void rz_core_visual_mark_set(RzCore *core, ut8 ch, ut64 addr) {
|
||||
if (!core->marks_init) {
|
||||
if (!core->vmarks_init) {
|
||||
rz_core_visual_mark_reset(core);
|
||||
}
|
||||
core->marks[ch] = addr;
|
||||
core->vmarks[ch] = addr;
|
||||
}
|
||||
|
||||
RZ_IPI void rz_core_visual_mark_del(RzCore *core, ut8 ch) {
|
||||
if (!core->marks_init) {
|
||||
if (!core->vmarks_init) {
|
||||
return;
|
||||
}
|
||||
core->marks[ch] = UT64_MAX;
|
||||
core->vmarks[ch] = UT64_MAX;
|
||||
}
|
||||
|
||||
RZ_IPI void rz_core_visual_mark(RzCore *core, ut8 ch) {
|
||||
|
|
@ -52,7 +52,7 @@ RZ_IPI void rz_core_visual_mark(RzCore *core, ut8 ch) {
|
|||
}
|
||||
|
||||
RZ_IPI void rz_core_visual_mark_seek(RzCore *core, ut8 ch) {
|
||||
if (core->marks_init && core->marks[ch] != UT64_MAX) {
|
||||
rz_core_seek(core, core->marks[ch], true);
|
||||
if (core->vmarks_init && core->vmarks[ch] != UT64_MAX) {
|
||||
rz_core_seek(core, core->vmarks[ch], true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ include_files = [
|
|||
'rz_list.h',
|
||||
'rz_magic.h',
|
||||
'rz_main.h',
|
||||
'rz_mark.h',
|
||||
'rz_parse.h',
|
||||
'rz_pdb.h',
|
||||
'rz_platform.h',
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include <rz_util/rz_annotated_code.h>
|
||||
#include <rz_heap_glibc.h>
|
||||
#include <rz_windows_heap.h>
|
||||
#include <rz_mark.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
|
@ -282,6 +283,7 @@ struct rz_core_t {
|
|||
RzLang *lang;
|
||||
RzDebug *dbg;
|
||||
RzFlag *flags;
|
||||
RzMark *marks;
|
||||
char *lastsearch; ///< Legacy search. Will be removed
|
||||
RzSearch *search; ///< Legacy search. Will be removed
|
||||
RzEgg *egg;
|
||||
|
|
@ -341,8 +343,8 @@ struct rz_core_t {
|
|||
RzList /*<char *>*/ *warnings_after;
|
||||
RzPath *sys_path; ///< pointer to RzPath, contains path prefix of the system
|
||||
|
||||
bool marks_init;
|
||||
ut64 marks[UT8_MAX + 1];
|
||||
bool vmarks_init;
|
||||
ut64 vmarks[UT8_MAX + 1];
|
||||
|
||||
RzMainCallback rz_main_rizin;
|
||||
// int (*rz_main_rizin)(int argc, char **argv);
|
||||
|
|
|
|||
86
librz/include/rz_mark.h
Normal file
86
librz/include/rz_mark.h
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
#ifndef RZ_MARK_H
|
||||
#define RZ_MARK_H
|
||||
|
||||
#include <rz_types.h>
|
||||
#include <rz_list.h>
|
||||
#include <rz_skiplist.h>
|
||||
#include <rz_util/ht_sp.h>
|
||||
#include <rz_util/rz_serialize.h>
|
||||
#include <sdb.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// NOTE: Marks are currently used for the Hex Widget only
|
||||
// but the API is kept generic in case of future reuse
|
||||
|
||||
RZ_LIB_VERSION_HEADER(rz_mark);
|
||||
|
||||
#define RZ_MARK_NAME_SIZE 512
|
||||
|
||||
typedef struct rz_mark_item_t {
|
||||
ut64 from; ///< inclusive starting address of mark
|
||||
ut64 to; ///< inclusive ending address of mark
|
||||
char *name; ///< unique name for each mark, escaped to avoid issues with rizin shell
|
||||
char *realname; ///< real name, without any escaping
|
||||
char *comment; ///< item comment
|
||||
char *color; ///< item color
|
||||
} RzMarkItem;
|
||||
|
||||
typedef struct rz_marks_at_offset_t {
|
||||
ut64 off;
|
||||
RzList /*<RzMarkItem *>*/ *marks;
|
||||
} RzMarksAtOffset;
|
||||
|
||||
typedef struct rz_mark_t {
|
||||
HtSP *ht_name; ///< name -> RzBookmarkItem*
|
||||
RzList /*<RzBookmarkItem *>*/ *items; ///< All bookmarks
|
||||
RzSkipList *by_off; ///< offset -> RzBookmarksAtOffset*
|
||||
} RzMark;
|
||||
|
||||
typedef bool (*RzMarkItemCb)(RzMarkItem *item, void *user);
|
||||
|
||||
typedef enum {
|
||||
RZ_MARK_MATCH_CONTAINS,
|
||||
RZ_MARK_MATCH_START,
|
||||
RZ_MARK_MATCH_END
|
||||
} RzMarkMatchMode;
|
||||
|
||||
#ifdef RZ_API
|
||||
RZ_API RZ_OWN RzMark *rz_mark_new(void);
|
||||
RZ_API void rz_mark_free(RZ_OWN RzMark *b);
|
||||
RZ_API RZ_OWN RzMarkItem *rz_mark_set(RZ_NONNULL RzMark *b, RZ_NONNULL const char *name, ut64 from, ut64 to);
|
||||
RZ_API RZ_BORROW RzMarkItem *rz_mark_get(RZ_NONNULL RzMark *b, RZ_NONNULL const char *name);
|
||||
RZ_API RZ_BORROW RzMarkItem *rz_mark_get_start(RZ_NONNULL RzMark *b, ut64 off);
|
||||
RZ_API RZ_BORROW RzMarkItem *rz_mark_get_end(RZ_NONNULL RzMark *b, ut64 off);
|
||||
RZ_API RZ_BORROW RzMarkItem *rz_mark_get_at(RZ_NONNULL RzMark *b, ut64 off);
|
||||
RZ_API RZ_OWN RzList /*<RzMarkItem *>*/ *rz_mark_get_all_off(RZ_NONNULL RzMark *b, ut64 off);
|
||||
RZ_API RZ_OWN RzList /*<RzMarkItem *>*/ *rz_mark_all_list(RZ_NONNULL RzMark *b);
|
||||
RZ_API RZ_BORROW const RzList /*<RzMarkItem *>*/ *rz_mark_get_list(RZ_NONNULL RzMark *b, ut64 off);
|
||||
RZ_API void rz_mark_item_free(RZ_OWN RzMarkItem *item);
|
||||
RZ_API void rz_mark_item_set_comment(RZ_NONNULL RzMarkItem *item, RZ_NULLABLE const char *comment);
|
||||
RZ_API void rz_mark_item_set_realname(RZ_NONNULL RzMarkItem *item, RZ_NONNULL const char *realname);
|
||||
RZ_API RZ_NULLABLE const char *rz_mark_item_set_color(RZ_NONNULL RzMarkItem *item, RZ_NONNULL const char *color);
|
||||
RZ_API int rz_mark_rename(RZ_NONNULL RzMark *b, RZ_NONNULL RzMarkItem *item, RZ_NONNULL const char *name);
|
||||
RZ_API bool rz_mark_starts_or_ends(RZ_NONNULL RzMark *b, ut64 from, ut64 to);
|
||||
RZ_API bool rz_mark_unset(RZ_NONNULL RzMark *b, RZ_NONNULL RzMarkItem *item);
|
||||
RZ_API bool rz_mark_unset_all_off(RZ_NONNULL RzMark *b, ut64 off);
|
||||
RZ_API void rz_mark_unset_all(RZ_NONNULL RzMark *b);
|
||||
RZ_API int rz_mark_unset_glob(RZ_NONNULL RzMark *b, RZ_NONNULL const char *glob);
|
||||
RZ_API int rz_mark_count(RZ_NONNULL RzMark *b, RZ_NONNULL const char *glob);
|
||||
RZ_API void rz_mark_foreach(RZ_NONNULL RzMark *b, RZ_NONNULL RzMarkItemCb cb, RZ_NULLABLE void *user);
|
||||
RZ_API void rz_mark_foreach_glob(RZ_NONNULL RzMark *b, RZ_NONNULL const char *glob, RZ_NONNULL RzMarkItemCb cb, RZ_NULLABLE void *user);
|
||||
|
||||
/* serialize */
|
||||
|
||||
RZ_API void rz_serialize_mark_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzMark *bm);
|
||||
RZ_API bool rz_serialize_mark_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzMark *bm, RZ_NULLABLE RzSerializeResultInfo *res);
|
||||
|
||||
#endif // RZ_API
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // RZ_MARK_H
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define RZ_PROJECT_VERSION 19
|
||||
#define RZ_PROJECT_VERSION 20
|
||||
|
||||
typedef Sdb RzProject;
|
||||
|
||||
|
|
|
|||
602
librz/mark/mark.c
Normal file
602
librz/mark/mark.c
Normal file
|
|
@ -0,0 +1,602 @@
|
|||
// SPDX-FileCopyrightText: 2025 PremadeS <emadsohail001@gmail.com>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <rz_types_base.h>
|
||||
#include <rz_util/rz_assert.h>
|
||||
#include <rz_mark.h>
|
||||
#include <rz_util.h>
|
||||
#include <rz_cons.h>
|
||||
|
||||
RZ_LIB_VERSION(rz_mark);
|
||||
|
||||
#define STRDUP_OR_NULL(s) (!RZ_STR_ISEMPTY(s) ? strdup(s) : NULL)
|
||||
|
||||
static void mark_skiplist_free(void *data) {
|
||||
RzMarksAtOffset *item = (RzMarksAtOffset *)data;
|
||||
rz_list_free(item->marks);
|
||||
free(data);
|
||||
}
|
||||
|
||||
static int mark_skiplist_cmp(const void *va, const void *vb, void *user) {
|
||||
const RzMarksAtOffset *a = (RzMarksAtOffset *)va, *b = (RzMarksAtOffset *)vb;
|
||||
if (a->off == b->off) {
|
||||
return 0;
|
||||
}
|
||||
return a->off < b->off ? -1 : 1;
|
||||
}
|
||||
|
||||
static void free_item_name(RzMarkItem *item) {
|
||||
if (item->name != item->realname) {
|
||||
free(item->name);
|
||||
}
|
||||
}
|
||||
|
||||
static void free_item_realname(RzMarkItem *item) {
|
||||
if (item->name != item->realname) {
|
||||
free(item->realname);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_name(RzMarkItem *item, char *name) {
|
||||
free_item_name(item);
|
||||
item->name = name;
|
||||
free_item_realname(item);
|
||||
item->realname = item->name;
|
||||
}
|
||||
|
||||
/* return the list of marks at the nearest position.
|
||||
dir == -1 -> result <= off
|
||||
dir == 0 -> result == off
|
||||
dir == 1 -> result >= off*/
|
||||
static RzMarksAtOffset *rz_mark_get_nearest_list(RzMark *b, ut64 off, int dir) {
|
||||
RzMarksAtOffset key = { .off = off };
|
||||
RzMarksAtOffset *marks = (dir >= 0)
|
||||
? rz_skiplist_get_geq(b->by_off, &key)
|
||||
: rz_skiplist_get_leq(b->by_off, &key);
|
||||
return (dir == 0 && marks && marks->off != off) ? NULL : marks;
|
||||
}
|
||||
|
||||
RZ_API RZ_OWN RzMark *rz_mark_new(void) {
|
||||
RzMark *b = RZ_NEW0(RzMark);
|
||||
if (!b) {
|
||||
return NULL;
|
||||
}
|
||||
b->ht_name = ht_sp_new(HT_STR_DUP, NULL, (HtSPFreeValue)rz_mark_item_free);
|
||||
b->by_off = rz_skiplist_new(mark_skiplist_free, mark_skiplist_cmp);
|
||||
return b;
|
||||
}
|
||||
|
||||
static char *filter_item_name(const char *name) {
|
||||
char *res = rz_str_dup(name);
|
||||
if (!res) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rz_str_trim(res);
|
||||
rz_name_filter(res, 0, true);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void remove_offsetmap(RzMark *b, RzMarkItem *item) {
|
||||
rz_return_if_fail(b && item);
|
||||
RzMarksAtOffset *marks = rz_mark_get_nearest_list(b, item->from, 0);
|
||||
if (marks) {
|
||||
rz_list_delete_data(marks->marks, item);
|
||||
if (rz_list_empty(marks->marks)) {
|
||||
rz_skiplist_delete(b->by_off, marks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static RzMarksAtOffset *marks_at_offset(RzMark *b, ut64 off) {
|
||||
RzMarksAtOffset *res = rz_mark_get_nearest_list(b, off, 0);
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
res = RZ_NEW(RzMarksAtOffset);
|
||||
if (!res) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
res->marks = rz_list_new();
|
||||
if (!res->marks) {
|
||||
free(res);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
res->off = off;
|
||||
rz_skiplist_insert(b->by_off, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool update_mark_item_range(RzMark *b, RzMarkItem *item, ut64 new_from, ut64 new_to,
|
||||
bool is_new, bool force) {
|
||||
rz_return_val_if_fail(b && item, false);
|
||||
|
||||
if (item->from != new_from || item->to != new_to || force) {
|
||||
if (!is_new) {
|
||||
remove_offsetmap(b, item);
|
||||
}
|
||||
|
||||
item->from = new_from;
|
||||
item->to = new_to;
|
||||
|
||||
RzMarksAtOffset *marksAtOffset = marks_at_offset(b, new_from);
|
||||
if (!marksAtOffset) {
|
||||
return false;
|
||||
}
|
||||
|
||||
rz_list_append(marksAtOffset->marks, item);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool update_mark_item_name(RzMark *b, RzMarkItem *item, const char *newname, bool force) {
|
||||
if (!b || !item || !newname) {
|
||||
return false;
|
||||
}
|
||||
if (!force && (item->name == newname || (RZ_STR_EQ(item->name, newname)))) {
|
||||
return false;
|
||||
}
|
||||
char *fname = filter_item_name(newname);
|
||||
if (!fname) {
|
||||
return false;
|
||||
}
|
||||
bool res = (item->name)
|
||||
? ht_sp_update_key(b->ht_name, item->name, fname)
|
||||
: ht_sp_insert(b->ht_name, fname, item);
|
||||
if (res) {
|
||||
set_name(item, fname);
|
||||
return true;
|
||||
}
|
||||
free(fname);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool mark_item_matches(RzMarkItem *bi, ut64 off, RzMarkMatchMode mode) {
|
||||
switch (mode) {
|
||||
case RZ_MARK_MATCH_CONTAINS:
|
||||
return RZ_BETWEEN(bi->from, off, bi->to);
|
||||
case RZ_MARK_MATCH_START:
|
||||
return (off == bi->from);
|
||||
case RZ_MARK_MATCH_END:
|
||||
return (off == bi->to);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void mark_iterate_matches(RzMark *b, ut64 off, RzMarkMatchMode mode,
|
||||
bool stop_on_first, RzList /*<RzMarkItem *>*/ *out) {
|
||||
RzMarksAtOffset *cur = rz_mark_get_nearest_list(b, off, -1);
|
||||
while (cur) {
|
||||
RzListIter *it;
|
||||
RzMarkItem *bi;
|
||||
|
||||
rz_list_foreach (cur->marks, it, bi) {
|
||||
if (!mark_item_matches(bi, off, mode)) {
|
||||
continue;
|
||||
}
|
||||
rz_list_append(out, bi);
|
||||
if (stop_on_first) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (cur->off == 0) {
|
||||
break;
|
||||
}
|
||||
cur = rz_mark_get_nearest_list(b, cur->off - 1, -1);
|
||||
}
|
||||
}
|
||||
|
||||
static RzMarkItem *mark_match(RzMark *b, ut64 off, RzMarkMatchMode mode) {
|
||||
rz_return_val_if_fail(b, NULL);
|
||||
|
||||
RzList *results = rz_list_newf(NULL);
|
||||
mark_iterate_matches(b, off, mode, true, results);
|
||||
RzMarkItem *ret = results->length ? rz_list_get_n(results, 0) : NULL;
|
||||
rz_list_free(results);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static RzList /*<RzMarkItem *>*/ *mark_match_all(RzMark *b, ut64 off, RzMarkMatchMode mode) {
|
||||
rz_return_val_if_fail(b, NULL);
|
||||
|
||||
RzList *results = rz_list_newf(NULL);
|
||||
mark_iterate_matches(b, off, mode, false, results);
|
||||
return results;
|
||||
}
|
||||
|
||||
static bool mark_starts_or_ends(RzMark *b, ut64 from, ut64 to) {
|
||||
if (!b || from > to) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RzList *all = rz_mark_all_list(b);
|
||||
if (!all) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RzListIter *it;
|
||||
RzMarkItem *bi;
|
||||
rz_list_foreach (all, it, bi) {
|
||||
if (RZ_BETWEEN(from, bi->from, to) ||
|
||||
RZ_BETWEEN(from, bi->to, to)) {
|
||||
rz_list_free(all);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
rz_list_free(all);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool append_bi(RzMarkItem *item, void *user) {
|
||||
rz_list_append((RzList *)user, item);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get all defined marks as a list.
|
||||
*
|
||||
* \param b The mark container.
|
||||
* \return A newly allocated list of all mark items (\p RzMarkItem*).
|
||||
*/
|
||||
RZ_API RZ_OWN RzList /*<RzMarkItem *>*/ *rz_mark_all_list(RZ_NONNULL RzMark *b) {
|
||||
RzList *ret = rz_list_new();
|
||||
if (!b || !ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
rz_mark_foreach(b, append_bi, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Check whether any mark starts or ends in the given range.
|
||||
*
|
||||
* \param b The mark container.
|
||||
* \param from Start of the range.
|
||||
* \param to End of the range.
|
||||
* \return True if a mark starts or ends in the given range, False otherwise.
|
||||
*/
|
||||
RZ_API bool rz_mark_starts_or_ends(RZ_NONNULL RzMark *b, ut64 from, ut64 to) {
|
||||
return mark_starts_or_ends(b, from, to);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get all mark items that contain a given offset.
|
||||
*
|
||||
* \param b The mark container.
|
||||
* \param off The offset to query.
|
||||
* \return A list of all matching mark items. NULL if none are found.
|
||||
*/
|
||||
RZ_API RZ_OWN RzList /*<RzMarkItem *>*/ *rz_mark_get_all_off(RZ_NONNULL RzMark *b, ut64 off) {
|
||||
return mark_match_all(b, off, RZ_MARK_MATCH_CONTAINS);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the last mark item that contains the given offset.
|
||||
*
|
||||
* \param b The mark container.
|
||||
* \param off The offset to query.
|
||||
* \return The matching mark item. NULL if none is found.
|
||||
*/
|
||||
RZ_API RZ_BORROW RzMarkItem *rz_mark_get_at(RZ_NONNULL RzMark *b, ut64 off) {
|
||||
return mark_match(b, off, RZ_MARK_MATCH_CONTAINS);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the mark item that starts at a given offset.
|
||||
*
|
||||
* \param b The mark container.
|
||||
* \param off The offset to query.
|
||||
* \return The mark item starting at \p off. NULL if none is found.
|
||||
*/
|
||||
RZ_API RZ_BORROW RzMarkItem *rz_mark_get_start(RZ_NONNULL RzMark *b, ut64 off) {
|
||||
return mark_match(b, off, RZ_MARK_MATCH_START);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the mark item that ends at a given offset.
|
||||
*
|
||||
* \param b The mark container.
|
||||
* \param off The offset to query.
|
||||
* \return The mark item ending at \p off. NULL if none is found.
|
||||
*/
|
||||
RZ_API RZ_BORROW RzMarkItem *rz_mark_get_end(RZ_NONNULL RzMark *b, ut64 off) {
|
||||
return mark_match(b, off, RZ_MARK_MATCH_END);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the mark item with the given name.
|
||||
*
|
||||
* \param b The mark container.
|
||||
* \param name The name of the mark to retrieve.
|
||||
* \return The matching mark item. NULL if not found.
|
||||
*/
|
||||
RZ_API RZ_BORROW RzMarkItem *rz_mark_get(RZ_NONNULL RzMark *b, RZ_NONNULL const char *name) {
|
||||
rz_return_val_if_fail(b, NULL);
|
||||
RzMarkItem *r = ht_sp_find(b->ht_name, name, NULL);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get all mark items containing with a given offset.
|
||||
*
|
||||
* \param b The mark container.
|
||||
* \param off The offset to query.
|
||||
* \return A list of mark items. NULL if none are found.
|
||||
*/
|
||||
RZ_API RZ_BORROW const RzList /*<RzMarkItem *>*/ *rz_mark_get_list(RZ_NONNULL RzMark *b, ut64 off) {
|
||||
const RzMarksAtOffset *item = rz_mark_get_nearest_list(b, off, 0);
|
||||
return item ? item->marks : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Create or update a mark item.
|
||||
*
|
||||
* If a mark with the given \p name already exists, it is updated.
|
||||
* Otherwise, a new one is created.
|
||||
*
|
||||
* \param b The mark container.
|
||||
* \param name The name of the mark.
|
||||
* \param from Start offset.
|
||||
* \param to End offset.
|
||||
* \return The created or updated mark item. NULL on error.
|
||||
*/
|
||||
RZ_API RZ_OWN RzMarkItem *rz_mark_set(RZ_NONNULL RzMark *b, RZ_NONNULL const char *name, ut64 from, ut64 to) {
|
||||
rz_return_val_if_fail(b && name && *name, NULL);
|
||||
|
||||
bool is_new = false;
|
||||
char *itemname = filter_item_name(name);
|
||||
if (!itemname) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RzMarkItem *item = rz_mark_get(b, itemname);
|
||||
free(itemname);
|
||||
if (item && item->from == from && item->to == to) {
|
||||
return item;
|
||||
}
|
||||
|
||||
if (!item) {
|
||||
item = RZ_NEW0(RzMarkItem);
|
||||
if (!item) {
|
||||
goto err;
|
||||
}
|
||||
is_new = true;
|
||||
}
|
||||
|
||||
update_mark_item_range(b, item, from, to, is_new, true);
|
||||
update_mark_item_name(b, item, name, true);
|
||||
return item;
|
||||
err:
|
||||
rz_mark_item_free(item);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Free a mark container and all its associated data.
|
||||
*
|
||||
* \param b The mark container to free.
|
||||
*/
|
||||
RZ_API void rz_mark_free(RZ_OWN RzMark *b) {
|
||||
rz_warn_if_fail(b);
|
||||
rz_skiplist_free(b->by_off);
|
||||
ht_sp_free(b->ht_name);
|
||||
free(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Free a mark item.
|
||||
*
|
||||
* \param item The mark item to free.
|
||||
*/
|
||||
RZ_API void rz_mark_item_free(RZ_OWN RzMarkItem *item) {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
free(item->color);
|
||||
free(item->comment);
|
||||
|
||||
free_item_name(item);
|
||||
free(item->realname);
|
||||
free(item);
|
||||
}
|
||||
|
||||
#define FOREACH_BODY(condition) \
|
||||
RzSkipListNode *it, *tmp; \
|
||||
RzMarksAtOffset *marks_at; \
|
||||
RzListIter *it2, *tmp2; \
|
||||
RzMarkItem *bi; \
|
||||
rz_skiplist_foreach_safe(b->by_off, it, tmp, marks_at) { \
|
||||
if (marks_at) { \
|
||||
rz_list_foreach_safe (marks_at->marks, it2, tmp2, bi) { \
|
||||
if (condition) { \
|
||||
if (!cb(bi, user)) { \
|
||||
return; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Iterate through all mark items.
|
||||
*
|
||||
* \param b The mark container.
|
||||
* \param cb Callback function to invoke for each mark item.
|
||||
* \param user User-provided data passed to the callback.
|
||||
*/
|
||||
RZ_API void rz_mark_foreach(RZ_NONNULL RzMark *b, RZ_NONNULL RzMarkItemCb cb, RZ_NULLABLE void *user) {
|
||||
FOREACH_BODY(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Iterate through all mark items matching a glob pattern.
|
||||
*
|
||||
* \param b The mark container.
|
||||
* \param glob Glob/Regex pattern to filter mark names.
|
||||
* \param cb Callback function to invoke for each matching mark item.
|
||||
* \param user User-provided data passed to the callback.
|
||||
*/
|
||||
RZ_API void rz_mark_foreach_glob(RZ_NONNULL RzMark *b, RZ_NONNULL const char *glob, RZ_NONNULL RzMarkItemCb cb, RZ_NULLABLE void *user) {
|
||||
FOREACH_BODY(!glob || rz_str_glob(bi->name, glob));
|
||||
}
|
||||
|
||||
struct unset_off_foreach_t {
|
||||
RzMark *b;
|
||||
ut64 offset;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Unset (remove) a specific mark item.
|
||||
*
|
||||
* The item is freed.
|
||||
*
|
||||
* \param b The mark container.
|
||||
* \param item The mark item to unset.
|
||||
* \return True if successfully unset. False otherwise.
|
||||
*/
|
||||
RZ_API bool rz_mark_unset(RZ_NONNULL RzMark *b, RZ_NONNULL RzMarkItem *item) {
|
||||
rz_return_val_if_fail(b && item, false);
|
||||
remove_offsetmap(b, item);
|
||||
ht_sp_delete(b->ht_name, item->name);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool unset_off_foreach(void *user, const char *k, const void *v) {
|
||||
struct unset_off_foreach_t *u = (struct unset_off_foreach_t *)user;
|
||||
RzMarkItem *bi = (RzMarkItem *)v;
|
||||
|
||||
// check if any marked range contains the current offset
|
||||
if (RZ_BETWEEN(bi->from, u->offset, bi->to)) {
|
||||
rz_mark_unset(u->b, bi);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Unset all marks found at a specific offset.
|
||||
*
|
||||
* \param b The mark container.
|
||||
* \param off The offset to clear marks from.
|
||||
* \return True if at least one mark was unset. False otherwise.
|
||||
*/
|
||||
RZ_API bool rz_mark_unset_all_off(RZ_NONNULL RzMark *b, ut64 off) {
|
||||
rz_return_val_if_fail(b, false);
|
||||
struct unset_off_foreach_t u = { b, off };
|
||||
ht_sp_foreach(b->ht_name, unset_off_foreach, &u);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct unset_foreach_t {
|
||||
RzMark *b;
|
||||
int n;
|
||||
};
|
||||
|
||||
static bool unset_foreach(RzMarkItem *bi, void *user) {
|
||||
struct unset_foreach_t *u = (struct unset_foreach_t *)user;
|
||||
rz_mark_unset(u->b, bi);
|
||||
u->n++;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Unset all marks matching a glob pattern.
|
||||
*
|
||||
* \param b The mark container.
|
||||
* \param glob Glob/Regex pattern to filter mark names.
|
||||
* \return The number of marks unset. -1 on error.
|
||||
*/
|
||||
RZ_API int rz_mark_unset_glob(RZ_NONNULL RzMark *b, RZ_NONNULL const char *glob) {
|
||||
rz_return_val_if_fail(b, -1);
|
||||
struct unset_foreach_t u = { .b = b, .n = 0 };
|
||||
rz_mark_foreach_glob(b, glob, unset_foreach, &u);
|
||||
return u.n;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Unset all marks in the container.
|
||||
*
|
||||
* \param b The mark container.
|
||||
*/
|
||||
RZ_API void rz_mark_unset_all(RZ_NONNULL RzMark *b) {
|
||||
rz_return_if_fail(b);
|
||||
ht_sp_free(b->ht_name);
|
||||
b->ht_name = ht_sp_new(HT_STR_DUP, NULL, (HtSPFreeValue)rz_mark_item_free);
|
||||
rz_skiplist_purge(b->by_off);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set or remove the color of a mark item.
|
||||
*
|
||||
* \param item The mark item.
|
||||
* \param color The new color string, or NULL to remove.
|
||||
* \return The updated color string. NULL if unset.
|
||||
*/
|
||||
RZ_API RZ_NULLABLE const char *rz_mark_item_set_color(RZ_NONNULL RzMarkItem *item, RZ_NONNULL const char *color) {
|
||||
rz_return_val_if_fail(item, NULL);
|
||||
free(item->color);
|
||||
item->color = STRDUP_OR_NULL(color);
|
||||
return item->color;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set or remove the comment of a mark item.
|
||||
*
|
||||
* \param item The mark item.
|
||||
* \param comment The new comment string.
|
||||
*/
|
||||
RZ_API void rz_mark_item_set_comment(RZ_NONNULL RzMarkItem *item, RZ_NULLABLE const char *comment) {
|
||||
rz_return_if_fail(item);
|
||||
free(item->comment);
|
||||
item->comment = RZ_STR_ISEMPTY(comment) ? NULL : rz_str_dup(comment);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Rename a mark item.
|
||||
*
|
||||
* \param b The mark container.
|
||||
* \param item The mark item to rename.
|
||||
* \param name The new name.
|
||||
* \return True if renamed successfully. False otherwise.
|
||||
*/
|
||||
RZ_API int rz_mark_rename(RZ_NONNULL RzMark *b, RZ_NONNULL RzMarkItem *item, RZ_NONNULL const char *name) {
|
||||
rz_return_val_if_fail(b && item && name && *name, false);
|
||||
return update_mark_item_name(b, item, name, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set or remove the real name of a mark item.
|
||||
*
|
||||
* \param item The mark item.
|
||||
* \param realname The new real name.
|
||||
*/
|
||||
RZ_API void rz_mark_item_set_realname(RZ_NONNULL RzMarkItem *item, RZ_NONNULL const char *realname) {
|
||||
rz_return_if_fail(item);
|
||||
free_item_realname(item);
|
||||
item->realname = RZ_STR_ISEMPTY(realname) ? NULL : rz_str_dup(realname);
|
||||
}
|
||||
|
||||
static bool mark_count_foreach(RzMarkItem *fi, void *user) {
|
||||
int *count = (int *)user;
|
||||
(*count)++;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Count mark items matching a glob pattern.
|
||||
*
|
||||
* \param b The mark container.
|
||||
* \param glob Glob/Regex pattern to filter mark names.
|
||||
* \return The number of matching marks. -1 on error.
|
||||
*/
|
||||
RZ_API int rz_mark_count(RZ_NONNULL RzMark *b, RZ_NONNULL const char *glob) {
|
||||
int count = 0;
|
||||
rz_return_val_if_fail(b, -1);
|
||||
rz_mark_foreach_glob(b, glob, mark_count_foreach, &count);
|
||||
return count;
|
||||
}
|
||||
30
librz/mark/meson.build
Normal file
30
librz/mark/meson.build
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
mark_sources = [
|
||||
'mark.c',
|
||||
'serialize_mark.c'
|
||||
]
|
||||
|
||||
rz_mark = library('rz_mark', mark_sources,
|
||||
include_directories: [platform_inc],
|
||||
dependencies: [
|
||||
rz_util_dep
|
||||
],
|
||||
install: true,
|
||||
implicit_include_directories: false,
|
||||
install_rpath: rpath_lib,
|
||||
soversion: rizin_libversion,
|
||||
version: rizin_version,
|
||||
name_suffix: lib_name_suffix,
|
||||
name_prefix: lib_name_prefix,
|
||||
)
|
||||
|
||||
rz_mark_dep = declare_dependency(
|
||||
link_with: rz_mark,
|
||||
include_directories: [platform_inc]
|
||||
)
|
||||
|
||||
meson.override_dependency('rz_mark', rz_mark_dep)
|
||||
|
||||
modules += { 'rz_mark': {
|
||||
'target': rz_mark,
|
||||
'dependencies': ['rz_util']
|
||||
}}
|
||||
186
librz/mark/serialize_mark.c
Normal file
186
librz/mark/serialize_mark.c
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
// SPDX-FileCopyrightText: 2025 PremadeS <emadsohail001@gmail.com>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <rz_util/rz_assert.h>
|
||||
#include <rz_types.h>
|
||||
#include <rz_util.h>
|
||||
#include <rz_mark.h>
|
||||
|
||||
/*
|
||||
* SDB Format:
|
||||
*
|
||||
* /marks
|
||||
* <mark name>={"realname":<str>,"from":<uint>,"to":<uint>,"color":<str>,"comment":<str>,}
|
||||
*/
|
||||
|
||||
static bool mark_save_cb(RzMarkItem *bm, void *user) {
|
||||
Sdb *db = user;
|
||||
PJ *j = pj_new();
|
||||
if (!j) {
|
||||
return false;
|
||||
}
|
||||
pj_o(j);
|
||||
if (bm->realname) {
|
||||
pj_ks(j, "realname", bm->realname);
|
||||
}
|
||||
pj_kn(j, "from", bm->from);
|
||||
pj_kn(j, "to", bm->to);
|
||||
if (bm->color) {
|
||||
pj_ks(j, "color", bm->color);
|
||||
}
|
||||
if (bm->comment) {
|
||||
pj_ks(j, "comment", bm->comment);
|
||||
}
|
||||
pj_end(j);
|
||||
sdb_set(db, bm->name, pj_string(j));
|
||||
pj_free(j);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Save marks to a database.
|
||||
*
|
||||
* Serializes all marks from the given container \p bm into the namespace
|
||||
* "marks" within the database \p db.
|
||||
*
|
||||
* \param db Database to save into.
|
||||
* \param bm Mark container to serialize.
|
||||
*/
|
||||
RZ_API void rz_serialize_mark_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzMark *bm) {
|
||||
rz_return_if_fail(db && bm);
|
||||
rz_mark_foreach(bm, mark_save_cb, sdb_ns(db, "marks", true));
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
BOOKMARK_FIELD_REALNAME,
|
||||
BOOKMARK_FIELD_FROM,
|
||||
BOOKMARK_FIELD_TO,
|
||||
BOOKMARK_FIELD_COLOR,
|
||||
BOOKMARK_FIELD_COMMENT,
|
||||
} MarkField;
|
||||
|
||||
typedef struct {
|
||||
RzMark *mark;
|
||||
RzKeyParser *parser;
|
||||
} MarkLoadCtx;
|
||||
|
||||
static bool marks_load_cb(void *user, const SdbKv *kv) {
|
||||
MarkLoadCtx *ctx = user;
|
||||
|
||||
char *json_str = sdbkv_dup_value(kv);
|
||||
if (!json_str) {
|
||||
return true;
|
||||
}
|
||||
RzJson *json = rz_json_parse(json_str);
|
||||
if (!json || json->type != RZ_JSON_OBJECT) {
|
||||
free(json_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
RzMarkItem proto = { 0 };
|
||||
bool offset_set = false;
|
||||
bool size_set = false;
|
||||
|
||||
RZ_KEY_PARSER_JSON(ctx->parser, json, child, {
|
||||
case BOOKMARK_FIELD_REALNAME:
|
||||
if (child->type != RZ_JSON_STRING) {
|
||||
break;
|
||||
}
|
||||
proto.realname = (char *)child->str_value;
|
||||
break;
|
||||
case BOOKMARK_FIELD_FROM:
|
||||
if (child->type != RZ_JSON_INTEGER) {
|
||||
break;
|
||||
}
|
||||
proto.from = child->num.u_value;
|
||||
offset_set = true;
|
||||
break;
|
||||
case BOOKMARK_FIELD_TO:
|
||||
if (child->type != RZ_JSON_INTEGER) {
|
||||
break;
|
||||
}
|
||||
proto.to = child->num.u_value;
|
||||
size_set = true;
|
||||
break;
|
||||
case BOOKMARK_FIELD_COLOR:
|
||||
if (child->type != RZ_JSON_STRING) {
|
||||
break;
|
||||
}
|
||||
proto.color = (char *)child->str_value;
|
||||
break;
|
||||
case BOOKMARK_FIELD_COMMENT:
|
||||
if (child->type != RZ_JSON_STRING) {
|
||||
break;
|
||||
}
|
||||
proto.comment = (char *)child->str_value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
});
|
||||
|
||||
bool res = true;
|
||||
if (!offset_set || !size_set) {
|
||||
res = false;
|
||||
goto beach;
|
||||
}
|
||||
|
||||
RzMarkItem *item = rz_mark_set(ctx->mark, sdbkv_key(kv), proto.from, proto.to);
|
||||
if (proto.realname) {
|
||||
rz_mark_item_set_realname(item, proto.realname);
|
||||
}
|
||||
if (proto.color) {
|
||||
rz_mark_item_set_color(item, proto.color);
|
||||
}
|
||||
if (proto.comment) {
|
||||
rz_mark_item_set_comment(item, proto.comment);
|
||||
}
|
||||
beach:
|
||||
rz_json_free(json);
|
||||
free(json_str);
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool load_marks(RZ_NONNULL Sdb *marks_db, RZ_NONNULL RzMark *bm) {
|
||||
MarkLoadCtx ctx = { bm, rz_key_parser_new() };
|
||||
if (!ctx.parser) {
|
||||
return false;
|
||||
}
|
||||
rz_key_parser_add(ctx.parser, "realname", BOOKMARK_FIELD_REALNAME);
|
||||
rz_key_parser_add(ctx.parser, "from", BOOKMARK_FIELD_FROM);
|
||||
rz_key_parser_add(ctx.parser, "to", BOOKMARK_FIELD_TO);
|
||||
rz_key_parser_add(ctx.parser, "color", BOOKMARK_FIELD_COLOR);
|
||||
rz_key_parser_add(ctx.parser, "comment", BOOKMARK_FIELD_COMMENT);
|
||||
bool r = sdb_foreach(marks_db, marks_load_cb, &ctx);
|
||||
rz_key_parser_free(ctx.parser);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Load marks from a database.
|
||||
*
|
||||
* Clears all existing marks in \p bm and loads marks from the namespace
|
||||
* "marks" within the database \p db.
|
||||
*
|
||||
* \param db Database to load from.
|
||||
* \param bm Mark container to populate.
|
||||
* \param res Optional result info structure for reporting errors, may be NULL.
|
||||
* \return True if loading succeeded. False if an error occurred.
|
||||
*
|
||||
* \note If no "marks" namespace exists, this is treated as an empty set
|
||||
* of marks (not an error).
|
||||
*/
|
||||
RZ_API bool rz_serialize_mark_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzMark *bm, RZ_NULLABLE RzSerializeResultInfo *res) {
|
||||
rz_return_val_if_fail(db && bm, false);
|
||||
rz_mark_unset_all(bm);
|
||||
|
||||
Sdb *marks_db = sdb_ns(db, "marks", false);
|
||||
if (!marks_db) {
|
||||
// Old project, no marks saved -> treat as empty, not an error
|
||||
return true;
|
||||
}
|
||||
if (!load_marks(marks_db, bm)) {
|
||||
RZ_SERIALIZE_ERR(res, "failed to parse mark json");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ subdir('syscall')
|
|||
subdir('magic')
|
||||
subdir('search')
|
||||
subdir('flag')
|
||||
subdir('mark')
|
||||
subdir('reg')
|
||||
subdir('type')
|
||||
subdir('bin')
|
||||
|
|
|
|||
|
|
@ -751,6 +751,7 @@ if host_machine.system() == 'windows'
|
|||
build_root / 'librz' / 'debug',
|
||||
build_root / 'librz' / 'egg',
|
||||
build_root / 'librz' / 'flag',
|
||||
build_root / 'librz' / 'mark',
|
||||
build_root / 'librz' / 'hash',
|
||||
build_root / 'librz' / 'io',
|
||||
build_root / 'librz' / 'lang',
|
||||
|
|
|
|||
255
test/db/cmd/cmd_mark
Normal file
255
test/db/cmd/cmd_mark
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
NAME=m command with space
|
||||
FILE==
|
||||
CMDS=<<EOF
|
||||
m foobar 0x2000 @ 0x1000
|
||||
ml
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
[0x00001000 - 0x00002000] foobar
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=no marks
|
||||
FILE==
|
||||
CMDS=m
|
||||
EXPECT=
|
||||
RUN
|
||||
|
||||
NAME=mr
|
||||
FILE=malloc://1024
|
||||
CMDS=<<EOF
|
||||
m foo 0x20
|
||||
mr foo bar
|
||||
ml
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
[0x00000000 - 0x00000020] bar
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=mm
|
||||
FILE=malloc://1024
|
||||
CMDS=<<EOF
|
||||
m foo 0x20
|
||||
mm foo 0x10 0x30
|
||||
ml
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
[0x00000010 - 0x00000030] foo
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=mlj
|
||||
FILE=malloc://1024
|
||||
CMDS=<<EOF
|
||||
m foo 0x0000000c @ 0x0000000b
|
||||
m bar 0x00000003 @ 0x00000002
|
||||
mlj
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
[{"from":2,"to":3,"name":"bar","realname":"bar","size":1},{"from":11,"to":12,"name":"foo","realname":"foo","size":1}]
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=md
|
||||
FILE=malloc://1024
|
||||
CMDS=<<EOF
|
||||
m a 0x20
|
||||
md a @ 0x100
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
[0x00000000 - 0x00000020] a + 224
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=mC
|
||||
FILE=malloc://1024
|
||||
CMDS=<<EOF
|
||||
m a 0x20
|
||||
mC a foo
|
||||
mC a
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
foo
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=m-*
|
||||
FILE=malloc://1024
|
||||
CMDS=<<EOF
|
||||
m a 0x20
|
||||
m-*
|
||||
ml
|
||||
EOF
|
||||
EXPECT=
|
||||
RUN
|
||||
|
||||
NAME=Set/get
|
||||
FILE==
|
||||
CMDS=<<EOF
|
||||
m south 0x20
|
||||
m+ alpine 0x30
|
||||
m rain 800 @ 666
|
||||
m+ air 900 @ 666
|
||||
m+ es 20 @ 12
|
||||
ml
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
[0x00000000 - 0x00000020] south
|
||||
[0x00000000 - 0x00000030] alpine
|
||||
[0x0000000c - 0x00000014] es
|
||||
[0x0000029a - 0x00000320] rain
|
||||
[0x0000029a - 0x00000384] air
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=marks (JSON)
|
||||
FILE==
|
||||
CMDS=<<EOF
|
||||
# Add marks.
|
||||
m south 0x20
|
||||
m+ alpine 0x32
|
||||
m rain 932 @ 666
|
||||
m+ air 032 @ 666
|
||||
m+ es 100 @ 12
|
||||
mlj
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
[{"from":0,"to":32,"name":"south","realname":"south","size":32},{"from":0,"to":50,"name":"alpine","realname":"alpine","size":50},{"from":12,"to":100,"name":"es","realname":"es","size":88},{"from":666,"to":932,"name":"rain","realname":"rain","size":266},{"from":666,"to":26,"name":"air","realname":"air","size":0}]
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=m and m+ behavior
|
||||
FILE==
|
||||
CMDS=<<EOF
|
||||
m foo 10
|
||||
m bar 20
|
||||
m cow 10
|
||||
m low 20
|
||||
ml~?
|
||||
m-*
|
||||
m+ foo 10
|
||||
m+ bar 20
|
||||
m+ cow 10
|
||||
m+ low 20
|
||||
ml~?
|
||||
m-*
|
||||
m foo 15 @ 10
|
||||
m bar 25 @ 20
|
||||
m cow 30 @ 10
|
||||
m low 40 @ 20
|
||||
ml~?
|
||||
m-*
|
||||
m+ foo 15 @ 10
|
||||
m+ bar 25 @ 20
|
||||
m+ cow 30 @ 10
|
||||
m+ low 40 @ 20
|
||||
ml~?
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
1
|
||||
4
|
||||
2
|
||||
4
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=mark renaming and reusing the old name for something new
|
||||
FILE=malloc://1024
|
||||
CMDS=<<EOF
|
||||
m foo 0x150 @ 0x100
|
||||
mr foo bar
|
||||
m foo 0x250 @ 0x200
|
||||
ml
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
[0x00000100 - 0x00000150] bar
|
||||
[0x00000200 - 0x00000250] foo
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=base64 armored marks
|
||||
FILE=malloc://1024
|
||||
CMDS=<<EOF
|
||||
m direct1 1 verbatim @ 0
|
||||
m direct2 21 base64:ImJhc2U2NCBhcm1vcmVkIg== @ 16
|
||||
m indirect 40 @ 32
|
||||
mC indirect base64:d29ya3MgdG9v
|
||||
ml*
|
||||
mlj
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
[0x00000000 - 0x00000001] direct1 1 base64:dmVyYmF0aW0=
|
||||
[0x00000010 - 0x00000015] direct2 5 base64:ImJhc2U2NCBhcm1vcmVkIg==
|
||||
[0x00000020 - 0x00000028] indirect 8 base64:d29ya3MgdG9v
|
||||
[{"from":0,"to":1,"name":"direct1","realname":"direct1","size":1,"comment":"verbatim"},{"from":16,"to":21,"name":"direct2","realname":"direct2","size":5,"comment":"\"base64 armored\""},{"from":32,"to":40,"name":"indirect","realname":"indirect","size":8,"comment":"works too"}]
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=m base64:NotB64=100
|
||||
FILE=malloc://1024
|
||||
CMDS=<<EOF
|
||||
m base64:NotB64 200 @ 100
|
||||
ml
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
[0x00000064 - 0x000000c8] base64:NotB64
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=mark list at
|
||||
FILE==
|
||||
CMDS=<<EOF
|
||||
m foo 0x0000000c @ 0x0000000b
|
||||
m bar 0x00000003 @ 0x00000002
|
||||
ml. @ 0x0000000b
|
||||
ml.j @ 0x0000000b
|
||||
ml.t @ 0x0000000b
|
||||
ml.q @ 0x0000000b
|
||||
ml. @ 0x00000002
|
||||
ml.j @ 0x00000002
|
||||
ml.t @ 0x00000002
|
||||
ml.q @ 0x00000002
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
[0x0000000b - 0x0000000c] foo
|
||||
[{"from":11,"to":12,"name":"foo","realname":"foo","size":1}]
|
||||
from to name realname
|
||||
------------------------------------
|
||||
0x0000000b 0x0000000c foo foo
|
||||
foo
|
||||
[0x00000002 - 0x00000003] bar
|
||||
[{"from":2,"to":3,"name":"bar","realname":"bar","size":1}]
|
||||
from to name realname
|
||||
------------------------------------
|
||||
0x00000002 0x00000003 bar bar
|
||||
bar
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=pxc shows the start and end of mark with name and range
|
||||
FILE=malloc://1024
|
||||
CMDS=<<EOF
|
||||
m foo 0x20
|
||||
pxc
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF comment
|
||||
0x00000000 0000 0000 0000 0000 0000 0000 0000 0000 ................ ; [s] foo [0x0-0x20]
|
||||
0x00000010 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
0x00000020 0000 0000 0000 0000 0000 0000 0000 0000 ................ ; [e] foo [0x0-0x20]
|
||||
0x00000030 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
0x00000040 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
0x00000050 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
0x00000060 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
0x00000070 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
0x00000080 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
0x00000090 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
0x000000a0 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
0x000000b0 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
0x000000c0 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
0x000000d0 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
0x000000e0 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
0x000000f0 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
EOF
|
||||
RUN
|
||||
|
|
@ -377,6 +377,7 @@ Detailed project load info:
|
|||
project migrated from version 16 to 17.
|
||||
project migrated from version 17 to 18.
|
||||
project migrated from version 18 to 19.
|
||||
project migrated from version 19 to 20.
|
||||
EOF
|
||||
RUN
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ if get_option('enable_tests')
|
|||
'bin_mach0',
|
||||
'bitvector',
|
||||
'bits',
|
||||
'marks',
|
||||
'buf',
|
||||
'cmd',
|
||||
'compare',
|
||||
|
|
@ -94,6 +95,7 @@ if get_option('enable_tests')
|
|||
'sdb_sdb',
|
||||
'sdb_util',
|
||||
'serialize_analysis',
|
||||
'serialize_mark',
|
||||
'serialize_config',
|
||||
'serialize_debug',
|
||||
'serialize_flag',
|
||||
|
|
@ -152,6 +154,7 @@ if get_option('enable_tests')
|
|||
rz_crypto_dep,
|
||||
rz_magic_dep,
|
||||
rz_il_dep,
|
||||
rz_mark_dep,
|
||||
lrt,
|
||||
],
|
||||
install: false,
|
||||
|
|
|
|||
149
test/unit/test_marks.c
Normal file
149
test/unit/test_marks.c
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
// SPDX-FileCopyrightText: 2025 PremadeS <emadsohail001@gmail.com>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <rz_mark.h>
|
||||
#include "minunit.h"
|
||||
|
||||
bool test_rz_mark_get_at() {
|
||||
RzMark *mark = rz_mark_new();
|
||||
|
||||
RzMarkItem *foo = rz_mark_set(mark, "foo", 1024, 2048);
|
||||
|
||||
RzMarkItem *bi;
|
||||
bi = rz_mark_get_start(mark, 1024);
|
||||
mu_assert_ptreq(bi, foo, "mark start at exact");
|
||||
bi = rz_mark_get_start(mark, 1023);
|
||||
mu_assert_null(bi, "no mark start at -1");
|
||||
bi = rz_mark_get_start(mark, 1025);
|
||||
mu_assert_null(bi, "no mark start at +1");
|
||||
|
||||
bi = rz_mark_get_end(mark, 2048);
|
||||
mu_assert_ptreq(bi, foo, "mark end at exact");
|
||||
bi = rz_mark_get_start(mark, 2047);
|
||||
mu_assert_null(bi, "no mark end at -1");
|
||||
bi = rz_mark_get_start(mark, 2049);
|
||||
mu_assert_null(bi, "no mark end at +1");
|
||||
|
||||
rz_mark_free(mark);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_mark_get_all_off() {
|
||||
RzMark *mark = rz_mark_new();
|
||||
|
||||
RzMarkItem *a = rz_mark_set(mark, "a", 0x100, 0x120);
|
||||
RzMarkItem *b = rz_mark_set(mark, "b", 0x110, 0x130);
|
||||
|
||||
RzList *list = rz_mark_get_all_off(mark, 0x110);
|
||||
mu_assert_eq(rz_list_length(list), 2, "2 marks overlap 0x110");
|
||||
|
||||
RzMarkItem *item;
|
||||
RzListIter *it;
|
||||
int found = 0;
|
||||
rz_list_foreach (list, it, item) {
|
||||
if (item == a || item == b)
|
||||
found++;
|
||||
}
|
||||
mu_assert_eq(found, 2, "both overlapping items found");
|
||||
|
||||
rz_mark_free(mark);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_mark_all_list() {
|
||||
RzMark *mark = rz_mark_new();
|
||||
|
||||
rz_mark_set(mark, "a", 0x100, 0x120);
|
||||
rz_mark_set(mark, "b", 0x110, 0x130);
|
||||
rz_mark_set(mark, "c", 0x120, 0x140);
|
||||
|
||||
RzList *all = rz_mark_all_list(mark);
|
||||
mu_assert_eq(rz_list_length(all), 3, "all marks listed");
|
||||
|
||||
rz_mark_free(mark);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_mark_unset() {
|
||||
RzMark *mark = rz_mark_new();
|
||||
RzMarkItem *a = rz_mark_set(mark, "a", 0, 10);
|
||||
rz_mark_set(mark, "b", 5, 15);
|
||||
|
||||
bool res = rz_mark_unset(mark, a);
|
||||
mu_assert_true(res, "unset one mark");
|
||||
mu_assert_null(rz_mark_get(mark, "a"), "a no longer exists");
|
||||
|
||||
bool res2 = rz_mark_unset_all_off(mark, 6);
|
||||
mu_assert_true(res2, "unset all at offset");
|
||||
mu_assert_null(rz_mark_get(mark, "b"), "b no longer exists");
|
||||
|
||||
rz_mark_free(mark);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_mark_rename() {
|
||||
RzMark *mark = rz_mark_new();
|
||||
RzMarkItem *item = rz_mark_set(mark, "foo", 100, 200);
|
||||
mu_assert_notnull(item, "mark added");
|
||||
|
||||
rz_mark_rename(mark, item, "bar");
|
||||
mu_assert_null(rz_mark_get(mark, "foo"), "old name no longer exists");
|
||||
|
||||
RzMarkItem *renamed = rz_mark_get(mark, "bar");
|
||||
mu_assert_notnull(renamed, "new name exists");
|
||||
mu_assert_streq(renamed->name, "bar", "renamed mark name matches");
|
||||
|
||||
mu_assert_true(item == renamed, "renamed item is same object");
|
||||
|
||||
rz_mark_free(mark);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_mark_set_properties() {
|
||||
RzMark *mark = rz_mark_new();
|
||||
RzMarkItem *item = rz_mark_set(mark, "foo", 100, 200);
|
||||
mu_assert_notnull(item, "mark added");
|
||||
|
||||
rz_mark_item_set_comment(item, "test comment");
|
||||
mu_assert_true(item->comment, "set comment works");
|
||||
mu_assert_streq(item->comment, "test comment", "comment matches");
|
||||
|
||||
rz_mark_item_set_color(item, "0xFF00FF");
|
||||
mu_assert_true(item->color, "set color works");
|
||||
mu_assert_streq(item->color, "0xFF00FF", "color matches");
|
||||
|
||||
rz_mark_item_set_realname(item, "real_foo");
|
||||
mu_assert_true(item->realname, "set realname works");
|
||||
mu_assert_streq(item->realname, "real_foo", "realname matches");
|
||||
|
||||
rz_mark_free(mark);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_mark_count_and_starts_or_ends() {
|
||||
RzMark *mark = rz_mark_new();
|
||||
rz_mark_set(mark, "a", 0, 10);
|
||||
rz_mark_set(mark, "b", 5, 15);
|
||||
|
||||
int c = rz_mark_count(mark, "*");
|
||||
mu_assert_eq(c, 2, "2 marks counted");
|
||||
|
||||
bool so = rz_mark_starts_or_ends(mark, 0, 10);
|
||||
mu_assert_true(so, "a starts/ends at 0/10");
|
||||
|
||||
rz_mark_free(mark);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
int all_tests(void) {
|
||||
mu_run_test(test_rz_mark_get_at);
|
||||
mu_run_test(test_rz_mark_get_all_off);
|
||||
mu_run_test(test_rz_mark_all_list);
|
||||
mu_run_test(test_rz_mark_unset);
|
||||
mu_run_test(test_rz_mark_set_properties);
|
||||
mu_run_test(test_rz_mark_rename);
|
||||
mu_run_test(test_rz_mark_count_and_starts_or_ends);
|
||||
return tests_passed != tests_run;
|
||||
}
|
||||
|
||||
mu_main(all_tests)
|
||||
111
test/unit/test_serialize_mark.c
Normal file
111
test/unit/test_serialize_mark.c
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
// SPDX-FileCopyrightText: 2025 PremadeS <emadsohail001@gmail.com>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <rz_mark.h>
|
||||
#include <rz_flag.h>
|
||||
#include "minunit.h"
|
||||
#include "test_sdb.h"
|
||||
|
||||
Sdb *ref_0_db() {
|
||||
Sdb *db = sdb_new0();
|
||||
|
||||
Sdb *marks_db = sdb_ns(db, "marks", true);
|
||||
sdb_set(marks_db, "foobars", "{\"realname\":\"Foobars\",\"from\":4000,\"to\":5000,\"color\":\"white\",\"comment\":\"windowpane\"}");
|
||||
sdb_set(marks_db, "f00b4r5", "{\"realname\":\"f00b4r5\",\"from\":5000,\"to\":6213}");
|
||||
sdb_set(marks_db, "deliverance", "{\"realname\":\"deliverance\",\"from\":1403,\"to\":1500}");
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
RzMark *ref_0_mark() {
|
||||
RzMark *mark = rz_mark_new();
|
||||
|
||||
rz_mark_set(mark, "deliverance", 0x578 + 3, 1500);
|
||||
rz_mark_set(mark, "f00b4r5", 5000, 6213);
|
||||
|
||||
RzMarkItem *foobars = rz_mark_set(mark, "foobars", 4000, 5000);
|
||||
rz_mark_item_set_realname(foobars, "Foobars");
|
||||
rz_mark_item_set_color(foobars, "white");
|
||||
rz_mark_item_set_comment(foobars, "windowpane");
|
||||
|
||||
return mark;
|
||||
}
|
||||
|
||||
RzMark *ref_1_mark() {
|
||||
RzMark *mark = rz_mark_new();
|
||||
return mark;
|
||||
}
|
||||
|
||||
static bool test_save(RzMark *mark, Sdb *ref) {
|
||||
Sdb *db = sdb_new0();
|
||||
rz_serialize_mark_save(db, mark);
|
||||
assert_sdb_eq(db, ref, "save");
|
||||
sdb_free(db);
|
||||
sdb_free(ref);
|
||||
rz_mark_free(mark);
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
bool equal;
|
||||
RzMark *other;
|
||||
} markCmpCtx;
|
||||
|
||||
static bool mark_cmp(RzMarkItem *actual, RzMarkItem *expected) {
|
||||
mu_assert_notnull(expected, "mark");
|
||||
assert_streq_null(actual->realname, expected->realname, "mark realname");
|
||||
mu_assert_eq_fmt(actual->from, expected->from, "mark from", "0x%" PFMT64x);
|
||||
mu_assert_eq_fmt(actual->to, expected->to, "mark to", "0x%" PFMT64x);
|
||||
assert_streq_null(actual->color, expected->color, "mark color");
|
||||
assert_streq_null(actual->comment, expected->comment, "mark comment");
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool mark_cmp_cb(RzMarkItem *fi, void *user) {
|
||||
markCmpCtx *ctx = user;
|
||||
RzMarkItem *fo = rz_mark_get(ctx->other, fi->name);
|
||||
if (!mark_cmp(fi, fo)) {
|
||||
ctx->equal = false;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool test_load(Sdb *db, RzMark *ref) {
|
||||
RzMark *mark = rz_mark_new();
|
||||
|
||||
bool loaded = rz_serialize_mark_load(db, mark, NULL);
|
||||
sdb_free(db);
|
||||
mu_assert("load success", loaded);
|
||||
|
||||
size_t actual_count = rz_mark_count(mark, NULL);
|
||||
size_t expected_count = rz_mark_count(ref, NULL);
|
||||
mu_assert_eq(actual_count, expected_count, "marks count");
|
||||
|
||||
markCmpCtx cmp_ctx = { true, ref };
|
||||
rz_mark_foreach(mark, mark_cmp_cb, &cmp_ctx);
|
||||
mu_assert("marks equal", cmp_ctx.equal);
|
||||
|
||||
rz_mark_free(mark);
|
||||
rz_mark_free(ref);
|
||||
return true;
|
||||
}
|
||||
|
||||
#define TEST_CALL(name, call) \
|
||||
bool name() { \
|
||||
if (!(call)) { \
|
||||
return false; \
|
||||
} \
|
||||
mu_end; \
|
||||
}
|
||||
|
||||
TEST_CALL(test_mark_0_save, test_save(ref_0_mark(), ref_0_db()));
|
||||
TEST_CALL(test_mark_0_load, test_load(ref_0_db(), ref_0_mark()));
|
||||
|
||||
int all_tests() {
|
||||
mu_run_test(test_mark_0_save);
|
||||
mu_run_test(test_mark_0_load);
|
||||
return tests_passed != tests_run;
|
||||
}
|
||||
|
||||
mu_main(all_tests)
|
||||
Loading…
Reference in a new issue