1 /* radare - LGPL - Copyright 2009-2020 - pancake */
2 
3 #include <r_cmd.h>
4 #include <r_util.h>
5 #include <stdio.h>
6 #include <r_cons.h>
7 #include <r_cmd.h>
8 #include <r_util.h>
9 
10 /*!
11  * Number of sub-commands to show as options when displaying the help of a
12  * command. When a command has more options than MAX_CHILDREN_SHOW, `?` is shown
13  * instead.
14  *
15  * Example with MAX_CHILDREN_SHOW=3:
16  * w -> wa
17  *   -> wb
18  *   -> wc
19  *
20  * When doing `?`, you would see:
21  * w[abc]
22  *
23  * If there is also:
24  *   -> wd
25  * you would see:
26  * w[?]
27  */
28 #define MAX_CHILDREN_SHOW 7
29 
30 static const RCmdDescHelp not_defined_help = {
31 	.usage = "Usage not defined",
32 	.summary = "Help summary not defined",
33 	.description = "Help description not defined.",
34 };
35 
36 static const RCmdDescHelp root_help = {
37 	.usage = "[.][times][cmd][~grep][@[@iter]addr!size][|>pipe] ; ...",
38 	.description = "",
39 };
40 
41 static int value = 0;
42 
43 #define NCMDS (sizeof (cmd->cmds)/sizeof(*cmd->cmds))
44 R_LIB_VERSION (r_cmd);
45 
cmd_desc_set_parent(RCmdDesc * cd,RCmdDesc * parent)46 static bool cmd_desc_set_parent(RCmdDesc *cd, RCmdDesc *parent) {
47 	r_return_val_if_fail (cd && !cd->parent, false);
48 	if (parent) {
49 		cd->parent = parent;
50 		r_pvector_push (&parent->children, cd);
51 		parent->n_children++;
52 	}
53 	return true;
54 }
55 
cmd_desc_unset_parent(RCmdDesc * cd)56 static void cmd_desc_unset_parent(RCmdDesc *cd) {
57 	r_return_if_fail (cd && cd->parent);
58 	RCmdDesc *parent = cd->parent;
59 	r_pvector_remove_data (&parent->children, cd);
60 	parent->n_children--;
61 	cd->parent = NULL;
62 }
63 
cmd_desc_remove_from_ht_cmds(RCmd * cmd,RCmdDesc * cd)64 static void cmd_desc_remove_from_ht_cmds(RCmd *cmd, RCmdDesc *cd) {
65 	void **it_cd;
66 	bool res = ht_pp_delete (cmd->ht_cmds, cd->name);
67 	r_return_if_fail (res);
68 	r_cmd_desc_children_foreach (cd, it_cd) {
69 		RCmdDesc *child_cd = *it_cd;
70 		cmd_desc_remove_from_ht_cmds (cmd, child_cd);
71 	}
72 }
73 
cmd_desc_free(RCmdDesc * cd)74 static void cmd_desc_free(RCmdDesc *cd) {
75 	if (!cd) {
76 		return;
77 	}
78 
79 	r_pvector_clear (&cd->children);
80 	free (cd->name);
81 	free (cd);
82 }
83 
create_cmd_desc(RCmd * cmd,RCmdDesc * parent,RCmdDescType type,const char * name,const RCmdDescHelp * help,bool ht_insert)84 static RCmdDesc *create_cmd_desc(RCmd *cmd, RCmdDesc *parent, RCmdDescType type, const char *name, const RCmdDescHelp *help, bool ht_insert) {
85 	RCmdDesc *res = R_NEW0 (RCmdDesc);
86 	if (!res) {
87 		return NULL;
88 	}
89 	res->type = type;
90 	res->name = strdup (name);
91 	if (!res->name) {
92 		goto err;
93 	}
94 	res->n_children = 0;
95 	res->help = help? help: &not_defined_help;
96 	r_pvector_init (&res->children, (RPVectorFree)cmd_desc_free);
97 	if (ht_insert && !ht_pp_insert (cmd->ht_cmds, name, res)) {
98 		goto err;
99 	}
100 	cmd_desc_set_parent (res, parent);
101 	return res;
102 err:
103 	cmd_desc_free (res);
104 	return NULL;
105 }
106 
r_cmd_alias_init(RCmd * cmd)107 R_API void r_cmd_alias_init(RCmd *cmd) {
108 	cmd->aliases.count = 0;
109 	cmd->aliases.keys = NULL;
110 	cmd->aliases.values = NULL;
111 }
112 
r_cmd_new(void)113 R_API RCmd *r_cmd_new(void) {
114 	int i;
115 	RCmd *cmd = R_NEW0 (RCmd);
116 	if (!cmd) {
117 		return cmd;
118 	}
119 	cmd->lcmds = r_list_new ();
120 	for (i = 0; i < NCMDS; i++) {
121 		cmd->cmds[i] = NULL;
122 	}
123 	cmd->nullcallback = cmd->data = NULL;
124 	cmd->ht_cmds = ht_pp_new0 ();
125 	cmd->root_cmd_desc = create_cmd_desc (cmd, NULL, R_CMD_DESC_TYPE_ARGV, "", &root_help, true);
126 	r_core_plugin_init (cmd);
127 	r_cmd_macro_init (&cmd->macro);
128 	r_cmd_alias_init (cmd);
129 	return cmd;
130 }
131 
r_cmd_free(RCmd * cmd)132 R_API RCmd *r_cmd_free(RCmd *cmd) {
133 	int i;
134 	if (!cmd) {
135 		return NULL;
136 	}
137 	ht_up_free (cmd->ts_symbols_ht);
138 	r_cmd_alias_free (cmd);
139 	r_cmd_macro_fini (&cmd->macro);
140 	ht_pp_free (cmd->ht_cmds);
141 	// dinitialize plugin commands
142 	r_core_plugin_fini (cmd);
143 	r_list_free (cmd->plist);
144 	r_list_free (cmd->lcmds);
145 	for (i = 0; i < NCMDS; i++) {
146 		if (cmd->cmds[i]) {
147 			R_FREE (cmd->cmds[i]);
148 		}
149 	}
150 	cmd_desc_free (cmd->root_cmd_desc);
151 	free (cmd);
152 	return NULL;
153 }
154 
r_cmd_get_root(RCmd * cmd)155 R_API RCmdDesc *r_cmd_get_root(RCmd *cmd) {
156 	return cmd->root_cmd_desc;
157 }
158 
r_cmd_get_desc(RCmd * cmd,const char * cmd_identifier)159 R_API RCmdDesc *r_cmd_get_desc(RCmd *cmd, const char *cmd_identifier) {
160 	r_return_val_if_fail (cmd && cmd_identifier, NULL);
161 	char *cmdid = strdup (cmd_identifier);
162 	char *end_cmdid = cmdid + strlen (cmdid);
163 	RCmdDesc *res = NULL;
164 	bool is_exact_match = true;
165 	// match longer commands first
166 	while (*cmdid) {
167 		RCmdDesc *cd = ht_pp_find (cmd->ht_cmds, cmdid, NULL);
168 		if (cd) {
169 			switch (cd->type) {
170 			case R_CMD_DESC_TYPE_ARGV:
171 				if (!is_exact_match) {
172 					break;
173 				}
174 				// fallthrough
175 			case R_CMD_DESC_TYPE_GROUP:
176 				// fallthrough
177 			case R_CMD_DESC_TYPE_OLDINPUT:
178 				res = cd;
179 				goto out;
180 			case R_CMD_DESC_TYPE_INNER:
181 				break;
182 			}
183 		}
184 		is_exact_match = false;
185 		*(--end_cmdid) = '\0';
186 	}
187 out:
188 	free (cmdid);
189 	return res;
190 }
191 
r_cmd_alias_keys(RCmd * cmd,int * sz)192 R_API char **r_cmd_alias_keys(RCmd *cmd, int *sz) {
193 	if (sz) {
194 		*sz = cmd->aliases.count;
195 	}
196 	return cmd->aliases.keys;
197 }
198 
r_cmd_alias_free(RCmd * cmd)199 R_API void r_cmd_alias_free (RCmd *cmd) {
200 	int i; // find
201 	for (i = 0; i < cmd->aliases.count; i++) {
202 		free (cmd->aliases.keys[i]);
203 		free (cmd->aliases.values[i]);
204 	}
205 	cmd->aliases.count = 0;
206 	R_FREE (cmd->aliases.keys);
207 	R_FREE (cmd->aliases.values);
208 	free (cmd->aliases.remote);
209 }
210 
r_cmd_alias_del(RCmd * cmd,const char * k)211 R_API bool r_cmd_alias_del (RCmd *cmd, const char *k) {
212 	int i; // find
213 	for (i = 0; i < cmd->aliases.count; i++) {
214 		if (!k || !strcmp (k, cmd->aliases.keys[i])) {
215 			R_FREE (cmd->aliases.values[i]);
216 			cmd->aliases.count--;
217 			if (cmd->aliases.count > 0) {
218 				if (i > 0) {
219 					free (cmd->aliases.keys[i]);
220 					cmd->aliases.keys[i] = cmd->aliases.keys[0];
221 					free (cmd->aliases.values[i]);
222 					cmd->aliases.values[i] = cmd->aliases.values[0];
223 				}
224 				memmove (cmd->aliases.values,
225 					cmd->aliases.values + 1,
226 					cmd->aliases.count * sizeof (void*));
227 				memmove (cmd->aliases.keys,
228 					cmd->aliases.keys + 1,
229 					cmd->aliases.count * sizeof (void*));
230 			}
231 			return true;
232 		}
233 	}
234 	return false;
235 }
236 
237 // XXX: use a hashtable or any other standard data structure
r_cmd_alias_set(RCmd * cmd,const char * k,const char * v,int remote)238 R_API int r_cmd_alias_set(RCmd *cmd, const char *k, const char *v, int remote) {
239 	void *tofree = NULL;
240 	if (!strncmp (v, "base64:", 7)) {
241 		ut8 *s = r_base64_decode_dyn (v + 7, -1);
242 		if (s) {
243 			tofree = s;
244 			v = (const char *)s;
245 		}
246 	}
247 	int i;
248 	for (i = 0; i < cmd->aliases.count; i++) {
249 		int matches = !strcmp (k, cmd->aliases.keys[i]);
250 		if (matches) {
251 			free (cmd->aliases.values[i]);
252 			cmd->aliases.values[i] = strdup (v);
253 			free (tofree);
254 			return 1;
255 		}
256 	}
257 
258 	i = cmd->aliases.count++;
259 	char **K = (char **)realloc (cmd->aliases.keys,
260 				     sizeof (char *) * cmd->aliases.count);
261 	if (K) {
262 		cmd->aliases.keys = K;
263 		int *R = (int *)realloc (cmd->aliases.remote,
264 				sizeof (int) * cmd->aliases.count);
265 		if (R) {
266 			cmd->aliases.remote = R;
267 			char **V = (char **)realloc (cmd->aliases.values,
268 					sizeof (char *) * cmd->aliases.count);
269 			if (V) {
270 				cmd->aliases.values = V;
271 				cmd->aliases.keys[i] = strdup (k);
272 				cmd->aliases.values[i] = strdup (v);
273 				cmd->aliases.remote[i] = remote;
274 			}
275 		}
276 	}
277 	free (tofree);
278 	return 0;
279 }
280 
r_cmd_alias_get(RCmd * cmd,const char * k,int remote)281 R_API const char *r_cmd_alias_get(RCmd *cmd, const char *k, int remote) {
282 	int matches, i;
283 	if (!cmd || !k) {
284 		return NULL;
285 	}
286 	for (i = 0; i < cmd->aliases.count; i++) {
287 		matches = 0;
288 		if (remote) {
289 			if (cmd->aliases.remote[i]) {
290 				matches = !strncmp (k, cmd->aliases.keys[i],
291 					strlen (cmd->aliases.keys[i]));
292 			}
293 		} else {
294 			matches = !strcmp (k, cmd->aliases.keys[i]);
295 		}
296 		if (matches) {
297 			return cmd->aliases.values[i];
298 		}
299 	}
300 	return NULL;
301 }
302 
r_cmd_set_data(RCmd * cmd,void * data)303 R_API void r_cmd_set_data(RCmd *cmd, void *data) {
304 	cmd->data = data;
305 }
306 
r_cmd_add(RCmd * c,const char * cmd,RCmdCb cb)307 R_API bool r_cmd_add(RCmd *c, const char *cmd, RCmdCb cb) {
308 	int idx = (ut8)cmd[0];
309 	RCmdItem *item = c->cmds[idx];
310 	if (!item) {
311 		item = R_NEW0 (RCmdItem);
312 		c->cmds[idx] = item;
313 	}
314 	strncpy (item->cmd, cmd, sizeof (item->cmd)-1);
315 	item->callback = cb;
316 	return true;
317 }
318 
r_cmd_del(RCmd * cmd,const char * command)319 R_API void r_cmd_del(RCmd *cmd, const char *command) {
320 	int idx = (ut8)command[0];
321 	R_FREE (cmd->cmds[idx]);
322 }
323 
r_cmd_call(RCmd * cmd,const char * input)324 R_API int r_cmd_call(RCmd *cmd, const char *input) {
325 	struct r_cmd_item_t *c;
326 	int ret = -1;
327 	RListIter *iter;
328 	RCorePlugin *cp;
329 	r_return_val_if_fail (cmd && input, -1);
330 	if (!*input) {
331 		if (cmd->nullcallback) {
332 			ret = cmd->nullcallback (cmd->data);
333 		}
334 	} else {
335 		char *nstr = NULL;
336 		const char *ji = r_cmd_alias_get (cmd, input, 1);
337 		if (ji) {
338 			if (*ji == '$') {
339 				r_cons_strcat (ji + 1);
340 				return true;
341 			} else {
342 				nstr = r_str_newf ("=!%s", input);
343 				input = nstr;
344 			}
345 		}
346 		r_list_foreach (cmd->plist, iter, cp) {
347 			if (cp->call (cmd->data, input)) {
348 				free (nstr);
349 				return true;
350 			}
351 		}
352 		if (!*input) {
353 			free (nstr);
354 			return -1;
355 		}
356 		c = cmd->cmds[((ut8)input[0]) & 0xff];
357 		if (c && c->callback) {
358 			const char *inp = (*input)? input + 1: "";
359 			ret = c->callback (cmd->data, inp);
360 		} else {
361 			ret = -1;
362 		}
363 		free (nstr);
364 	}
365 	return ret;
366 }
367 
int2cmdstatus(int v)368 static RCmdStatus int2cmdstatus(int v) {
369 	if (v == -2) {
370 		return R_CMD_STATUS_EXIT;
371 	} else if (v < 0) {
372 		return R_CMD_STATUS_ERROR;
373 	} else {
374 		return R_CMD_STATUS_OK;
375 	}
376 }
377 
r_cmd_call_parsed_args(RCmd * cmd,RCmdParsedArgs * args)378 R_API RCmdStatus r_cmd_call_parsed_args(RCmd *cmd, RCmdParsedArgs *args) {
379 	RCmdStatus res = R_CMD_STATUS_INVALID;
380 
381 	// As old RCorePlugin do not register new commands in RCmd, we have no
382 	// way of knowing if one of those is able to handle the input, so we
383 	// have to pass the input to all of them before looking into the
384 	// RCmdDesc tree
385 	RListIter *iter;
386 	RCorePlugin *cp;
387 	char *exec_string = r_cmd_parsed_args_execstr (args);
388 	r_list_foreach (cmd->plist, iter, cp) {
389 		if (cp->call && cp->call (cmd->data, exec_string)) {
390 			res = R_CMD_STATUS_OK;
391 			break;
392 		}
393 	}
394 	R_FREE (exec_string);
395 	if (res == R_CMD_STATUS_OK) {
396 		return res;
397 	}
398 
399 	RCmdDesc *cd = r_cmd_get_desc (cmd, r_cmd_parsed_args_cmd (args));
400 	if (!cd) {
401 		return R_CMD_STATUS_INVALID;
402 	}
403 
404 	res = R_CMD_STATUS_INVALID;
405 	switch (cd->type) {
406 	case R_CMD_DESC_TYPE_GROUP:
407 		if (!cd->d.group_data.exec_cd) {
408 			break;
409 		}
410 		cd = cd->d.group_data.exec_cd;
411 		// fallthrough
412 	case R_CMD_DESC_TYPE_ARGV:
413 		if (cd->d.argv_data.cb) {
414 			res = cd->d.argv_data.cb (cmd->data, args->argc, (const char **)args->argv);
415 		}
416 		break;
417 	case R_CMD_DESC_TYPE_OLDINPUT:
418 		exec_string = r_cmd_parsed_args_execstr (args);
419 		res = int2cmdstatus (cd->d.oldinput_data.cb (cmd->data, exec_string + strlen (cd->name)));
420 		R_FREE (exec_string);
421 		break;
422 	default:
423 		res = R_CMD_STATUS_INVALID;
424 		R_LOG_ERROR ("RCmdDesc type not handled\n");
425 		break;
426 	}
427 	return res;
428 }
429 
strlen0(const char * s)430 static size_t strlen0(const char *s) {
431 	return s? strlen (s): 0;
432 }
433 
fill_children_chars(RStrBuf * sb,RCmdDesc * cd)434 static void fill_children_chars(RStrBuf *sb, RCmdDesc *cd) {
435 	if (cd->help->options) {
436 		r_strbuf_append (sb, cd->help->options);
437 		return;
438 	}
439 
440 	RStrBuf csb;
441 	r_strbuf_init (&csb);
442 
443 	void **it;
444 	r_cmd_desc_children_foreach (cd, it) {
445 		RCmdDesc *child = *(RCmdDesc **)it;
446 		if (r_str_startswith (child->name, cd->name) && strlen (child->name) == strlen (cd->name) + 1) {
447 			r_strbuf_appendf (&csb, "%c", child->name[strlen (cd->name)]);
448 		}
449 	}
450 
451 	if (r_strbuf_is_empty (&csb) || r_strbuf_length (&csb) >= MAX_CHILDREN_SHOW) {
452 		r_strbuf_fini (&csb);
453 		r_strbuf_set (&csb, "?");
454 	}
455 
456 	if (!cd->n_children || r_cmd_desc_has_handler (cd)) {
457 		r_strbuf_prepend (&csb, "[");
458 		r_strbuf_append (&csb, "]");
459 	} else {
460 		r_strbuf_prepend (&csb, "<");
461 		r_strbuf_append (&csb, ">");
462 	}
463 	char *tmp = r_strbuf_drain_nofree (&csb);
464 	r_strbuf_append (sb, tmp);
465 	free (tmp);
466 }
467 
show_children_shortcut(RCmdDesc * cd)468 static bool show_children_shortcut(RCmdDesc *cd) {
469 	return cd->n_children || cd->help->options || cd->type == R_CMD_DESC_TYPE_OLDINPUT;
470 }
471 
fill_usage_strbuf(RStrBuf * sb,RCmdDesc * cd,bool use_color)472 static void fill_usage_strbuf(RStrBuf *sb, RCmdDesc *cd, bool use_color) {
473 	RCons *cons = r_cons_singleton ();
474 	const char *pal_label_color = use_color? cons->context->pal.label: "",
475 		   *pal_args_color = use_color? cons->context->pal.args: "",
476 		   *pal_input_color = use_color? cons->context->pal.input: "",
477 		   *pal_help_color = use_color? cons->context->pal.help: "",
478 		   *pal_reset = use_color? cons->context->pal.reset: "";
479 
480 	r_strbuf_appendf (sb, "%sUsage: %s", pal_label_color, pal_reset);
481 	if (cd->help->usage) {
482 		r_strbuf_appendf (sb, "%s%s%s", cd->help->usage, pal_args_color, pal_reset);
483 	} else {
484 		r_strbuf_appendf (sb, "%s%s", pal_input_color, cd->name);
485 		if (show_children_shortcut (cd)) {
486 			r_strbuf_append (sb, pal_reset);
487 			fill_children_chars (sb, cd);
488 		}
489 		if (R_STR_ISNOTEMPTY (cd->help->args_str)) {
490 			r_strbuf_appendf (sb, "%s%s%s", pal_args_color, cd->help->args_str, pal_reset);
491 		}
492 	}
493 	if (cd->help->summary) {
494 		r_strbuf_appendf (sb, "   %s# %s%s", pal_help_color, cd->help->summary, pal_reset);
495 	}
496 	r_strbuf_append (sb, "\n");
497 }
498 
calc_padding_len(RCmdDesc * cd)499 static size_t calc_padding_len(RCmdDesc *cd) {
500 	size_t name_len = strlen (cd->name);
501 	size_t args_len = 0;
502 	size_t children_length = 0;
503 	if (show_children_shortcut (cd)) {
504 		RStrBuf sb;
505 		r_strbuf_init (&sb);
506 		fill_children_chars (&sb, cd);
507 		children_length += r_strbuf_length (&sb);
508 		r_strbuf_fini (&sb);
509 	}
510 	if (R_STR_ISNOTEMPTY (cd->help->args_str)) {
511 		args_len = strlen0 (cd->help->args_str);
512 	}
513 	return name_len + args_len + children_length;
514 }
515 
update_max_len(RCmdDesc * cd,size_t max_len)516 static size_t update_max_len(RCmdDesc *cd, size_t max_len) {
517 	size_t val = calc_padding_len (cd);
518 	return val > max_len? val: max_len;
519 }
520 
print_child_help(RStrBuf * sb,RCmdDesc * cd,size_t max_len,bool use_color)521 static void print_child_help(RStrBuf *sb, RCmdDesc *cd, size_t max_len, bool use_color) {
522 	size_t str_len = calc_padding_len (cd);
523 	int padding = str_len < max_len? max_len - str_len: 0;
524 	const char *cd_summary = r_str_get (cd->help->summary);
525 
526 	RCons *cons = r_cons_singleton ();
527 	const char *pal_args_color = use_color? cons->context->pal.args: "",
528 		   *pal_opt_color = use_color? cons->context->pal.reset: "",
529 		   *pal_help_color = use_color? cons->context->pal.help: "",
530 		   *pal_input_color = use_color? cons->context->pal.input: "",
531 		   *pal_reset = use_color? cons->context->pal.reset: "";
532 
533 	r_strbuf_appendf (sb, "| %s%s", pal_input_color, cd->name);
534 	if (show_children_shortcut (cd)) {
535 		r_strbuf_append (sb, pal_opt_color);
536 		fill_children_chars (sb, cd);
537 	}
538 	if (R_STR_ISNOTEMPTY (cd->help->args_str)) {
539 		r_strbuf_appendf (sb, "%s%s", pal_args_color, cd->help->args_str);
540 	}
541 	r_strbuf_appendf (sb, " %*s%s# %s%s\n", padding, "", pal_help_color, cd_summary, pal_reset);
542 }
543 
argv_group_get_help(RCmd * cmd,RCmdDesc * cd,bool use_color)544 static char *argv_group_get_help(RCmd *cmd, RCmdDesc *cd, bool use_color) {
545 	RStrBuf *sb = r_strbuf_new (NULL);
546 	fill_usage_strbuf (sb, cd, use_color);
547 
548 	void **it_cd;
549 	size_t max_len = 0;
550 
551 	r_cmd_desc_children_foreach (cd, it_cd) {
552 		RCmdDesc *child = *(RCmdDesc **)it_cd;
553 		max_len = update_max_len (child, max_len);
554 	}
555 
556 	r_cmd_desc_children_foreach (cd, it_cd) {
557 		RCmdDesc *child = *(RCmdDesc **)it_cd;
558 		print_child_help (sb, child, max_len, use_color);
559 	}
560 	return r_strbuf_drain (sb);
561 }
562 
argv_get_help(RCmd * cmd,RCmdDesc * cd,RCmdParsedArgs * a,size_t detail,bool use_color)563 static char *argv_get_help(RCmd *cmd, RCmdDesc *cd, RCmdParsedArgs *a, size_t detail, bool use_color) {
564 	RCons *cons = r_cons_singleton ();
565 	const char *pal_help_color = use_color? cons->context->pal.help: "",
566 		   *pal_input_color = use_color? cons->context->pal.input: "",
567 		   *pal_label_color = use_color? cons->context->pal.label: "",
568 		   *pal_reset = use_color? cons->context->pal.reset: "";
569 
570 	RStrBuf *sb = r_strbuf_new (NULL);
571 
572 	fill_usage_strbuf (sb, cd, use_color);
573 
574 	switch (detail) {
575 	case 1:
576 		return r_strbuf_drain (sb);
577 	case 2:
578 		if (cd->help->description) {
579 			r_strbuf_appendf (sb, "\n%s\n", cd->help->description);
580 		}
581 		if (cd->help->examples) {
582 			r_strbuf_appendf (sb, "\n%sExamples:%s\n", pal_label_color, pal_reset);
583 			const RCmdDescExample *it = cd->help->examples;
584 			while (it->example) {
585 				r_strbuf_appendf (sb, "| %s%s%s %s# %s%s\n", pal_input_color,
586 					it->example, pal_reset, pal_help_color, it->comment, pal_reset);
587 				it++;
588 			}
589 		}
590 		return r_strbuf_drain (sb);
591 	default:
592 		r_strbuf_free (sb);
593 		return NULL;
594 	}
595 }
596 
oldinput_get_help(RCmd * cmd,RCmdDesc * cd,RCmdParsedArgs * a)597 static char *oldinput_get_help(RCmd *cmd, RCmdDesc *cd, RCmdParsedArgs *a) {
598 	const char *s = NULL;
599 	r_cons_push ();
600 	RCmdStatus status = r_cmd_call_parsed_args (cmd, a);
601 	if (status == R_CMD_STATUS_OK) {
602 		r_cons_filter ();
603 		s = r_cons_get_buffer ();
604 	}
605 	char *res = strdup (r_str_get (s));
606 	r_cons_pop ();
607 	return res;
608 }
609 
r_cmd_get_help(RCmd * cmd,RCmdParsedArgs * args,bool use_color)610 R_API char *r_cmd_get_help(RCmd *cmd, RCmdParsedArgs *args, bool use_color) {
611 	char *cmdid = strdup (r_cmd_parsed_args_cmd (args));
612 	char *cmdid_p = cmdid + strlen (cmdid) - 1;
613 	size_t detail = 0;
614 	while (cmdid_p >= cmdid && *cmdid_p == '?') {
615 		*cmdid_p = '\0';
616 		cmdid_p--;
617 		detail++;
618 	}
619 
620 	if (detail == 0) {
621 		// there should be at least one `?`
622 		free (cmdid);
623 		return NULL;
624 	}
625 
626 	RCmdDesc *cd = cmdid_p >= cmdid? r_cmd_get_desc (cmd, cmdid): r_cmd_get_root (cmd);
627 	free (cmdid);
628 	if (!cd || !cd->help) {
629 		return NULL;
630 	}
631 
632 	switch (cd->type) {
633 	case R_CMD_DESC_TYPE_GROUP:
634 		if (detail > 1 && cd->d.group_data.exec_cd) {
635 			cd = cd->d.group_data.exec_cd;
636 		}
637 		// fallthrough
638 	case R_CMD_DESC_TYPE_ARGV:
639 		if (detail == 1 && !r_pvector_empty (&cd->children)) {
640 			if (args->argc > 1) {
641 				return NULL;
642 			}
643 			return argv_group_get_help (cmd, cd, use_color);
644 		}
645 		return argv_get_help (cmd, cd, args, detail, use_color);
646 	case R_CMD_DESC_TYPE_OLDINPUT:
647 		return oldinput_get_help (cmd, cd, args);
648 	case R_CMD_DESC_TYPE_INNER:
649 		r_warn_if_reached ();
650 		return NULL;
651 	}
652 	return NULL;
653 }
654 
655 /** macro.c **/
656 
r_cmd_macro_item_new(void)657 R_API RCmdMacroItem *r_cmd_macro_item_new(void) {
658 	return R_NEW0 (RCmdMacroItem);
659 }
660 
r_cmd_macro_item_free(RCmdMacroItem * item)661 R_API void r_cmd_macro_item_free(RCmdMacroItem *item) {
662 	if (!item) {
663 		return;
664 	}
665 	free (item->name);
666 	free (item->args);
667 	free (item->code);
668 	free (item);
669 }
670 
r_cmd_macro_init(RCmdMacro * mac)671 R_API void r_cmd_macro_init(RCmdMacro *mac) {
672 	mac->counter = 0;
673 	mac->_brk_value = 0;
674 	mac->brk_value = &mac->_brk_value;
675 	mac->cb_printf = (void *)printf;
676 	mac->num = NULL;
677 	mac->user = NULL;
678 	mac->cmd = NULL;
679 	mac->macros = r_list_newf ((RListFree)r_cmd_macro_item_free);
680 }
681 
r_cmd_macro_fini(RCmdMacro * mac)682 R_API void r_cmd_macro_fini(RCmdMacro *mac) {
683 	r_list_free (mac->macros);
684 	mac->macros = NULL;
685 }
686 
687 // XXX add support single line function definitions
688 // XXX add support for single name multiple nargs macros
r_cmd_macro_add(RCmdMacro * mac,const char * oname)689 R_API bool r_cmd_macro_add(RCmdMacro *mac, const char *oname) {
690 	struct r_cmd_macro_item_t *macro;
691 	char *name, *args = NULL;
692 	//char buf[R_CMD_MAXLEN];
693 	RCmdMacroItem *m;
694 	int macro_update;
695 	RListIter *iter;
696 	char *pbody;
697 	// char *bufp;
698 	char *ptr;
699 	int lidx;
700 
701 	if (!*oname) {
702 		r_cmd_macro_list (mac);
703 		return false;
704 	}
705 
706 	name = strdup (oname);
707 	if (!name) {
708 		return false;
709 	}
710 
711 	pbody = strchr (name, ';');
712 	if (!pbody) {
713 		eprintf ("Invalid macro body\n");
714 		free (name);
715 		return false;
716 	}
717 	*pbody = '\0';
718 	pbody++;
719 
720 	if (*name && name[1] && name[strlen (name)-1]==')') {
721 		eprintf ("r_cmd_macro_add: missing macro body?\n");
722 		free (name);
723 		return false;
724 	}
725 
726 	macro = NULL;
727 	ptr = strchr (name, ' ');
728 	if (ptr) {
729 		*ptr='\0';
730 		args = ptr +1;
731 	}
732 	macro_update = 0;
733 	r_list_foreach (mac->macros, iter, m) {
734 		if (!strcmp (name, m->name)) {
735 			macro = m;
736 			// keep macro->name
737 			free (macro->code);
738 			free (macro->args);
739 			macro_update = 1;
740 			break;
741 		}
742 	}
743 	if (ptr) {
744 		*ptr = ' ';
745 	}
746 	if (!macro) {
747 		macro = r_cmd_macro_item_new ();
748 		if (!macro) {
749 			free (name);
750 			return false;
751 		}
752 		macro->name = strdup (name);
753 	}
754 
755 	macro->codelen = (pbody[0])? strlen (pbody)+2 : 4096;
756 	macro->code = (char *)malloc (macro->codelen);
757 	*macro->code = '\0';
758 	macro->nargs = 0;
759 	if (!args) {
760 		args = "";
761 	}
762 	macro->args = strdup (args);
763 	ptr = strchr (macro->name, ' ');
764 	if (ptr != NULL) {
765 		*ptr = '\0';
766 		macro->nargs = r_str_word_set0 (ptr+1);
767 	}
768 
769 	for (lidx = 0; pbody[lidx]; lidx++) {
770 		if (pbody[lidx] == ';') {
771 			pbody[lidx] = '\n';
772 		} else if (pbody[lidx] == ')' && pbody[lidx - 1] == '\n') {
773 			pbody[lidx] = '\0';
774 		}
775 	}
776 	strncpy (macro->code, pbody, macro->codelen);
777 	macro->code[macro->codelen-1] = 0;
778 	if (macro_update == 0) {
779 		r_list_append (mac->macros, macro);
780 	}
781 	free (name);
782 	return true;
783 }
784 
r_cmd_macro_rm(RCmdMacro * mac,const char * _name)785 R_API bool r_cmd_macro_rm(RCmdMacro *mac, const char *_name) {
786 	r_return_val_if_fail (mac && _name, false);
787 	RListIter *iter;
788 	RCmdMacroItem *m;
789 	char *name = strdup (_name);
790 	if (!name) {
791 		return false;
792 	}
793 	char *ptr = strchr (name, ')');
794 	if (ptr) {
795 		*ptr = '\0';
796 	}
797 	bool ret = false;
798 	r_list_foreach (mac->macros, iter, m) {
799 		if (!strcmp (m->name, name)) {
800 			r_list_delete (mac->macros, iter);
801 			eprintf ("Macro '%s' removed.\n", name);
802 			ret = true;
803 			break;
804 		}
805 	}
806 	free (name);
807 	return ret;
808 }
809 
810 // TODO: use mac->cb_printf which is r_cons_printf at the end
r_cmd_macro_list(RCmdMacro * mac)811 R_API void r_cmd_macro_list(RCmdMacro *mac) {
812 	RCmdMacroItem *m;
813 	int j, idx = 0;
814 	RListIter *iter;
815 	r_list_foreach (mac->macros, iter, m) {
816 		mac->cb_printf ("%d (%s %s; ", idx, m->name, m->args);
817 		for (j=0; m->code[j]; j++) {
818 			if (m->code[j] == '\n') {
819 				mac->cb_printf ("; ");
820 			} else {
821 				mac->cb_printf ("%c", m->code[j]);
822 			}
823 		}
824 		mac->cb_printf (")\n");
825 		idx++;
826 	}
827 }
828 
829 // TODO: use mac->cb_printf which is r_cons_printf at the end
r_cmd_macro_meta(RCmdMacro * mac)830 R_API void r_cmd_macro_meta(RCmdMacro *mac) {
831 	RCmdMacroItem *m;
832 	int j;
833 	RListIter *iter;
834 	r_list_foreach (mac->macros, iter, m) {
835 		mac->cb_printf ("(%s %s, ", m->name, m->args);
836 		for (j=0; m->code[j]; j++) {
837 			if (m->code[j] == '\n') {
838 				mac->cb_printf ("; ");
839 			} else {
840 				mac->cb_printf ("%c", m->code[j]);
841 			}
842 		}
843 		mac->cb_printf (")\n");
844 	}
845 }
846 
847 #if 0
848 (define name value
849   f $0 @ $1)
850 
851 (define loop cmd
852   loop:
853   ? $0 == 0
854   ?? .loop:
855   )
856 
857 .(define patata 3)
858 #endif
859 
r_cmd_macro_cmd_args(RCmdMacro * mac,const char * ptr,const char * args,int nargs)860 R_API int r_cmd_macro_cmd_args(RCmdMacro *mac, const char *ptr, const char *args, int nargs) {
861 	int i, j;
862 	char *pcmd, cmd[R_CMD_MAXLEN];
863 	const char *arg = args;
864 
865 	for (*cmd=i=j=0; j<R_CMD_MAXLEN && ptr[j]; i++,j++) {
866 		if (ptr[j]=='$') {
867 			if (ptr[j+1]>='0' && ptr[j+1]<='9') {
868 				int wordlen;
869 				int w = ptr[j+1]-'0';
870 				const char *word = r_str_word_get0 (arg, w);
871 				if (word && *word) {
872 					wordlen = strlen (word);
873 					if ((i + wordlen + 1) >= sizeof (cmd)) {
874 						return -1;
875 					}
876 					memcpy (cmd+i, word, wordlen+1);
877 					i += wordlen-1;
878 					j++;
879 				} else {
880 					eprintf ("Undefined argument %d\n", w);
881 				}
882 			} else if (ptr[j+1]=='@') {
883 				char off[32];
884 				int offlen;
885 				offlen = snprintf (off, sizeof (off), "%d",
886 					mac->counter);
887 				if ((i + offlen + 1) >= sizeof (cmd)) {
888 					return -1;
889 				}
890 				memcpy (cmd+i, off, offlen+1);
891 				i += offlen-1;
892 				j++;
893 			} else {
894 				cmd[i] = ptr[j];
895 				cmd[i+1] = '\0';
896 			}
897 		} else {
898 			cmd[i] = ptr[j];
899 			cmd[i+1] = '\0';
900 		}
901 	}
902 	for (pcmd = cmd; *pcmd && (*pcmd == ' ' || *pcmd == '\t'); pcmd++) {
903 		;
904 	}
905 	//eprintf ("-pre %d\n", (int)mac->num->value);
906 	int xx = (*pcmd==')')? 0: mac->cmd (mac->user, pcmd);
907 	//eprintf ("-pos %p %d\n", mac->num, (int)mac->num->value);
908 	return xx;
909 }
910 
r_cmd_macro_label_process(RCmdMacro * mac,RCmdMacroLabel * labels,int * labels_n,char * ptr)911 R_API char *r_cmd_macro_label_process(RCmdMacro *mac, RCmdMacroLabel *labels, int *labels_n, char *ptr) {
912 	int i;
913 	for (; *ptr == ' '; ptr++) {
914 		;
915 	}
916 	if (ptr[strlen (ptr) - 1]==':' && !strchr (ptr, ' ')) {
917 		/* label detected */
918 		if (ptr[0] == '.') {
919 		//	eprintf("---> GOTO '%s'\n", ptr+1);
920 			/* goto */
921 			for (i = 0; i < *labels_n; i++) {
922 			//	eprintf("---| chk '%s'\n", labels[i].name);
923 				if (!strcmp (ptr + 1, labels[i].name)) {
924 					return labels[i].ptr;
925 				}
926 			}
927 			return NULL;
928 		} else
929 		/* conditional goto */
930 		if (ptr[0]=='?' && ptr[1]=='!' && ptr[2] != '?') {
931 			if (mac->num && mac->num->value != 0) {
932 				char *label = ptr + 3;
933 				for (; *label == ' ' || *label == '.'; label++) {
934 					;
935 				}
936 				// eprintf("===> GOTO %s\n", label);
937 				/* goto label ptr+3 */
938 				for (i = 0; i < *labels_n; i++) {
939 					if (!strcmp (label, labels[i].name)) {
940 						return labels[i].ptr;
941 					}
942 				}
943 				return NULL;
944 			}
945 		} else
946 		/* conditional goto */
947 		if (ptr[0] == '?' && ptr[1] == '?' && ptr[2] != '?') {
948 			if (mac->num->value == 0) {
949 				char *label = ptr + 3;
950 				for (; label[0] == ' ' || label[0] == '.'; label++) {
951 					;
952 				}
953 				//		eprintf("===> GOTO %s\n", label);
954 				/* goto label ptr+3 */
955 				for (i=0; i<*labels_n; i++) {
956 					if (!strcmp (label, labels[i].name)) {
957 						return labels[i].ptr;
958 					}
959 				}
960 				return NULL;
961 			}
962 		} else {
963 			for (i = 0; i < *labels_n; i++) {
964 		//	eprintf("---| chk '%s'\n", labels[i].name);
965 				if (!strcmp (ptr + 1, labels[i].name)) {
966 					i = 0;
967 					break;
968 				}
969 			}
970 			/* Add label */
971 		//	eprintf("===> ADD LABEL(%s)\n", ptr);
972 			if (i == 0) {
973 				strncpy (labels[*labels_n].name, ptr, 64);
974 				labels[*labels_n].ptr = ptr+strlen (ptr)+1;
975 				*labels_n = *labels_n + 1;
976 			}
977 		}
978 		ptr += strlen (ptr) + 1;
979 	}
980 	return ptr;
981 }
982 
983 /* TODO: add support for spaced arguments */
r_cmd_macro_call(RCmdMacro * mac,const char * name)984 R_API int r_cmd_macro_call(RCmdMacro *mac, const char *name) {
985 	char *args;
986 	int nargs = 0;
987 	char *str, *ptr, *ptr2;
988 	RListIter *iter;
989 	static int macro_level = 0;
990 	RCmdMacroItem *m;
991 	/* labels */
992 	int labels_n = 0;
993 	struct r_cmd_macro_label_t labels[MACRO_LABELS];
994 
995 	str = strdup (name);
996 	if (!str) {
997 		perror ("strdup");
998 		return false;
999 	}
1000 	ptr = strchr (str, ')');
1001 	if (!ptr) {
1002 		eprintf ("Missing end ')' parenthesis.\n");
1003 		free (str);
1004 		return false;
1005 	} else {
1006 		*ptr = '\0';
1007 	}
1008 
1009 	args = strchr (str, ' ');
1010 	if (args) {
1011 		*args = '\0';
1012 		args++;
1013 		nargs = r_str_word_set0 (args);
1014 	}
1015 
1016 	macro_level++;
1017 	if (macro_level > MACRO_LIMIT) {
1018 		eprintf ("Maximum macro recursivity reached.\n");
1019 		macro_level--;
1020 		free (str);
1021 		return 0;
1022 	}
1023 	ptr = strchr (str, ';');
1024 	if (ptr) {
1025 		*ptr = 0;
1026 	}
1027 
1028 	r_cons_break_push (NULL, NULL);
1029 	r_list_foreach (mac->macros, iter, m) {
1030 		if (!strcmp (str, m->name)) {
1031 			char *ptr = m->code;
1032 			char *end = strchr (ptr, '\n');
1033 			if (m->nargs != 0 && nargs != m->nargs) {
1034 				eprintf ("Macro '%s' expects %d args, not %d\n", m->name, m->nargs, nargs);
1035 				macro_level --;
1036 				free (str);
1037 				r_cons_break_pop ();
1038 				return false;
1039 			}
1040 			mac->brk = 0;
1041 			do {
1042 				if (end) {
1043 					*end = '\0';
1044 				}
1045 				if (r_cons_is_breaked ()) {
1046 					eprintf ("Interrupted at (%s)\n", ptr);
1047 					if (end) {
1048 						*end = '\n';
1049 					}
1050 					free (str);
1051 					r_cons_break_pop ();
1052 					return false;
1053 				}
1054 				r_cons_flush ();
1055 				/* Label handling */
1056 				ptr2 = r_cmd_macro_label_process (mac, &(labels[0]), &labels_n, ptr);
1057 				if (!ptr2) {
1058 					eprintf ("Oops. invalid label name\n");
1059 					break;
1060 				} else if (ptr != ptr2) {
1061 					ptr = ptr2;
1062 					if (end) {
1063 						*end = '\n';
1064 					}
1065 					end = strchr (ptr, '\n');
1066 					continue;
1067 				}
1068 				/* Command execution */
1069 				if (*ptr) {
1070 					mac->num->value = value;
1071 					int r = r_cmd_macro_cmd_args (mac, ptr, args, nargs);
1072 					// TODO: handle quit? r == 0??
1073 					// quit, exits the macro. like a break
1074 					value = mac->num->value;
1075 					if (r < 0) {
1076 						free (str);
1077 						r_cons_break_pop ();
1078 						return r;
1079 					}
1080 				}
1081 				if (end) {
1082 					*end = '\n';
1083 					ptr = end + 1;
1084 				} else {
1085 					macro_level --;
1086 					free (str);
1087 					goto out_clean;
1088 				}
1089 
1090 				/* Fetch next command */
1091 				end = strchr (ptr, '\n');
1092 			} while (!mac->brk);
1093 			if (mac->brk) {
1094 				macro_level--;
1095 				free (str);
1096 				goto out_clean;
1097 			}
1098 		}
1099 	}
1100 	eprintf ("No macro named '%s'\n", str);
1101 	macro_level--;
1102 	free (str);
1103 out_clean:
1104 	r_cons_break_pop ();
1105 	return true;
1106 }
1107 
r_cmd_macro_break(RCmdMacro * mac,const char * value)1108 R_API int r_cmd_macro_break(RCmdMacro *mac, const char *value) {
1109 	mac->brk = 1;
1110 	mac->brk_value = NULL;
1111 	mac->_brk_value = (ut64)r_num_math (mac->num, value);
1112 	if (value && *value) {
1113 		mac->brk_value = &mac->_brk_value;
1114 	}
1115 	return 0;
1116 }
1117 
1118 /* RCmdParsedArgs */
1119 
r_cmd_parsed_args_new(const char * cmd,int n_args,char ** args)1120 R_API RCmdParsedArgs *r_cmd_parsed_args_new(const char *cmd, int n_args, char **args) {
1121 	r_return_val_if_fail (cmd && n_args >= 0, NULL);
1122 	RCmdParsedArgs *res = R_NEW0 (RCmdParsedArgs);
1123 	res->has_space_after_cmd = true;
1124 	res->argc = n_args + 1;
1125 	res->argv = R_NEWS0 (char *, res->argc);
1126 	res->argv[0] = strdup(cmd);
1127 	int i;
1128 	for (i = 1; i < res->argc; i++) {
1129 		res->argv[i] = strdup (args[i - 1]);
1130 	}
1131 	return res;
1132 }
1133 
r_cmd_parsed_args_newcmd(const char * cmd)1134 R_API RCmdParsedArgs *r_cmd_parsed_args_newcmd(const char *cmd) {
1135 	return r_cmd_parsed_args_new (cmd, 0, NULL);
1136 }
1137 
r_cmd_parsed_args_newargs(int n_args,char ** args)1138 R_API RCmdParsedArgs *r_cmd_parsed_args_newargs(int n_args, char **args) {
1139 	return r_cmd_parsed_args_new ("", n_args, args);
1140 }
1141 
r_cmd_parsed_args_free(RCmdParsedArgs * a)1142 R_API void r_cmd_parsed_args_free(RCmdParsedArgs *a) {
1143 	if (!a) {
1144 		return;
1145 	}
1146 
1147 	int i;
1148 	for (i = 0; i < a->argc; i++) {
1149 		free (a->argv[i]);
1150 	}
1151 	free (a->argv);
1152 	free (a);
1153 }
1154 
free_array(char ** arr,int n)1155 static void free_array(char **arr, int n) {
1156 	int i;
1157 	for (i = 0; i < n; i++) {
1158 		free (arr[i]);
1159 	}
1160 	free (arr);
1161 }
1162 
r_cmd_parsed_args_setargs(RCmdParsedArgs * a,int n_args,char ** args)1163 R_API bool r_cmd_parsed_args_setargs(RCmdParsedArgs *a, int n_args, char **args) {
1164 	r_return_val_if_fail (a && a->argv && a->argv[0], false);
1165 	char **tmp = R_NEWS0 (char *, n_args + 1);
1166 	if (!tmp) {
1167 		return false;
1168 	}
1169 	tmp[0] = strdup (a->argv[0]);
1170 	int i;
1171 	for (i = 1; i < n_args + 1; i++) {
1172 		tmp[i] = strdup (args[i - 1]);
1173 		if (!tmp[i]) {
1174 			goto err;
1175 		}
1176 	}
1177 	free_array (a->argv, a->argc);
1178 	a->argv = tmp;
1179 	a->argc = n_args + 1;
1180 	return true;
1181 err:
1182 	free_array (tmp, n_args + 1);
1183 	return false;
1184 }
1185 
r_cmd_parsed_args_setcmd(RCmdParsedArgs * a,const char * cmd)1186 R_API bool r_cmd_parsed_args_setcmd(RCmdParsedArgs *a, const char *cmd) {
1187 	r_return_val_if_fail (a && a->argv && a->argv[0], false);
1188 	char *tmp = strdup (cmd);
1189 	if (!tmp) {
1190 		return false;
1191 	}
1192 	free (a->argv[0]);
1193 	a->argv[0] = tmp;
1194 	return true;
1195 }
1196 
parsed_args_iterateargs(RCmdParsedArgs * a,RStrBuf * sb)1197 static void parsed_args_iterateargs(RCmdParsedArgs *a, RStrBuf *sb) {
1198 	int i;
1199 	for (i = 1; i < a->argc; i++) {
1200 		if (i > 1) {
1201 			r_strbuf_append (sb, " ");
1202 		}
1203 		r_strbuf_append (sb, a->argv[i]);
1204 	}
1205 }
1206 
r_cmd_parsed_args_argstr(RCmdParsedArgs * a)1207 R_API char *r_cmd_parsed_args_argstr(RCmdParsedArgs *a) {
1208 	r_return_val_if_fail (a && a->argv && a->argv[0], NULL);
1209 	RStrBuf *sb = r_strbuf_new ("");
1210 	parsed_args_iterateargs (a, sb);
1211 	return r_strbuf_drain (sb);
1212 }
1213 
r_cmd_parsed_args_execstr(RCmdParsedArgs * a)1214 R_API char *r_cmd_parsed_args_execstr(RCmdParsedArgs *a) {
1215 	r_return_val_if_fail (a && a->argv && a->argv[0], NULL);
1216 	RStrBuf *sb = r_strbuf_new (a->argv[0]);
1217 	if (a->argc > 1 && a->has_space_after_cmd) {
1218 		r_strbuf_append (sb, " ");
1219 	}
1220 	parsed_args_iterateargs (a, sb);
1221 	return r_strbuf_drain (sb);
1222 }
1223 
r_cmd_parsed_args_cmd(RCmdParsedArgs * a)1224 R_API const char *r_cmd_parsed_args_cmd(RCmdParsedArgs *a) {
1225 	r_return_val_if_fail (a && a->argv && a->argv[0], NULL);
1226 	return a->argv[0];
1227 }
1228 
1229 /* RCmdDescriptor */
1230 
argv_new(RCmd * cmd,RCmdDesc * parent,const char * name,RCmdArgvCb cb,const RCmdDescHelp * help,bool ht_insert)1231 static RCmdDesc *argv_new(RCmd *cmd, RCmdDesc *parent, const char *name, RCmdArgvCb cb, const RCmdDescHelp *help, bool ht_insert) {
1232 	RCmdDesc *res = create_cmd_desc (cmd, parent, R_CMD_DESC_TYPE_ARGV, name, help, ht_insert);
1233 	if (!res) {
1234 		return NULL;
1235 	}
1236 
1237 	res->d.argv_data.cb = cb;
1238 	return res;
1239 }
1240 
r_cmd_desc_argv_new(RCmd * cmd,RCmdDesc * parent,const char * name,RCmdArgvCb cb,const RCmdDescHelp * help)1241 R_API RCmdDesc *r_cmd_desc_argv_new(RCmd *cmd, RCmdDesc *parent, const char *name, RCmdArgvCb cb, const RCmdDescHelp *help) {
1242 	r_return_val_if_fail (cmd && parent && name, NULL);
1243 	return argv_new (cmd, parent, name, cb, help, true);
1244 }
1245 
r_cmd_desc_inner_new(RCmd * cmd,RCmdDesc * parent,const char * name,const RCmdDescHelp * help)1246 R_API RCmdDesc *r_cmd_desc_inner_new(RCmd *cmd, RCmdDesc *parent, const char *name, const RCmdDescHelp *help) {
1247 	r_return_val_if_fail (cmd && parent && name, NULL);
1248 	return create_cmd_desc (cmd, parent, R_CMD_DESC_TYPE_INNER, name, help, false);
1249 }
1250 
r_cmd_desc_group_new(RCmd * cmd,RCmdDesc * parent,const char * name,RCmdArgvCb cb,const RCmdDescHelp * help,const RCmdDescHelp * group_help)1251 R_API RCmdDesc *r_cmd_desc_group_new(RCmd *cmd, RCmdDesc *parent, const char *name, RCmdArgvCb cb, const RCmdDescHelp *help, const RCmdDescHelp *group_help) {
1252 	r_return_val_if_fail (cmd && parent && name, NULL);
1253 	RCmdDesc *res = create_cmd_desc (cmd, parent, R_CMD_DESC_TYPE_GROUP, name, group_help, true);
1254 	if (!res) {
1255 		return NULL;
1256 	}
1257 
1258 	RCmdDesc *exec_cd = NULL;
1259 	if (cb && help) {
1260 		exec_cd = argv_new (cmd, res, name, cb, help, false);
1261 		if (!exec_cd) {
1262 			r_cmd_desc_remove (cmd, res);
1263 			return NULL;
1264 		}
1265 	}
1266 
1267 	res->d.group_data.exec_cd = exec_cd;
1268 	return res;
1269 }
1270 
r_cmd_desc_oldinput_new(RCmd * cmd,RCmdDesc * parent,const char * name,RCmdCb cb,const RCmdDescHelp * help)1271 R_API RCmdDesc *r_cmd_desc_oldinput_new(RCmd *cmd, RCmdDesc *parent, const char *name, RCmdCb cb, const RCmdDescHelp *help) {
1272 	r_return_val_if_fail (cmd && parent && name && cb, NULL);
1273 	RCmdDesc *res = create_cmd_desc (cmd, parent, R_CMD_DESC_TYPE_OLDINPUT, name, help, true);
1274 	if (!res) {
1275 		return NULL;
1276 	}
1277 	res->d.oldinput_data.cb = cb;
1278 	return res;
1279 }
1280 
r_cmd_desc_parent(RCmdDesc * cd)1281 R_API RCmdDesc *r_cmd_desc_parent(RCmdDesc *cd) {
1282 	r_return_val_if_fail (cd, NULL);
1283 	return cd->parent;
1284 }
1285 
r_cmd_desc_has_handler(RCmdDesc * cd)1286 R_API bool r_cmd_desc_has_handler(RCmdDesc *cd) {
1287 	r_return_val_if_fail (cd, false);
1288 	switch (cd->type) {
1289 	case R_CMD_DESC_TYPE_ARGV:
1290 		return cd->d.argv_data.cb;
1291 	case R_CMD_DESC_TYPE_OLDINPUT:
1292 		return cd->d.oldinput_data.cb;
1293 	case R_CMD_DESC_TYPE_INNER:
1294 		return false;
1295 	case R_CMD_DESC_TYPE_GROUP:
1296 		return cd->d.group_data.exec_cd && r_cmd_desc_has_handler (cd->d.group_data.exec_cd);
1297 	}
1298 	return false;
1299 }
1300 
r_cmd_desc_remove(RCmd * cmd,RCmdDesc * cd)1301 R_API bool r_cmd_desc_remove(RCmd *cmd, RCmdDesc *cd) {
1302 	r_return_val_if_fail (cmd && cd, false);
1303 	if (cd->parent) {
1304 		cmd_desc_unset_parent (cd);
1305 	}
1306 	cmd_desc_remove_from_ht_cmds (cmd, cd);
1307 	cmd_desc_free (cd);
1308 	return true;
1309 }
1310