13b6e0a59SMatt Amdur /*
23b6e0a59SMatt Amdur * CDDL HEADER START
33b6e0a59SMatt Amdur *
43b6e0a59SMatt Amdur * The contents of this file are subject to the terms of the
53b6e0a59SMatt Amdur * Common Development and Distribution License (the "License").
63b6e0a59SMatt Amdur * You may not use this file except in compliance with the License.
73b6e0a59SMatt Amdur *
83b6e0a59SMatt Amdur * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93b6e0a59SMatt Amdur * or http://www.opensolaris.org/os/licensing.
103b6e0a59SMatt Amdur * See the License for the specific language governing permissions
113b6e0a59SMatt Amdur * and limitations under the License.
123b6e0a59SMatt Amdur *
133b6e0a59SMatt Amdur * When distributing Covered Code, include this CDDL HEADER in each
143b6e0a59SMatt Amdur * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153b6e0a59SMatt Amdur * If applicable, add the following below this CDDL HEADER, with the
163b6e0a59SMatt Amdur * fields enclosed by brackets "[]" replaced with your own identifying
173b6e0a59SMatt Amdur * information: Portions Copyright [yyyy] [name of copyright owner]
183b6e0a59SMatt Amdur *
193b6e0a59SMatt Amdur * CDDL HEADER END
203b6e0a59SMatt Amdur */
213b6e0a59SMatt Amdur /*
2272a1114bSHenrik Mattsson * Copyright (c) 2013 by Delphix. All rights reserved.
23*d75f3745SJohn Levon * Copyright (c) 2018, Joyent, Inc.
24065c692aSJosef 'Jeff' Sipek * Copyright (c) 2013 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
253b6e0a59SMatt Amdur */
263b6e0a59SMatt Amdur /*
273b6e0a59SMatt Amdur * This file contains all of the interfaces for mdb's tab completion engine.
283b6e0a59SMatt Amdur * Currently some interfaces are private to mdb and its internal implementation,
293b6e0a59SMatt Amdur * those are in mdb_tab.h. Other pieces are public interfaces. Those are in
303b6e0a59SMatt Amdur * mdb_modapi.h.
313b6e0a59SMatt Amdur *
323b6e0a59SMatt Amdur * Memory allocations in tab completion context have to be done very carefully.
333b6e0a59SMatt Amdur * We need to think of ourselves as the same as any other command that is being
343b6e0a59SMatt Amdur * executed by the user, which means we must use UM_GC to handle being
353b6e0a59SMatt Amdur * interrupted.
363b6e0a59SMatt Amdur */
373b6e0a59SMatt Amdur
383b6e0a59SMatt Amdur #include <mdb/mdb_modapi.h>
393b6e0a59SMatt Amdur #include <mdb/mdb_ctf.h>
403b6e0a59SMatt Amdur #include <mdb/mdb_ctf_impl.h>
413b6e0a59SMatt Amdur #include <mdb/mdb_string.h>
423b6e0a59SMatt Amdur #include <mdb/mdb_module.h>
433b6e0a59SMatt Amdur #include <mdb/mdb_debug.h>
443b6e0a59SMatt Amdur #include <mdb/mdb_print.h>
453b6e0a59SMatt Amdur #include <mdb/mdb_nv.h>
463b6e0a59SMatt Amdur #include <mdb/mdb_tab.h>
4772a1114bSHenrik Mattsson #include <mdb/mdb_target.h>
483b6e0a59SMatt Amdur #include <mdb/mdb.h>
493b6e0a59SMatt Amdur
503b6e0a59SMatt Amdur #include <ctype.h>
513b6e0a59SMatt Amdur
523b6e0a59SMatt Amdur /*
533b6e0a59SMatt Amdur * There may be another way to do this, but this works well enough.
543b6e0a59SMatt Amdur */
553b6e0a59SMatt Amdur #define COMMAND_SEPARATOR "::"
563b6e0a59SMatt Amdur
573b6e0a59SMatt Amdur /*
583b6e0a59SMatt Amdur * find_command_start --
593b6e0a59SMatt Amdur *
60*d75f3745SJohn Levon * Given a buffer find the start of the last command.
613b6e0a59SMatt Amdur */
623b6e0a59SMatt Amdur static char *
tab_find_command_start(char * buf)633b6e0a59SMatt Amdur tab_find_command_start(char *buf)
643b6e0a59SMatt Amdur {
653b6e0a59SMatt Amdur char *offset = strstr(buf, COMMAND_SEPARATOR);
663b6e0a59SMatt Amdur
673b6e0a59SMatt Amdur if (offset == NULL)
683b6e0a59SMatt Amdur return (NULL);
693b6e0a59SMatt Amdur
703b6e0a59SMatt Amdur for (;;) {
713b6e0a59SMatt Amdur char *next = strstr(offset + strlen(COMMAND_SEPARATOR),
723b6e0a59SMatt Amdur COMMAND_SEPARATOR);
733b6e0a59SMatt Amdur
743b6e0a59SMatt Amdur if (next == NULL) {
753b6e0a59SMatt Amdur return (offset);
763b6e0a59SMatt Amdur }
773b6e0a59SMatt Amdur
783b6e0a59SMatt Amdur offset = next;
793b6e0a59SMatt Amdur }
803b6e0a59SMatt Amdur }
813b6e0a59SMatt Amdur
823b6e0a59SMatt Amdur /*
833b6e0a59SMatt Amdur * get_dcmd --
843b6e0a59SMatt Amdur *
85*d75f3745SJohn Levon * Given a buffer containing a command and its argument return
86*d75f3745SJohn Levon * the name of the command and the offset in the buffer where
87*d75f3745SJohn Levon * the command arguments start.
883b6e0a59SMatt Amdur *
89*d75f3745SJohn Levon * Note: This will modify the buffer.
903b6e0a59SMatt Amdur */
913b6e0a59SMatt Amdur char *
tab_get_dcmd(char * buf,char ** args,uint_t * flags)923b6e0a59SMatt Amdur tab_get_dcmd(char *buf, char **args, uint_t *flags)
933b6e0a59SMatt Amdur {
943b6e0a59SMatt Amdur char *start = buf + strlen(COMMAND_SEPARATOR);
953b6e0a59SMatt Amdur char *separator = start;
963b6e0a59SMatt Amdur const char *end = buf + strlen(buf);
973b6e0a59SMatt Amdur uint_t space = 0;
983b6e0a59SMatt Amdur
993b6e0a59SMatt Amdur while (separator < end && !isspace(*separator))
1003b6e0a59SMatt Amdur separator++;
1013b6e0a59SMatt Amdur
1023b6e0a59SMatt Amdur if (separator == end) {
1033b6e0a59SMatt Amdur *args = NULL;
1043b6e0a59SMatt Amdur } else {
1053b6e0a59SMatt Amdur if (isspace(*separator))
1063b6e0a59SMatt Amdur space = 1;
1073b6e0a59SMatt Amdur
1083b6e0a59SMatt Amdur *separator++ = '\0';
1093b6e0a59SMatt Amdur *args = separator;
1103b6e0a59SMatt Amdur }
1113b6e0a59SMatt Amdur
1123b6e0a59SMatt Amdur if (space)
1133b6e0a59SMatt Amdur *flags |= DCMD_TAB_SPACE;
1143b6e0a59SMatt Amdur
1153b6e0a59SMatt Amdur return (start);
1163b6e0a59SMatt Amdur }
1173b6e0a59SMatt Amdur
1183b6e0a59SMatt Amdur /*
1193b6e0a59SMatt Amdur * count_args --
1203b6e0a59SMatt Amdur *
121*d75f3745SJohn Levon * Given a buffer containing dmcd arguments return the total number
122*d75f3745SJohn Levon * of arguments.
1233b6e0a59SMatt Amdur *
124*d75f3745SJohn Levon * While parsing arguments we need to keep track of whether or not the last
125*d75f3745SJohn Levon * arguments ends with a trailing space.
1263b6e0a59SMatt Amdur */
1273b6e0a59SMatt Amdur static int
tab_count_args(const char * input,uint_t * flags)1283b6e0a59SMatt Amdur tab_count_args(const char *input, uint_t *flags)
1293b6e0a59SMatt Amdur {
1303b6e0a59SMatt Amdur const char *index;
1313b6e0a59SMatt Amdur int argc = 0;
1323b6e0a59SMatt Amdur uint_t space = *flags & DCMD_TAB_SPACE;
1333b6e0a59SMatt Amdur index = input;
1343b6e0a59SMatt Amdur
1353b6e0a59SMatt Amdur while (*index != '\0') {
1363b6e0a59SMatt Amdur while (*index != '\0' && isspace(*index)) {
1373b6e0a59SMatt Amdur index++;
1383b6e0a59SMatt Amdur space = 1;
1393b6e0a59SMatt Amdur }
1403b6e0a59SMatt Amdur
1413b6e0a59SMatt Amdur if (*index != '\0' && !isspace(*index)) {
1423b6e0a59SMatt Amdur argc++;
1433b6e0a59SMatt Amdur space = 0;
1443b6e0a59SMatt Amdur while (*index != '\0' && !isspace (*index)) {
1453b6e0a59SMatt Amdur index++;
1463b6e0a59SMatt Amdur }
1473b6e0a59SMatt Amdur }
1483b6e0a59SMatt Amdur }
1493b6e0a59SMatt Amdur
1503b6e0a59SMatt Amdur if (space)
1513b6e0a59SMatt Amdur *flags |= DCMD_TAB_SPACE;
1523b6e0a59SMatt Amdur else
1533b6e0a59SMatt Amdur *flags &= ~DCMD_TAB_SPACE;
1543b6e0a59SMatt Amdur
1553b6e0a59SMatt Amdur return (argc);
1563b6e0a59SMatt Amdur }
1573b6e0a59SMatt Amdur
1583b6e0a59SMatt Amdur /*
1593b6e0a59SMatt Amdur * copy_args --
1603b6e0a59SMatt Amdur *
161*d75f3745SJohn Levon * Given a buffer containing dcmd arguments and an array of mdb_arg_t's
162*d75f3745SJohn Levon * initialize the string value of each mdb_arg_t.
1633b6e0a59SMatt Amdur *
164*d75f3745SJohn Levon * Note: This will modify the buffer.
1653b6e0a59SMatt Amdur */
1663b6e0a59SMatt Amdur static int
tab_copy_args(char * input,int argc,mdb_arg_t * argv)1673b6e0a59SMatt Amdur tab_copy_args(char *input, int argc, mdb_arg_t *argv)
1683b6e0a59SMatt Amdur {
1693b6e0a59SMatt Amdur int i = 0;
1703b6e0a59SMatt Amdur char *index;
1713b6e0a59SMatt Amdur
1723b6e0a59SMatt Amdur index = input;
1733b6e0a59SMatt Amdur
1743b6e0a59SMatt Amdur while (*index) {
1753b6e0a59SMatt Amdur while (*index && isspace(*index)) {
1763b6e0a59SMatt Amdur index++;
1773b6e0a59SMatt Amdur }
1783b6e0a59SMatt Amdur
1793b6e0a59SMatt Amdur if (*index && !isspace(*index)) {
1803b6e0a59SMatt Amdur char *end = index;
1813b6e0a59SMatt Amdur
1823b6e0a59SMatt Amdur while (*end && !isspace(*end)) {
1833b6e0a59SMatt Amdur end++;
1843b6e0a59SMatt Amdur }
1853b6e0a59SMatt Amdur
1863b6e0a59SMatt Amdur if (*end) {
1873b6e0a59SMatt Amdur *end++ = '\0';
1883b6e0a59SMatt Amdur }
1893b6e0a59SMatt Amdur
1903b6e0a59SMatt Amdur argv[i].a_type = MDB_TYPE_STRING;
1913b6e0a59SMatt Amdur argv[i].a_un.a_str = index;
1923b6e0a59SMatt Amdur
1933b6e0a59SMatt Amdur index = end;
1943b6e0a59SMatt Amdur i++;
1953b6e0a59SMatt Amdur }
1963b6e0a59SMatt Amdur }
1973b6e0a59SMatt Amdur
1983b6e0a59SMatt Amdur if (i != argc)
1993b6e0a59SMatt Amdur return (-1);
2003b6e0a59SMatt Amdur
2013b6e0a59SMatt Amdur return (0);
2023b6e0a59SMatt Amdur }
2033b6e0a59SMatt Amdur
2043b6e0a59SMatt Amdur /*
2053b6e0a59SMatt Amdur * parse-buf --
2063b6e0a59SMatt Amdur *
207*d75f3745SJohn Levon * Parse the given buffer and return the specified dcmd, the number
208*d75f3745SJohn Levon * of arguments, and array of mdb_arg_t containing the argument
209*d75f3745SJohn Levon * values.
2103b6e0a59SMatt Amdur *
211*d75f3745SJohn Levon * Note: this will modify the specified buffer. Caller is responisble
212*d75f3745SJohn Levon * for freeing argvp.
2133b6e0a59SMatt Amdur */
2143b6e0a59SMatt Amdur static int
tab_parse_buf(char * buf,char ** dcmdp,int * argcp,mdb_arg_t ** argvp,uint_t * flags)2153b6e0a59SMatt Amdur tab_parse_buf(char *buf, char **dcmdp, int *argcp, mdb_arg_t **argvp,
2163b6e0a59SMatt Amdur uint_t *flags)
2173b6e0a59SMatt Amdur {
2183b6e0a59SMatt Amdur char *data = tab_find_command_start(buf);
2193b6e0a59SMatt Amdur char *args_data = NULL;
2203b6e0a59SMatt Amdur char *dcmd = NULL;
2213b6e0a59SMatt Amdur int argc = 0;
2223b6e0a59SMatt Amdur mdb_arg_t *argv = NULL;
2233b6e0a59SMatt Amdur
2243b6e0a59SMatt Amdur if (data == NULL) {
2253b6e0a59SMatt Amdur return (-1);
2263b6e0a59SMatt Amdur }
2273b6e0a59SMatt Amdur
2283b6e0a59SMatt Amdur dcmd = tab_get_dcmd(data, &args_data, flags);
2293b6e0a59SMatt Amdur
2303b6e0a59SMatt Amdur if (dcmd == NULL) {
2313b6e0a59SMatt Amdur return (-1);
2323b6e0a59SMatt Amdur }
2333b6e0a59SMatt Amdur
2343b6e0a59SMatt Amdur if (args_data != NULL) {
2353b6e0a59SMatt Amdur argc = tab_count_args(args_data, flags);
2363b6e0a59SMatt Amdur
2373b6e0a59SMatt Amdur if (argc != 0) {
2383b6e0a59SMatt Amdur argv = mdb_alloc(sizeof (mdb_arg_t) * argc,
2393b6e0a59SMatt Amdur UM_SLEEP | UM_GC);
2403b6e0a59SMatt Amdur
2413b6e0a59SMatt Amdur if (tab_copy_args(args_data, argc, argv) == -1)
2423b6e0a59SMatt Amdur return (-1);
2433b6e0a59SMatt Amdur }
2443b6e0a59SMatt Amdur }
2453b6e0a59SMatt Amdur
2463b6e0a59SMatt Amdur *dcmdp = dcmd;
2473b6e0a59SMatt Amdur *argcp = argc;
2483b6e0a59SMatt Amdur *argvp = argv;
2493b6e0a59SMatt Amdur
2503b6e0a59SMatt Amdur return (0);
2513b6e0a59SMatt Amdur }
2523b6e0a59SMatt Amdur
2533b6e0a59SMatt Amdur /*
2543b6e0a59SMatt Amdur * tab_command --
2553b6e0a59SMatt Amdur *
256*d75f3745SJohn Levon * This function is executed anytime a tab is entered. It checks
257*d75f3745SJohn Levon * the current buffer to determine if there is a valid dmcd,
258*d75f3745SJohn Levon * if that dcmd has a tab completion handler it will invoke it.
2593b6e0a59SMatt Amdur *
2603b6e0a59SMatt Amdur * This function returns the string (if any) that should be added to the
2613b6e0a59SMatt Amdur * existing buffer to complete it.
2623b6e0a59SMatt Amdur */
2633b6e0a59SMatt Amdur int
mdb_tab_command(mdb_tab_cookie_t * mcp,const char * buf)2643b6e0a59SMatt Amdur mdb_tab_command(mdb_tab_cookie_t *mcp, const char *buf)
2653b6e0a59SMatt Amdur {
2663b6e0a59SMatt Amdur char *data;
2673b6e0a59SMatt Amdur char *dcmd = NULL;
2683b6e0a59SMatt Amdur int argc = 0;
2693b6e0a59SMatt Amdur mdb_arg_t *argv = NULL;
2703b6e0a59SMatt Amdur int ret = 0;
2713b6e0a59SMatt Amdur mdb_idcmd_t *cp;
2723b6e0a59SMatt Amdur uint_t flags = 0;
2733b6e0a59SMatt Amdur
2743b6e0a59SMatt Amdur /*
2753b6e0a59SMatt Amdur * Parsing the command and arguments will modify the buffer
2763b6e0a59SMatt Amdur * (replacing spaces with \0), so make a copy of the specified
2773b6e0a59SMatt Amdur * buffer first.
2783b6e0a59SMatt Amdur */
2793b6e0a59SMatt Amdur data = mdb_alloc(strlen(buf) + 1, UM_SLEEP | UM_GC);
2803b6e0a59SMatt Amdur (void) strcpy(data, buf);
2813b6e0a59SMatt Amdur
2823b6e0a59SMatt Amdur /*
2833b6e0a59SMatt Amdur * Get the specified dcmd and arguments from the buffer.
2843b6e0a59SMatt Amdur */
2853b6e0a59SMatt Amdur ret = tab_parse_buf(data, &dcmd, &argc, &argv, &flags);
2863b6e0a59SMatt Amdur
28772a1114bSHenrik Mattsson /*
28872a1114bSHenrik Mattsson * Match against global symbols if the input is not a dcmd
28972a1114bSHenrik Mattsson */
2903b6e0a59SMatt Amdur if (ret != 0) {
29172a1114bSHenrik Mattsson (void) mdb_tab_complete_global(mcp, buf);
2923b6e0a59SMatt Amdur goto out;
2933b6e0a59SMatt Amdur }
2943b6e0a59SMatt Amdur
2953b6e0a59SMatt Amdur /*
2963b6e0a59SMatt Amdur * Check to see if the buffer contains a valid dcmd
2973b6e0a59SMatt Amdur */
2983b6e0a59SMatt Amdur cp = mdb_dcmd_lookup(dcmd);
2993b6e0a59SMatt Amdur
3003b6e0a59SMatt Amdur /*
3013b6e0a59SMatt Amdur * When argc is zero it indicates that we are trying to tab complete
30272a1114bSHenrik Mattsson * a dcmd or a global symbol. Note, that if there isn't the start of
30372a1114bSHenrik Mattsson * a dcmd, i.e. ::, then we will have already bailed in the call to
30472a1114bSHenrik Mattsson * tab_parse_buf.
3053b6e0a59SMatt Amdur */
3063b6e0a59SMatt Amdur if (cp == NULL && argc != 0) {
3073b6e0a59SMatt Amdur goto out;
3083b6e0a59SMatt Amdur }
3093b6e0a59SMatt Amdur
3103b6e0a59SMatt Amdur /*
3113b6e0a59SMatt Amdur * Invoke the command specific tab completion handler or the built in
3123b6e0a59SMatt Amdur * dcmd one if there is no dcmd.
3133b6e0a59SMatt Amdur */
3143b6e0a59SMatt Amdur if (cp == NULL)
3153b6e0a59SMatt Amdur (void) mdb_tab_complete_dcmd(mcp, dcmd);
3163b6e0a59SMatt Amdur else
3173b6e0a59SMatt Amdur mdb_call_tab(cp, mcp, flags, argc, argv);
3183b6e0a59SMatt Amdur
3193b6e0a59SMatt Amdur out:
3203b6e0a59SMatt Amdur return (mdb_tab_size(mcp));
3213b6e0a59SMatt Amdur }
3223b6e0a59SMatt Amdur
3233b6e0a59SMatt Amdur static int
tab_complete_dcmd(mdb_var_t * v,void * arg)3243b6e0a59SMatt Amdur tab_complete_dcmd(mdb_var_t *v, void *arg)
3253b6e0a59SMatt Amdur {
3263b6e0a59SMatt Amdur mdb_idcmd_t *idcp = mdb_nv_get_cookie(mdb_nv_get_cookie(v));
3273b6e0a59SMatt Amdur mdb_tab_cookie_t *mcp = (mdb_tab_cookie_t *)arg;
3283b6e0a59SMatt Amdur
3293b6e0a59SMatt Amdur /*
3303b6e0a59SMatt Amdur * The way that mdb is implemented, even commands like $C will show up
3313b6e0a59SMatt Amdur * here. As such, we don't want to match anything that doesn't start
3323b6e0a59SMatt Amdur * with an alpha or number. While nothing currently appears (via a
3333b6e0a59SMatt Amdur * cursory search with mdb -k) to start with a capital letter or a
3343b6e0a59SMatt Amdur * number, we'll support them anyways.
3353b6e0a59SMatt Amdur */
3363b6e0a59SMatt Amdur if (!isalnum(idcp->idc_name[0]))
3373b6e0a59SMatt Amdur return (0);
3383b6e0a59SMatt Amdur
3393b6e0a59SMatt Amdur mdb_tab_insert(mcp, idcp->idc_name);
3403b6e0a59SMatt Amdur return (0);
3413b6e0a59SMatt Amdur }
3423b6e0a59SMatt Amdur
3433b6e0a59SMatt Amdur int
mdb_tab_complete_dcmd(mdb_tab_cookie_t * mcp,const char * dcmd)3443b6e0a59SMatt Amdur mdb_tab_complete_dcmd(mdb_tab_cookie_t *mcp, const char *dcmd)
3453b6e0a59SMatt Amdur {
3462f045fd6SRobert Mustacchi if (dcmd != NULL)
3472f045fd6SRobert Mustacchi mdb_tab_setmbase(mcp, dcmd);
3483b6e0a59SMatt Amdur mdb_nv_sort_iter(&mdb.m_dcmds, tab_complete_dcmd, mcp,
3493b6e0a59SMatt Amdur UM_GC | UM_SLEEP);
3503b6e0a59SMatt Amdur return (0);
3513b6e0a59SMatt Amdur }
3523b6e0a59SMatt Amdur
3533b6e0a59SMatt Amdur static int
tab_complete_walker(mdb_var_t * v,void * arg)3543b6e0a59SMatt Amdur tab_complete_walker(mdb_var_t *v, void *arg)
3553b6e0a59SMatt Amdur {
3563b6e0a59SMatt Amdur mdb_iwalker_t *iwp = mdb_nv_get_cookie(mdb_nv_get_cookie(v));
3573b6e0a59SMatt Amdur mdb_tab_cookie_t *mcp = arg;
3583b6e0a59SMatt Amdur
3593b6e0a59SMatt Amdur mdb_tab_insert(mcp, iwp->iwlk_name);
3603b6e0a59SMatt Amdur return (0);
3613b6e0a59SMatt Amdur }
3623b6e0a59SMatt Amdur
3633b6e0a59SMatt Amdur int
mdb_tab_complete_walker(mdb_tab_cookie_t * mcp,const char * walker)3643b6e0a59SMatt Amdur mdb_tab_complete_walker(mdb_tab_cookie_t *mcp, const char *walker)
3653b6e0a59SMatt Amdur {
3663b6e0a59SMatt Amdur if (walker != NULL)
3673b6e0a59SMatt Amdur mdb_tab_setmbase(mcp, walker);
3683b6e0a59SMatt Amdur mdb_nv_sort_iter(&mdb.m_walkers, tab_complete_walker, mcp,
3693b6e0a59SMatt Amdur UM_GC | UM_SLEEP);
3703b6e0a59SMatt Amdur
3713b6e0a59SMatt Amdur return (0);
3723b6e0a59SMatt Amdur }
3733b6e0a59SMatt Amdur
3743b6e0a59SMatt Amdur mdb_tab_cookie_t *
mdb_tab_init(void)3753b6e0a59SMatt Amdur mdb_tab_init(void)
3763b6e0a59SMatt Amdur {
3773b6e0a59SMatt Amdur mdb_tab_cookie_t *mcp;
3783b6e0a59SMatt Amdur
3793b6e0a59SMatt Amdur mcp = mdb_zalloc(sizeof (mdb_tab_cookie_t), UM_SLEEP | UM_GC);
3803b6e0a59SMatt Amdur (void) mdb_nv_create(&mcp->mtc_nv, UM_SLEEP | UM_GC);
3813b6e0a59SMatt Amdur
3823b6e0a59SMatt Amdur return (mcp);
3833b6e0a59SMatt Amdur }
3843b6e0a59SMatt Amdur
3853b6e0a59SMatt Amdur size_t
mdb_tab_size(mdb_tab_cookie_t * mcp)3863b6e0a59SMatt Amdur mdb_tab_size(mdb_tab_cookie_t *mcp)
3873b6e0a59SMatt Amdur {
3883b6e0a59SMatt Amdur return (mdb_nv_size(&mcp->mtc_nv));
3893b6e0a59SMatt Amdur }
3903b6e0a59SMatt Amdur
3913b6e0a59SMatt Amdur /*
3923b6e0a59SMatt Amdur * Determine whether the specified name is a valid tab completion for
3933b6e0a59SMatt Amdur * the given command. If the name is a valid tab completion then
3943b6e0a59SMatt Amdur * it will be saved in the mdb_tab_cookie_t.
3953b6e0a59SMatt Amdur */
3963b6e0a59SMatt Amdur void
mdb_tab_insert(mdb_tab_cookie_t * mcp,const char * name)3973b6e0a59SMatt Amdur mdb_tab_insert(mdb_tab_cookie_t *mcp, const char *name)
3983b6e0a59SMatt Amdur {
399065c692aSJosef 'Jeff' Sipek size_t matches, index;
4003b6e0a59SMatt Amdur mdb_var_t *v;
4013b6e0a59SMatt Amdur
4023b6e0a59SMatt Amdur /*
4033b6e0a59SMatt Amdur * If we have a match set, then we want to verify that we actually match
4043b6e0a59SMatt Amdur * it.
4053b6e0a59SMatt Amdur */
406*d75f3745SJohn Levon if (strncmp(name, mcp->mtc_base, strlen(mcp->mtc_base)) != 0)
4073b6e0a59SMatt Amdur return;
4083b6e0a59SMatt Amdur
4093b6e0a59SMatt Amdur v = mdb_nv_lookup(&mcp->mtc_nv, name);
4103b6e0a59SMatt Amdur if (v != NULL)
4113b6e0a59SMatt Amdur return;
4123b6e0a59SMatt Amdur
413065c692aSJosef 'Jeff' Sipek (void) mdb_nv_insert(&mcp->mtc_nv, name, NULL, 0, MDB_NV_RDONLY);
4143b6e0a59SMatt Amdur
4153b6e0a59SMatt Amdur matches = mdb_tab_size(mcp);
4163b6e0a59SMatt Amdur if (matches == 1) {
417065c692aSJosef 'Jeff' Sipek (void) strlcpy(mcp->mtc_match, name, MDB_SYM_NAMLEN);
4183b6e0a59SMatt Amdur } else {
4193b6e0a59SMatt Amdur index = 0;
4203b6e0a59SMatt Amdur while (mcp->mtc_match[index] &&
421065c692aSJosef 'Jeff' Sipek mcp->mtc_match[index] == name[index])
4223b6e0a59SMatt Amdur index++;
4233b6e0a59SMatt Amdur
4243b6e0a59SMatt Amdur mcp->mtc_match[index] = '\0';
4253b6e0a59SMatt Amdur }
4263b6e0a59SMatt Amdur }
4273b6e0a59SMatt Amdur
4283b6e0a59SMatt Amdur /*ARGSUSED*/
4293b6e0a59SMatt Amdur static int
tab_print_cb(mdb_var_t * v,void * ignored)4303b6e0a59SMatt Amdur tab_print_cb(mdb_var_t *v, void *ignored)
4313b6e0a59SMatt Amdur {
4323b6e0a59SMatt Amdur mdb_printf("%s\n", mdb_nv_get_name(v));
4333b6e0a59SMatt Amdur return (0);
4343b6e0a59SMatt Amdur }
4353b6e0a59SMatt Amdur
4363b6e0a59SMatt Amdur void
mdb_tab_print(mdb_tab_cookie_t * mcp)4373b6e0a59SMatt Amdur mdb_tab_print(mdb_tab_cookie_t *mcp)
4383b6e0a59SMatt Amdur {
4393b6e0a59SMatt Amdur mdb_nv_sort_iter(&mcp->mtc_nv, tab_print_cb, NULL, UM_SLEEP | UM_GC);
4403b6e0a59SMatt Amdur }
4413b6e0a59SMatt Amdur
4423b6e0a59SMatt Amdur const char *
mdb_tab_match(mdb_tab_cookie_t * mcp)4433b6e0a59SMatt Amdur mdb_tab_match(mdb_tab_cookie_t *mcp)
4443b6e0a59SMatt Amdur {
445*d75f3745SJohn Levon return (mcp->mtc_match + strlen(mcp->mtc_base));
4463b6e0a59SMatt Amdur }
4473b6e0a59SMatt Amdur
4483b6e0a59SMatt Amdur void
mdb_tab_setmbase(mdb_tab_cookie_t * mcp,const char * base)4493b6e0a59SMatt Amdur mdb_tab_setmbase(mdb_tab_cookie_t *mcp, const char *base)
4503b6e0a59SMatt Amdur {
4513b6e0a59SMatt Amdur (void) strlcpy(mcp->mtc_base, base, MDB_SYM_NAMLEN);
4523b6e0a59SMatt Amdur }
4533b6e0a59SMatt Amdur
4543b6e0a59SMatt Amdur /*
4553b6e0a59SMatt Amdur * This function is currently a no-op due to the fact that we have to GC because
4563b6e0a59SMatt Amdur * we're in command context.
4573b6e0a59SMatt Amdur */
4583b6e0a59SMatt Amdur /*ARGSUSED*/
4593b6e0a59SMatt Amdur void
mdb_tab_fini(mdb_tab_cookie_t * mcp)4603b6e0a59SMatt Amdur mdb_tab_fini(mdb_tab_cookie_t *mcp)
4613b6e0a59SMatt Amdur {
4623b6e0a59SMatt Amdur }
4633b6e0a59SMatt Amdur
46472a1114bSHenrik Mattsson /*ARGSUSED*/
46572a1114bSHenrik Mattsson static int
tab_complete_global(void * arg,const GElf_Sym * sym,const char * name,const mdb_syminfo_t * sip,const char * obj)46672a1114bSHenrik Mattsson tab_complete_global(void *arg, const GElf_Sym *sym, const char *name,
46772a1114bSHenrik Mattsson const mdb_syminfo_t *sip, const char *obj)
46872a1114bSHenrik Mattsson {
46972a1114bSHenrik Mattsson mdb_tab_cookie_t *mcp = arg;
47072a1114bSHenrik Mattsson mdb_tab_insert(mcp, name);
47172a1114bSHenrik Mattsson return (0);
47272a1114bSHenrik Mattsson }
47372a1114bSHenrik Mattsson
47472a1114bSHenrik Mattsson /*
47572a1114bSHenrik Mattsson * This function tab completes against all loaded global symbols.
47672a1114bSHenrik Mattsson */
47772a1114bSHenrik Mattsson int
mdb_tab_complete_global(mdb_tab_cookie_t * mcp,const char * name)47872a1114bSHenrik Mattsson mdb_tab_complete_global(mdb_tab_cookie_t *mcp, const char *name)
47972a1114bSHenrik Mattsson {
48072a1114bSHenrik Mattsson mdb_tab_setmbase(mcp, name);
48172a1114bSHenrik Mattsson (void) mdb_tgt_symbol_iter(mdb.m_target, MDB_TGT_OBJ_EVERY,
48222ce0148SMatthew Ahrens MDB_TGT_SYMTAB, MDB_TGT_BIND_ANY | MDB_TGT_TYPE_OBJECT |
48372a1114bSHenrik Mattsson MDB_TGT_TYPE_FUNC, tab_complete_global, mcp);
48472a1114bSHenrik Mattsson return (0);
48572a1114bSHenrik Mattsson }
48672a1114bSHenrik Mattsson
4873b6e0a59SMatt Amdur /*
4883b6e0a59SMatt Amdur * This function takes a ctf id and determines whether or not the associated
4893b6e0a59SMatt Amdur * type should be considered as a potential match for the given tab
4903b6e0a59SMatt Amdur * completion command. We verify that the type itself is valid
4913b6e0a59SMatt Amdur * for completion given the current context of the command, resolve
4923b6e0a59SMatt Amdur * its actual name, and then pass it off to mdb_tab_insert to determine
4933b6e0a59SMatt Amdur * if it's an actual match.
4943b6e0a59SMatt Amdur */
4953b6e0a59SMatt Amdur static int
tab_complete_type(mdb_ctf_id_t id,void * arg)4963b6e0a59SMatt Amdur tab_complete_type(mdb_ctf_id_t id, void *arg)
4973b6e0a59SMatt Amdur {
4983b6e0a59SMatt Amdur int rkind;
4993b6e0a59SMatt Amdur char buf[MDB_SYM_NAMLEN];
5003b6e0a59SMatt Amdur mdb_ctf_id_t rid;
5013b6e0a59SMatt Amdur mdb_tab_cookie_t *mcp = arg;
5023b6e0a59SMatt Amdur uint_t flags = (uint_t)(uintptr_t)mcp->mtc_cba;
5033b6e0a59SMatt Amdur
5043b6e0a59SMatt Amdur /*
5053b6e0a59SMatt Amdur * CTF data includes types that mdb commands don't understand. Before
5063b6e0a59SMatt Amdur * we resolve the actual type prune any entry that is a type we
5073b6e0a59SMatt Amdur * don't care about.
5083b6e0a59SMatt Amdur */
5093b6e0a59SMatt Amdur switch (mdb_ctf_type_kind(id)) {
5103b6e0a59SMatt Amdur case CTF_K_CONST:
5113b6e0a59SMatt Amdur case CTF_K_RESTRICT:
5123b6e0a59SMatt Amdur case CTF_K_VOLATILE:
5133b6e0a59SMatt Amdur return (0);
5143b6e0a59SMatt Amdur }
5153b6e0a59SMatt Amdur
5163b6e0a59SMatt Amdur if (mdb_ctf_type_resolve(id, &rid) != 0)
5173b6e0a59SMatt Amdur return (1);
5183b6e0a59SMatt Amdur
5193b6e0a59SMatt Amdur rkind = mdb_ctf_type_kind(rid);
5203b6e0a59SMatt Amdur
5213b6e0a59SMatt Amdur if ((flags & MDB_TABC_MEMBERS) && rkind != CTF_K_STRUCT &&
5223b6e0a59SMatt Amdur rkind != CTF_K_UNION)
5233b6e0a59SMatt Amdur return (0);
5243b6e0a59SMatt Amdur
5253b6e0a59SMatt Amdur if ((flags & MDB_TABC_NOPOINT) && rkind == CTF_K_POINTER)
5263b6e0a59SMatt Amdur return (0);
5273b6e0a59SMatt Amdur
5283b6e0a59SMatt Amdur if ((flags & MDB_TABC_NOARRAY) && rkind == CTF_K_ARRAY)
5293b6e0a59SMatt Amdur return (0);
5303b6e0a59SMatt Amdur
5313b6e0a59SMatt Amdur (void) mdb_ctf_type_name(id, buf, sizeof (buf));
5323b6e0a59SMatt Amdur
5333b6e0a59SMatt Amdur mdb_tab_insert(mcp, buf);
5343b6e0a59SMatt Amdur return (0);
5353b6e0a59SMatt Amdur }
5363b6e0a59SMatt Amdur
5373b6e0a59SMatt Amdur /*ARGSUSED*/
5383b6e0a59SMatt Amdur static int
mdb_tab_complete_module(void * data,const mdb_map_t * mp,const char * name)5393b6e0a59SMatt Amdur mdb_tab_complete_module(void *data, const mdb_map_t *mp, const char *name)
5403b6e0a59SMatt Amdur {
5413b6e0a59SMatt Amdur (void) mdb_ctf_type_iter(name, tab_complete_type, data);
5423b6e0a59SMatt Amdur return (0);
5433b6e0a59SMatt Amdur }
5443b6e0a59SMatt Amdur
5453b6e0a59SMatt Amdur int
mdb_tab_complete_type(mdb_tab_cookie_t * mcp,const char * name,uint_t flags)5463b6e0a59SMatt Amdur mdb_tab_complete_type(mdb_tab_cookie_t *mcp, const char *name, uint_t flags)
5473b6e0a59SMatt Amdur {
5483b6e0a59SMatt Amdur mdb_tgt_t *t = mdb.m_target;
5493b6e0a59SMatt Amdur
5503b6e0a59SMatt Amdur mcp->mtc_cba = (void *)(uintptr_t)flags;
5513b6e0a59SMatt Amdur if (name != NULL)
5523b6e0a59SMatt Amdur mdb_tab_setmbase(mcp, name);
5533b6e0a59SMatt Amdur
5543b6e0a59SMatt Amdur (void) mdb_tgt_object_iter(t, mdb_tab_complete_module, mcp);
5550a47c91cSRobert Mustacchi (void) mdb_ctf_type_iter(MDB_CTF_SYNTHETIC_ITER, tab_complete_type,
5560a47c91cSRobert Mustacchi mcp);
5573b6e0a59SMatt Amdur return (0);
5583b6e0a59SMatt Amdur }
5593b6e0a59SMatt Amdur
5603b6e0a59SMatt Amdur /*ARGSUSED*/
5613b6e0a59SMatt Amdur static int
tab_complete_member(const char * name,mdb_ctf_id_t id,ulong_t off,void * arg)5623b6e0a59SMatt Amdur tab_complete_member(const char *name, mdb_ctf_id_t id, ulong_t off, void *arg)
5633b6e0a59SMatt Amdur {
5643b6e0a59SMatt Amdur mdb_tab_cookie_t *mcp = arg;
5653b6e0a59SMatt Amdur mdb_tab_insert(mcp, name);
5663b6e0a59SMatt Amdur return (0);
5673b6e0a59SMatt Amdur }
5683b6e0a59SMatt Amdur
5693b6e0a59SMatt Amdur int
mdb_tab_complete_member_by_id(mdb_tab_cookie_t * mcp,mdb_ctf_id_t id,const char * member)5703b6e0a59SMatt Amdur mdb_tab_complete_member_by_id(mdb_tab_cookie_t *mcp, mdb_ctf_id_t id,
5713b6e0a59SMatt Amdur const char *member)
5723b6e0a59SMatt Amdur {
5733b6e0a59SMatt Amdur if (member != NULL)
5743b6e0a59SMatt Amdur mdb_tab_setmbase(mcp, member);
5753b6e0a59SMatt Amdur (void) mdb_ctf_member_iter(id, tab_complete_member, mcp);
5763b6e0a59SMatt Amdur return (0);
5773b6e0a59SMatt Amdur }
5783b6e0a59SMatt Amdur
5793b6e0a59SMatt Amdur int
mdb_tab_complete_member(mdb_tab_cookie_t * mcp,const char * type,const char * member)5803b6e0a59SMatt Amdur mdb_tab_complete_member(mdb_tab_cookie_t *mcp, const char *type,
5813b6e0a59SMatt Amdur const char *member)
5823b6e0a59SMatt Amdur {
5833b6e0a59SMatt Amdur mdb_ctf_id_t id;
5843b6e0a59SMatt Amdur
5853b6e0a59SMatt Amdur if (mdb_ctf_lookup_by_name(type, &id) != 0)
5863b6e0a59SMatt Amdur return (-1);
5873b6e0a59SMatt Amdur
5883b6e0a59SMatt Amdur return (mdb_tab_complete_member_by_id(mcp, id, member));
5893b6e0a59SMatt Amdur }
5903b6e0a59SMatt Amdur
5913b6e0a59SMatt Amdur int
mdb_tab_complete_mt(mdb_tab_cookie_t * mcp,uint_t flags,int argc,const mdb_arg_t * argv)5923b6e0a59SMatt Amdur mdb_tab_complete_mt(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
5933b6e0a59SMatt Amdur const mdb_arg_t *argv)
5943b6e0a59SMatt Amdur {
5953b6e0a59SMatt Amdur char tn[MDB_SYM_NAMLEN];
5963b6e0a59SMatt Amdur int ret;
5973b6e0a59SMatt Amdur
5983b6e0a59SMatt Amdur if (argc == 0 && !(flags & DCMD_TAB_SPACE))
5993b6e0a59SMatt Amdur return (0);
6003b6e0a59SMatt Amdur
6013b6e0a59SMatt Amdur if (argc == 0)
6023b6e0a59SMatt Amdur return (mdb_tab_complete_type(mcp, NULL, MDB_TABC_MEMBERS));
6033b6e0a59SMatt Amdur
6043b6e0a59SMatt Amdur if ((ret = mdb_tab_typename(&argc, &argv, tn, sizeof (tn))) < 0)
6053b6e0a59SMatt Amdur return (ret);
6063b6e0a59SMatt Amdur
6073b6e0a59SMatt Amdur if (argc == 1 && (!(flags & DCMD_TAB_SPACE) || ret == 1))
6083b6e0a59SMatt Amdur return (mdb_tab_complete_type(mcp, tn, MDB_TABC_MEMBERS));
6093b6e0a59SMatt Amdur
6103b6e0a59SMatt Amdur if (argc == 1 && (flags & DCMD_TAB_SPACE))
6113b6e0a59SMatt Amdur return (mdb_tab_complete_member(mcp, tn, NULL));
6123b6e0a59SMatt Amdur
6133b6e0a59SMatt Amdur if (argc == 2)
6143b6e0a59SMatt Amdur return (mdb_tab_complete_member(mcp, tn, argv[1].a_un.a_str));
6153b6e0a59SMatt Amdur
6163b6e0a59SMatt Amdur return (0);
6173b6e0a59SMatt Amdur }
6183b6e0a59SMatt Amdur
6193b6e0a59SMatt Amdur /*
6203b6e0a59SMatt Amdur * This is similar to mdb_print.c's args_to_typename, but it has subtle
6213b6e0a59SMatt Amdur * differences surrounding how the strings of one element are handled that have
6223b6e0a59SMatt Amdur * 'struct', 'enum', or 'union' in them and instead works with them for tab
6233b6e0a59SMatt Amdur * completion purposes.
6243b6e0a59SMatt Amdur */
6253b6e0a59SMatt Amdur int
mdb_tab_typename(int * argcp,const mdb_arg_t ** argvp,char * buf,size_t len)6263b6e0a59SMatt Amdur mdb_tab_typename(int *argcp, const mdb_arg_t **argvp, char *buf, size_t len)
6273b6e0a59SMatt Amdur {
6283b6e0a59SMatt Amdur int argc = *argcp;
6293b6e0a59SMatt Amdur const mdb_arg_t *argv = *argvp;
6303b6e0a59SMatt Amdur
6313b6e0a59SMatt Amdur if (argc < 1 || argv->a_type != MDB_TYPE_STRING)
6323b6e0a59SMatt Amdur return (DCMD_USAGE);
6333b6e0a59SMatt Amdur
6343b6e0a59SMatt Amdur if (strcmp(argv->a_un.a_str, "struct") == 0 ||
6353b6e0a59SMatt Amdur strcmp(argv->a_un.a_str, "enum") == 0 ||
6363b6e0a59SMatt Amdur strcmp(argv->a_un.a_str, "union") == 0) {
6373b6e0a59SMatt Amdur if (argc == 1) {
6383b6e0a59SMatt Amdur (void) mdb_snprintf(buf, len, "%s ",
6393b6e0a59SMatt Amdur argv[0].a_un.a_str);
6403b6e0a59SMatt Amdur return (1);
6413b6e0a59SMatt Amdur }
6423b6e0a59SMatt Amdur
6433b6e0a59SMatt Amdur if (argv[1].a_type != MDB_TYPE_STRING)
6443b6e0a59SMatt Amdur return (DCMD_USAGE);
6453b6e0a59SMatt Amdur
6463b6e0a59SMatt Amdur (void) mdb_snprintf(buf, len, "%s %s",
6473b6e0a59SMatt Amdur argv[0].a_un.a_str, argv[1].a_un.a_str);
6483b6e0a59SMatt Amdur
6493b6e0a59SMatt Amdur *argcp = argc - 1;
6503b6e0a59SMatt Amdur *argvp = argv + 1;
6513b6e0a59SMatt Amdur } else {
6523b6e0a59SMatt Amdur (void) mdb_snprintf(buf, len, "%s", argv[0].a_un.a_str);
6533b6e0a59SMatt Amdur }
6543b6e0a59SMatt Amdur
6553b6e0a59SMatt Amdur return (0);
6563b6e0a59SMatt Amdur }
657