work in progress to improve help
[openocd.git] / src / helper / command.c
1 /***************************************************************************
2 * Copyright (C) 2005 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
4 * *
5 * part of this file is taken from libcli (libcli.sourceforge.net) *
6 * Copyright (C) David Parrish (david@dparrish.com) *
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the *
20 * Free Software Foundation, Inc., *
21 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
22 ***************************************************************************/
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include "replacements.h"
28
29 #include "command.h"
30
31 #include "log.h"
32 #include "time_support.h"
33
34 #include <stdlib.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <unistd.h>
40
41 #include <openocd_tcl.h>
42
43 int fast_and_dangerous = 0;
44
45 void command_print_help_line(command_context_t* context, struct command_s *command, int indent);
46
47 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
48 int handle_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
49 int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
50
51 /* forward declaration of jim_command */
52 extern int jim_command(command_context_t *context, char *line);
53
54
55
56 command_t* register_command(command_context_t *context, command_t *parent, char *name, int (*handler)(struct command_context_s *context, char* name, char** args, int argc), enum command_mode mode, char *help)
57 {
58 command_t *c, *p;
59
60 if (!context || !name)
61 return NULL;
62
63 c = malloc(sizeof(command_t));
64
65 c->name = strdup(name);
66 c->parent = parent;
67 c->children = NULL;
68 c->handler = handler;
69 c->mode = mode;
70 if (!help)
71 help="";
72 c->help = strdup(help);
73 c->next = NULL;
74
75 /* place command in tree */
76 if (parent)
77 {
78 if (parent->children)
79 {
80 /* find last child */
81 for (p = parent->children; p && p->next; p = p->next);
82 if (p)
83 p->next = c;
84 }
85 else
86 {
87 parent->children = c;
88 }
89 }
90 else
91 {
92 if (context->commands)
93 {
94 /* find last command */
95 for (p = context->commands; p && p->next; p = p->next);
96 if (p)
97 p->next = c;
98 }
99 else
100 {
101 context->commands = c;
102 }
103 }
104 /* accumulate help text in Tcl helptext list. */
105 Jim_Obj *helptext=Jim_GetGlobalVariableStr(interp, "ocd_helptext", JIM_ERRMSG);
106 Jim_Obj *cmd_entry=Jim_NewListObj(interp, NULL, 0);
107
108 Jim_Obj *cmd_list=Jim_NewListObj(interp, NULL, 0);
109
110 /* maximum of two levels :-) */
111 if (c->parent!=NULL)
112 {
113 Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->parent->name, -1));
114 }
115 Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->name, -1));
116
117 Jim_ListAppendElement(interp, cmd_entry, cmd_list);
118 Jim_ListAppendElement(interp, cmd_entry, Jim_NewStringObj(interp, c->help, -1));
119 Jim_ListAppendElement(interp, helptext, cmd_entry);
120 return c;
121 }
122
123 int unregister_all_commands(command_context_t *context)
124 {
125 command_t *c, *c2;
126
127 if (context == NULL)
128 return ERROR_OK;
129
130
131 while(NULL != context->commands)
132 {
133 c = context->commands;
134
135 while(NULL != c->children)
136 {
137 c2 = c->children;
138 c->children = c->children->next;
139 free(c2->name);
140 c2->name = NULL;
141 free(c2->help);
142 c2->help = NULL;
143 free(c2);
144 c2 = NULL;
145 }
146
147 context->commands = context->commands->next;
148
149 free(c->name);
150 c->name = NULL;
151 free(c->help);
152 c->help = NULL;
153 free(c);
154 c = NULL;
155 }
156
157 return ERROR_OK;
158 }
159
160 int unregister_command(command_context_t *context, char *name)
161 {
162 command_t *c, *p = NULL, *c2;
163
164 if ((!context) || (!name))
165 return ERROR_INVALID_ARGUMENTS;
166
167 /* find command */
168 for (c = context->commands; c; c = c->next)
169 {
170 if (strcmp(name, c->name) == 0)
171 {
172 /* unlink command */
173 if (p)
174 {
175 p->next = c->next;
176 }
177 else
178 {
179 context->commands = c->next;
180 }
181
182 /* unregister children */
183 if (c->children)
184 {
185 for (c2 = c->children; c2; c2 = c2->next)
186 {
187 free(c2->name);
188 if (c2->help)
189 free(c2->help);
190 free(c2);
191 }
192 }
193
194 /* delete command */
195 free(c->name);
196 if (c->help)
197 free(c->help);
198 free(c);
199 }
200
201 /* remember the last command for unlinking */
202 p = c;
203 }
204
205 return ERROR_OK;
206 }
207
208 int parse_line(char *line, char *words[], int max_words)
209 {
210 int nwords = 0;
211 char *p = line;
212 char *word_start = line;
213 int inquote = 0;
214
215 while (nwords < max_words - 1)
216 {
217 /* check if we reached
218 * a terminating NUL
219 * a matching closing quote character " or '
220 * we're inside a word but not a quote, and the current character is whitespace
221 */
222 if (!*p || *p == inquote || (word_start && !inquote && isspace(*p)))
223 {
224 /* we're inside a word or quote, and reached its end*/
225 if (word_start)
226 {
227 int len;
228 char *word_end=p;
229
230 /* This will handle extra whitespace within quotes */
231 while (isspace(*word_start)&&(word_start<word_end))
232 word_start++;
233 while (isspace(*(word_end-1))&&(word_start<word_end))
234 word_end--;
235 len = word_end - word_start;
236
237 if (len>0)
238 {
239 /* copy the word */
240 memcpy(words[nwords] = malloc(len + 1), word_start, len);
241 /* add terminating NUL */
242 words[nwords++][len] = 0;
243 }
244 }
245 /* we're done parsing the line */
246 if (!*p)
247 break;
248
249 /* skip over trailing quote or whitespace*/
250 if (inquote || isspace(*p))
251 p++;
252 while (isspace(*p))
253 p++;
254
255 inquote = 0;
256 word_start = 0;
257 }
258 else if (*p == '"' || *p == '\'')
259 {
260 /* we've reached the beginning of a quote */
261 inquote = *p++;
262 word_start = p;
263 }
264 else
265 {
266 /* we've reached the beginning of a new word */
267 if (!word_start)
268 word_start = p;
269
270 /* normal character, skip */
271 p++;
272 }
273 }
274
275 return nwords;
276 }
277
278 void command_output_text(command_context_t *context, const char *data)
279 {
280 if( context && context->output_handler && data ){
281 context->output_handler( context, data );
282 }
283 }
284
285 void command_print_n(command_context_t *context, char *format, ...)
286 {
287 char *string;
288
289 va_list ap;
290 va_start(ap, format);
291
292 string = alloc_vprintf(format, ap);
293 if (string != NULL)
294 {
295 context->output_handler(context, string);
296 free(string);
297 }
298
299 va_end(ap);
300 }
301
302 void command_print(command_context_t *context, char *format, ...)
303 {
304 char *string;
305
306 va_list ap;
307 va_start(ap, format);
308
309 string = alloc_vprintf(format, ap);
310 if (string != NULL)
311 {
312 strcat(string, "\n"); /* alloc_vprintf guaranteed the buffer to be at least one char longer */
313 context->output_handler(context, string);
314 free(string);
315 }
316
317 va_end(ap);
318 }
319
320 command_t *find_command(command_context_t *context, command_t *commands, char *words[], int num_words, int start_word, int *new_start_word)
321 {
322 command_t *c;
323
324 for (c = commands; c; c = c->next)
325 {
326 if (strcasecmp(c->name, words[start_word]))
327 continue;
328
329 if ((context->mode == COMMAND_CONFIG) || (c->mode == COMMAND_ANY) || (c->mode == context->mode) )
330 {
331 if (!c->children)
332 {
333 if (!c->handler)
334 {
335 return NULL;
336 }
337 else
338 {
339 *new_start_word=start_word;
340 return c;
341 }
342 }
343 else
344 {
345 if (start_word == num_words - 1)
346 {
347 return NULL;
348 }
349 return find_command(context, c->children, words, num_words, start_word + 1, new_start_word);
350 }
351 }
352 }
353 return NULL;
354 }
355
356 int find_and_run_command(command_context_t *context, command_t *commands, char *words[], int num_words)
357 {
358 int start_word=0;
359 command_t *c;
360 c = find_command(context, commands, words, num_words, start_word, &start_word);
361 if (c == NULL)
362 {
363 /* just return command not found */
364 return ERROR_COMMAND_NOTFOUND;
365 }
366
367 int retval = c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1);
368 if (retval == ERROR_COMMAND_SYNTAX_ERROR)
369 {
370 command_print(context, "Syntax error:");
371 command_print_help_line(context, c, 0);
372 }
373 else if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
374 {
375 /* just fall through for a shutdown request */
376 }
377 else if (retval != ERROR_OK)
378 {
379 /* we do not print out an error message because the command *should*
380 * have printed out an error
381 */
382 LOG_DEBUG("Command failed with error code %d", retval);
383 }
384
385 return retval;
386 }
387
388 int command_run_line_internal(command_context_t *context, char *line)
389 {
390 LOG_USER_N("%s", ""); /* Keep GDB connection alive*/
391
392 int nwords;
393 char *words[128] = {0};
394 int retval;
395 int i;
396
397 /* skip preceding whitespace */
398 while (isspace(*line))
399 line++;
400
401 /* empty line, ignore */
402 if (!*line)
403 return ERROR_OK;
404
405 /* ignore comments */
406 if (*line && (line[0] == '#'))
407 return ERROR_OK;
408
409 LOG_DEBUG("%s", line);
410
411 nwords = parse_line(line, words, sizeof(words) / sizeof(words[0]));
412
413 if (nwords > 0)
414 {
415 retval = find_and_run_command(context, context->commands, words, nwords);
416 }
417 else
418 return ERROR_INVALID_ARGUMENTS;
419
420 for (i = 0; i < nwords; i++)
421 free(words[i]);
422
423 return retval;
424 }
425
426 int command_run_line(command_context_t *context, char *line)
427 {
428 /* if a command is unknown to the "unknown" proc in tcl/commands.tcl will
429 * redirect it to OpenOCD.
430 *
431 * This avoids having to type the "openocd" prefix and makes OpenOCD
432 * commands "native" to Tcl.
433 */
434 return jim_command(context, line);
435 }
436
437
438 int command_run_linef(command_context_t *context, char *format, ...)
439 {
440 int retval=ERROR_FAIL;
441 char *string;
442 va_list ap;
443 va_start(ap, format);
444 string = alloc_vprintf(format, ap);
445 if (string!=NULL)
446 {
447 retval=command_run_line(context, string);
448 }
449 va_end(ap);
450 return retval;
451 }
452
453 void command_print_help_line(command_context_t* context, struct command_s *command, int indent)
454 {
455 command_t *c;
456 char *indent_text=malloc(indent + 2);
457
458 char *help = "no help available";
459 char name_buf[64];
460
461 if (indent)
462 {
463 indent_text[0] = ' ';
464 memset(indent_text + 1, '-', indent);
465 indent_text[indent + 1] = 0;
466 }
467
468 if (command->help)
469 help = command->help;
470
471 snprintf(name_buf, 64, command->name);
472
473 if (indent)
474 strncat(name_buf, indent_text, 64);
475
476 command_print(context, "%20s\t%s", name_buf, help, indent);
477
478 if (command->children)
479 {
480 for (c = command->children; c; c = c->next)
481 {
482 command_print_help_line(context, c, indent + 1);
483 }
484 }
485 free(indent_text);
486 }
487
488 int command_print_help_match(command_context_t* context, command_t* c_first, char* name, char** args, int argc)
489 {
490 command_t * c;
491
492 for (c = c_first; c; c = c->next)
493 {
494 if (argc > 0)
495 {
496 if (strcasecmp(c->name, args[0]))
497 continue;
498
499 if (argc > 1)
500 {
501 command_print_help_match(context, c->children, name, args + 1, argc - 1);
502 continue;
503 }
504 }
505
506 command_print_help_line(context, c, 0);
507 }
508
509 return ERROR_OK;
510 }
511
512 int command_print_help(command_context_t* context, char* name, char** args, int argc)
513 {
514 return command_print_help_match(context, context->commands, name, args, argc);
515 }
516
517 void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, const char* line), void *priv)
518 {
519 context->output_handler = output_handler;
520 context->output_handler_priv = priv;
521 }
522
523 command_context_t* copy_command_context(command_context_t* context)
524 {
525 command_context_t* copy_context = malloc(sizeof(command_context_t));
526
527 *copy_context = *context;
528
529 return copy_context;
530 }
531
532 int command_done(command_context_t *context)
533 {
534 free(context);
535 context = NULL;
536
537 return ERROR_OK;
538 }
539
540 command_context_t* command_init()
541 {
542 command_context_t* context = malloc(sizeof(command_context_t));
543
544 context->mode = COMMAND_EXEC;
545 context->commands = NULL;
546 context->current_target = 0;
547 context->output_handler = NULL;
548 context->output_handler_priv = NULL;
549
550 register_command(context, NULL, "help", command_print_help,
551 COMMAND_EXEC, "display this help");
552
553 register_command(context, NULL, "sleep", handle_sleep_command,
554 COMMAND_ANY, "sleep for <n> milliseconds");
555
556 register_command(context, NULL, "time", handle_time_command,
557 COMMAND_ANY, "time <cmd + args> - execute <cmd + args> and print time it took");
558
559 register_command(context, NULL, "fast", handle_fast_command,
560 COMMAND_ANY, "fast <enable/disable> - place at beginning of config files. Sets defaults to fast and dangerous.");
561
562 return context;
563 }
564
565 /* sleep command sleeps for <n> miliseconds
566 * this is useful in target startup scripts
567 */
568 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
569 {
570 unsigned long duration = 0;
571
572 if (argc == 1)
573 {
574 duration = strtoul(args[0], NULL, 0);
575 usleep(duration * 1000);
576 }
577
578 return ERROR_OK;
579 }
580
581 int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
582 {
583 if (argc!=1)
584 return ERROR_COMMAND_SYNTAX_ERROR;
585
586 fast_and_dangerous = strcmp("enable", args[0])==0;
587
588 return ERROR_OK;
589 }
590
591
592 int handle_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
593 {
594 duration_t duration;
595 char *duration_text;
596 int retval;
597 float t;
598
599 if (argc<1)
600 return ERROR_COMMAND_SYNTAX_ERROR;
601
602 duration_start_measure(&duration);
603
604 retval = find_and_run_command(cmd_ctx, cmd_ctx->commands, args, argc);
605 if (retval == ERROR_COMMAND_NOTFOUND)
606 {
607 command_print(cmd_ctx, "Command %s not found", args[0]);
608 }
609
610 duration_stop_measure(&duration, &duration_text);
611
612 t=duration.duration.tv_sec;
613 t+=((float)duration.duration.tv_usec / 1000000.0);
614 command_print(cmd_ctx, "%s took %fs", args[0], t);
615
616 free(duration_text);
617
618 return retval;
619 }

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)