#include <mdb/mdb_modapi.h>
#include <mdb/mdb_ctf.h>
#include <mdb/mdb_ctf_impl.h>
#include <mdb/mdb_string.h>
#include <mdb/mdb_module.h>
#include <mdb/mdb_debug.h>
#include <mdb/mdb_print.h>
#include <mdb/mdb_nv.h>
#include <mdb/mdb_tab.h>
#include <mdb/mdb_target.h>
#include <mdb/mdb.h>
#include <ctype.h>
#define COMMAND_SEPARATOR "::"
static char *
tab_find_command_start(char *buf)
{
char *offset = strstr(buf, COMMAND_SEPARATOR);
if (offset == NULL)
return (NULL);
for (;;) {
char *next = strstr(offset + strlen(COMMAND_SEPARATOR),
COMMAND_SEPARATOR);
if (next == NULL) {
return (offset);
}
offset = next;
}
}
char *
tab_get_dcmd(char *buf, char **args, uint_t *flags)
{
char *start = buf + strlen(COMMAND_SEPARATOR);
char *separator = start;
const char *end = buf + strlen(buf);
uint_t space = 0;
while (separator < end && !isspace(*separator))
separator++;
if (separator == end) {
*args = NULL;
} else {
if (isspace(*separator))
space = 1;
*separator++ = '\0';
*args = separator;
}
if (space)
*flags |= DCMD_TAB_SPACE;
return (start);
}
static int
tab_count_args(const char *input, uint_t *flags)
{
const char *index;
int argc = 0;
uint_t space = *flags & DCMD_TAB_SPACE;
index = input;
while (*index != '\0') {
while (*index != '\0' && isspace(*index)) {
index++;
space = 1;
}
if (*index != '\0' && !isspace(*index)) {
argc++;
space = 0;
while (*index != '\0' && !isspace (*index)) {
index++;
}
}
}
if (space)
*flags |= DCMD_TAB_SPACE;
else
*flags &= ~DCMD_TAB_SPACE;
return (argc);
}
static int
tab_copy_args(char *input, int argc, mdb_arg_t *argv)
{
int i = 0;
char *index;
index = input;
while (*index) {
while (*index && isspace(*index)) {
index++;
}
if (*index && !isspace(*index)) {
char *end = index;
while (*end && !isspace(*end)) {
end++;
}
if (*end) {
*end++ = '\0';
}
argv[i].a_type = MDB_TYPE_STRING;
argv[i].a_un.a_str = index;
index = end;
i++;
}
}
if (i != argc)
return (-1);
return (0);
}
static int
tab_parse_buf(char *buf, char **dcmdp, int *argcp, mdb_arg_t **argvp,
uint_t *flags)
{
char *data = tab_find_command_start(buf);
char *args_data = NULL;
char *dcmd = NULL;
int argc = 0;
mdb_arg_t *argv = NULL;
if (data == NULL) {
return (-1);
}
dcmd = tab_get_dcmd(data, &args_data, flags);
if (dcmd == NULL) {
return (-1);
}
if (args_data != NULL) {
argc = tab_count_args(args_data, flags);
if (argc != 0) {
argv = mdb_alloc(sizeof (mdb_arg_t) * argc,
UM_SLEEP | UM_GC);
if (tab_copy_args(args_data, argc, argv) == -1)
return (-1);
}
}
*dcmdp = dcmd;
*argcp = argc;
*argvp = argv;
return (0);
}
int
mdb_tab_command(mdb_tab_cookie_t *mcp, const char *buf)
{
char *data;
char *dcmd = NULL;
int argc = 0;
mdb_arg_t *argv = NULL;
int ret = 0;
mdb_idcmd_t *cp;
uint_t flags = 0;
data = mdb_alloc(strlen(buf) + 1, UM_SLEEP | UM_GC);
(void) strcpy(data, buf);
ret = tab_parse_buf(data, &dcmd, &argc, &argv, &flags);
if (ret != 0) {
(void) mdb_tab_complete_global(mcp, buf);
goto out;
}
cp = mdb_dcmd_lookup(dcmd);
if (cp == NULL && argc != 0) {
goto out;
}
if (cp == NULL)
(void) mdb_tab_complete_dcmd(mcp, dcmd);
else
mdb_call_tab(cp, mcp, flags, argc, argv);
out:
return (mdb_tab_size(mcp));
}
static int
tab_complete_dcmd(mdb_var_t *v, void *arg)
{
mdb_idcmd_t *idcp = mdb_nv_get_cookie(mdb_nv_get_cookie(v));
mdb_tab_cookie_t *mcp = (mdb_tab_cookie_t *)arg;
if (!isalnum(idcp->idc_name[0]))
return (0);
mdb_tab_insert(mcp, idcp->idc_name);
return (0);
}
int
mdb_tab_complete_dcmd(mdb_tab_cookie_t *mcp, const char *dcmd)
{
if (dcmd != NULL)
mdb_tab_setmbase(mcp, dcmd);
mdb_nv_sort_iter(&mdb.m_dcmds, tab_complete_dcmd, mcp,
UM_GC | UM_SLEEP);
return (0);
}
static int
tab_complete_walker(mdb_var_t *v, void *arg)
{
mdb_iwalker_t *iwp = mdb_nv_get_cookie(mdb_nv_get_cookie(v));
mdb_tab_cookie_t *mcp = arg;
mdb_tab_insert(mcp, iwp->iwlk_name);
return (0);
}
int
mdb_tab_complete_walker(mdb_tab_cookie_t *mcp, const char *walker)
{
if (walker != NULL)
mdb_tab_setmbase(mcp, walker);
mdb_nv_sort_iter(&mdb.m_walkers, tab_complete_walker, mcp,
UM_GC | UM_SLEEP);
return (0);
}
mdb_tab_cookie_t *
mdb_tab_init(void)
{
mdb_tab_cookie_t *mcp;
mcp = mdb_zalloc(sizeof (mdb_tab_cookie_t), UM_SLEEP | UM_GC);
(void) mdb_nv_create(&mcp->mtc_nv, UM_SLEEP | UM_GC);
return (mcp);
}
size_t
mdb_tab_size(mdb_tab_cookie_t *mcp)
{
return (mdb_nv_size(&mcp->mtc_nv));
}
void
mdb_tab_insert(mdb_tab_cookie_t *mcp, const char *name)
{
size_t matches, index;
mdb_var_t *v;
if (strncmp(name, mcp->mtc_base, strlen(mcp->mtc_base)) != 0)
return;
v = mdb_nv_lookup(&mcp->mtc_nv, name);
if (v != NULL)
return;
(void) mdb_nv_insert(&mcp->mtc_nv, name, NULL, 0, MDB_NV_RDONLY);
matches = mdb_tab_size(mcp);
if (matches == 1) {
(void) strlcpy(mcp->mtc_match, name, MDB_SYM_NAMLEN);
} else {
index = 0;
while (mcp->mtc_match[index] &&
mcp->mtc_match[index] == name[index])
index++;
mcp->mtc_match[index] = '\0';
}
}
static int
tab_print_cb(mdb_var_t *v, void *ignored)
{
mdb_printf("%s\n", mdb_nv_get_name(v));
return (0);
}
void
mdb_tab_print(mdb_tab_cookie_t *mcp)
{
mdb_nv_sort_iter(&mcp->mtc_nv, tab_print_cb, NULL, UM_SLEEP | UM_GC);
}
const char *
mdb_tab_match(mdb_tab_cookie_t *mcp)
{
return (mcp->mtc_match + strlen(mcp->mtc_base));
}
void
mdb_tab_setmbase(mdb_tab_cookie_t *mcp, const char *base)
{
(void) strlcpy(mcp->mtc_base, base, MDB_SYM_NAMLEN);
}
void
mdb_tab_fini(mdb_tab_cookie_t *mcp)
{
}
static int
tab_complete_global(void *arg, const GElf_Sym *sym, const char *name,
const mdb_syminfo_t *sip, const char *obj)
{
mdb_tab_cookie_t *mcp = arg;
mdb_tab_insert(mcp, name);
return (0);
}
int
mdb_tab_complete_global(mdb_tab_cookie_t *mcp, const char *name)
{
mdb_tab_setmbase(mcp, name);
(void) mdb_tgt_symbol_iter(mdb.m_target, MDB_TGT_OBJ_EVERY,
MDB_TGT_SYMTAB, MDB_TGT_BIND_ANY | MDB_TGT_TYPE_OBJECT |
MDB_TGT_TYPE_FUNC, tab_complete_global, mcp);
return (0);
}
static int
tab_complete_type(mdb_ctf_id_t id, void *arg)
{
int rkind;
char buf[MDB_SYM_NAMLEN];
mdb_ctf_id_t rid;
mdb_tab_cookie_t *mcp = arg;
uint_t flags = (uint_t)(uintptr_t)mcp->mtc_cba;
switch (mdb_ctf_type_kind(id)) {
case CTF_K_CONST:
case CTF_K_RESTRICT:
case CTF_K_VOLATILE:
return (0);
}
if (mdb_ctf_type_resolve(id, &rid) != 0)
return (1);
rkind = mdb_ctf_type_kind(rid);
if ((flags & MDB_TABC_MEMBERS) && rkind != CTF_K_STRUCT &&
rkind != CTF_K_UNION)
return (0);
if ((flags & MDB_TABC_NOPOINT) && rkind == CTF_K_POINTER)
return (0);
if ((flags & MDB_TABC_NOARRAY) && rkind == CTF_K_ARRAY)
return (0);
(void) mdb_ctf_type_name(id, buf, sizeof (buf));
mdb_tab_insert(mcp, buf);
return (0);
}
static int
mdb_tab_complete_module(void *data, const mdb_map_t *mp, const char *name)
{
(void) mdb_ctf_type_iter(name, tab_complete_type, data);
return (0);
}
int
mdb_tab_complete_type(mdb_tab_cookie_t *mcp, const char *name, uint_t flags)
{
mdb_tgt_t *t = mdb.m_target;
mcp->mtc_cba = (void *)(uintptr_t)flags;
if (name != NULL)
mdb_tab_setmbase(mcp, name);
(void) mdb_tgt_object_iter(t, mdb_tab_complete_module, mcp);
(void) mdb_ctf_type_iter(MDB_CTF_SYNTHETIC_ITER, tab_complete_type,
mcp);
return (0);
}
static int
tab_complete_member(const char *name, mdb_ctf_id_t id, ulong_t off, void *arg)
{
mdb_tab_cookie_t *mcp = arg;
mdb_tab_insert(mcp, name);
return (0);
}
int
mdb_tab_complete_member_by_id(mdb_tab_cookie_t *mcp, mdb_ctf_id_t id,
const char *member)
{
if (member != NULL)
mdb_tab_setmbase(mcp, member);
(void) mdb_ctf_member_iter(id, tab_complete_member, mcp,
MDB_CTF_F_ITER_ANON);
return (0);
}
int
mdb_tab_complete_member(mdb_tab_cookie_t *mcp, const char *type,
const char *member)
{
mdb_ctf_id_t id;
if (mdb_ctf_lookup_by_name(type, &id) != 0)
return (-1);
return (mdb_tab_complete_member_by_id(mcp, id, member));
}
int
mdb_tab_complete_mt(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
const mdb_arg_t *argv)
{
char tn[MDB_SYM_NAMLEN];
int ret;
if (argc == 0 && !(flags & DCMD_TAB_SPACE))
return (0);
if (argc == 0)
return (mdb_tab_complete_type(mcp, NULL, MDB_TABC_MEMBERS));
if ((ret = mdb_tab_typename(&argc, &argv, tn, sizeof (tn))) < 0)
return (ret);
if (argc == 1 && (!(flags & DCMD_TAB_SPACE) || ret == 1))
return (mdb_tab_complete_type(mcp, tn, MDB_TABC_MEMBERS));
if (argc == 1 && (flags & DCMD_TAB_SPACE))
return (mdb_tab_complete_member(mcp, tn, NULL));
if (argc == 2)
return (mdb_tab_complete_member(mcp, tn, argv[1].a_un.a_str));
return (0);
}
int
mdb_tab_typename(int *argcp, const mdb_arg_t **argvp, char *buf, size_t len)
{
int argc = *argcp;
const mdb_arg_t *argv = *argvp;
if (argc < 1 || argv->a_type != MDB_TYPE_STRING)
return (DCMD_USAGE);
if (strcmp(argv->a_un.a_str, "struct") == 0 ||
strcmp(argv->a_un.a_str, "enum") == 0 ||
strcmp(argv->a_un.a_str, "union") == 0) {
if (argc == 1) {
(void) mdb_snprintf(buf, len, "%s ",
argv[0].a_un.a_str);
return (1);
}
if (argv[1].a_type != MDB_TYPE_STRING)
return (DCMD_USAGE);
(void) mdb_snprintf(buf, len, "%s %s",
argv[0].a_un.a_str, argv[1].a_un.a_str);
*argcp = argc - 1;
*argvp = argv + 1;
} else {
(void) mdb_snprintf(buf, len, "%s", argv[0].a_un.a_str);
}
return (0);
}