utility fn.
[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 int fast_and_dangerous = 0;
42
43 void command_print_help_line(command_context_t* context, struct command_s *command, int indent);
44
45 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
46 int handle_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
47 int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
48
49 int build_unique_lengths(command_context_t *context, command_t *commands)
50 {
51 command_t *c, *p;
52
53 /* iterate through all commands */
54 for (c = commands; c; c = c->next)
55 {
56 /* find out how many characters are required to uniquely identify a command */
57 for (c->unique_len = 1; c->unique_len <= strlen(c->name); c->unique_len++)
58 {
59 int foundmatch = 0;
60
61 /* for every command, see if the current length is enough */
62 for (p = commands; p; p = p->next)
63 {
64 /* ignore the command itself */
65 if (c == p)
66 continue;
67
68 /* compare commands up to the current length */
69 if (strncmp(p->name, c->name, c->unique_len) == 0)
70 foundmatch++;
71 }
72
73 /* when none of the commands matched, we've found the minimum length required */
74 if (!foundmatch)
75 break;
76 }
77
78 /* if the current command has children, build the unique lengths for them */
79 if (c->children)
80 build_unique_lengths(context, c->children);
81 }
82
83 return ERROR_OK;
84 }
85
86 /* Avoid evaluating this each time we add a command. Reduces overhead from O(n^2) to O(n).
87 * Makes a difference on ARM7 types machines and is not observable on GHz machines.
88 */
89 static int unique_length_dirty = 1;
90
91 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)
92 {
93 command_t *c, *p;
94 unique_length_dirty = 1;
95
96 if (!context || !name)
97 return NULL;
98
99 c = malloc(sizeof(command_t));
100
101 c->name = strdup(name);
102 c->parent = parent;
103 c->children = NULL;
104 c->handler = handler;
105 c->mode = mode;
106 if (help)
107 c->help = strdup(help);
108 else
109 c->help = NULL;
110 c->unique_len = 0;
111 c->next = NULL;
112
113 /* place command in tree */
114 if (parent)
115 {
116 if (parent->children)
117 {
118 /* find last child */
119 for (p = parent->children; p && p->next; p = p->next);
120 if (p)
121 p->next = c;
122 }
123 else
124 {
125 parent->children = c;
126 }
127 }
128 else
129 {
130 if (context->commands)
131 {
132 /* find last command */
133 for (p = context->commands; p && p->next; p = p->next);
134 if (p)
135 p->next = c;
136 }
137 else
138 {
139 context->commands = c;
140 }
141 }
142
143 return c;
144 }
145
146 int unregister_all_commands(command_context_t *context)
147 {
148 command_t *c, *c2;
149
150 unique_length_dirty = 1;
151
152 if (context == NULL)
153 return ERROR_OK;
154
155
156 while(NULL != context->commands)
157 {
158 c = context->commands;
159
160 while(NULL != c->children)
161 {
162 c2 = c->children;
163 c->children = c->children->next;
164 free(c2->name);
165 c2->name = NULL;
166 free(c2->help);
167 c2->help = NULL;
168 free(c2);
169 c2 = NULL;
170 }
171
172 context->commands = context->commands->next;
173
174 free(c->name);
175 c->name = NULL;
176 free(c->help);
177 c->help = NULL;
178 free(c);
179 c = NULL;
180 }
181
182 return ERROR_OK;
183 }
184
185 int unregister_command(command_context_t *context, char *name)
186 {
187 command_t *c, *p = NULL, *c2;
188
189 unique_length_dirty = 1;
190
191 if ((!context) || (!name))
192 return ERROR_INVALID_ARGUMENTS;
193
194 /* find command */
195 for (c = context->commands; c; c = c->next)
196 {
197 if (strcmp(name, c->name) == 0)
198 {
199 /* unlink command */
200 if (p)
201 {
202 p->next = c->next;
203 }
204 else
205 {
206 context->commands = c->next;
207 }
208
209 /* unregister children */
210 if (c->children)
211 {
212 for (c2 = c->children; c2; c2 = c2->next)
213 {
214 free(c2->name);
215 if (c2->help)
216 free(c2->help);
217 free(c2);
218 }
219 }
220
221 /* delete command */
222 free(c->name);
223 if (c->help)
224 free(c->help);
225 free(c);
226 }
227
228 /* remember the last command for unlinking */
229 p = c;
230 }
231
232 return ERROR_OK;
233 }
234
235 int parse_line(char *line, char *words[], int max_words)
236 {
237 int nwords = 0;
238 char *p = line;
239 char *word_start = line;
240 int inquote = 0;
241
242 while (nwords < max_words - 1)
243 {
244 /* check if we reached
245 * a terminating NUL
246 * a matching closing quote character " or '
247 * we're inside a word but not a quote, and the current character is whitespace
248 */
249 if (!*p || *p == inquote || (word_start && !inquote && isspace(*p)))
250 {
251 /* we're inside a word or quote, and reached its end*/
252 if (word_start)
253 {
254 int len;
255 char *word_end=p;
256
257 /* This will handle extra whitespace within quotes */
258 while (isspace(*word_start)&&(word_start<word_end))
259 word_start++;
260 while (isspace(*(word_end-1))&&(word_start<word_end))
261 word_end--;
262 len = word_end - word_start;
263
264 if (len>0)
265 {
266 /* copy the word */
267 memcpy(words[nwords] = malloc(len + 1), word_start, len);
268 /* add terminating NUL */
269 words[nwords++][len] = 0;
270 }
271 }
272 /* we're done parsing the line */
273 if (!*p)
274 break;
275
276 /* skip over trailing quote or whitespace*/
277 if (inquote || isspace(*p))
278 p++;
279 while (isspace(*p))
280 p++;
281
282 inquote = 0;
283 word_start = 0;
284 }
285 else if (*p == '"' || *p == '\'')
286 {
287 /* we've reached the beginning of a quote */
288 inquote = *p++;
289 word_start = p;
290 }
291 else
292 {
293 /* we've reached the beginning of a new word */
294 if (!word_start)
295 word_start = p;
296
297 /* normal character, skip */
298 p++;
299 }
300 }
301
302 return nwords;
303 }
304
305 void command_print_n(command_context_t *context, char *format, ...)
306 {
307 char *string;
308
309 va_list ap;
310 va_start(ap, format);
311
312 string = alloc_vprintf(format, ap);
313 if (string != NULL)
314 {
315 context->output_handler(context, string);
316 free(string);
317 }
318
319 va_end(ap);
320 }
321
322 void command_print(command_context_t *context, char *format, ...)
323 {
324 char *string;
325
326 va_list ap;
327 va_start(ap, format);
328
329 string = alloc_vprintf(format, ap);
330 if (string != NULL)
331 {
332 strcat(string, "\n"); /* alloc_vprintf guaranteed the buffer to be at least one char longer */
333 context->output_handler(context, string);
334 free(string);
335 }
336
337 va_end(ap);
338 }
339
340 command_t *find_command(command_context_t *context, command_t *commands, char *words[], int num_words, int start_word, int *new_start_word)
341 {
342 command_t *c;
343
344 if (unique_length_dirty)
345 {
346 unique_length_dirty = 0;
347 /* update unique lengths */
348 build_unique_lengths(context, context->commands);
349 }
350
351 for (c = commands; c; c = c->next)
352 {
353 if (strncasecmp(c->name, words[start_word], c->unique_len))
354 continue;
355
356 if (strncasecmp(c->name, words[start_word], strlen(words[start_word])))
357 continue;
358
359 if ((context->mode == COMMAND_CONFIG) || (c->mode == COMMAND_ANY) || (c->mode == context->mode) )
360 {
361 if (!c->children)
362 {
363 if (!c->handler)
364 {
365 return NULL;
366 }
367 else
368 {
369 *new_start_word=start_word;
370 return c;
371 }
372 }
373 else
374 {
375 if (start_word == num_words - 1)
376 {
377 return NULL;
378 }
379 return find_command(context, c->children, words, num_words, start_word + 1, new_start_word);
380 }
381 }
382 }
383 return NULL;
384 }
385
386 int find_and_run_command(command_context_t *context, command_t *commands, char *words[], int num_words)
387 {
388 int start_word=0;
389 command_t *c;
390 c=find_command(context, commands, words, num_words, start_word, &start_word);
391 if (c==NULL)
392 {
393 command_print(context, "Command %s not found", words[start_word]);
394 return ERROR_COMMAND_SYNTAX_ERROR;
395 }
396
397 int retval = c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1);
398 if (retval == ERROR_COMMAND_SYNTAX_ERROR)
399 {
400 command_print(context, "Syntax error:");
401 command_print_help_line(context, c, 0);
402 }
403 else if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
404 {
405 /* just fall through for a shutdown request */
406 }
407 else if (retval != ERROR_OK)
408 {
409 /* we do not print out an error message because the command *should*
410 * have printed out an error
411 */
412 LOG_DEBUG("Command failed with error code %d", retval);
413 }
414 return retval;
415 }
416
417 int command_run_line_internal_op(command_context_t *context, char *line, int run)
418 {
419 LOG_USER_N("%s", ""); /* Keep GDB connection alive*/
420
421 int nwords;
422 char *words[128] = {0};
423 int retval;
424 int i;
425
426 /* skip preceding whitespace */
427 while (isspace(*line))
428 line++;
429
430 /* empty line, ignore */
431 if (!*line)
432 return ERROR_OK;
433
434 /* ignore comments */
435 if (*line && (line[0] == '#'))
436 return ERROR_OK;
437
438 LOG_DEBUG("%s", line);
439
440 nwords = parse_line(line, words, sizeof(words) / sizeof(words[0]));
441
442 if (nwords > 0)
443 if (run)
444 {
445 retval = find_and_run_command(context, context->commands, words, nwords);
446 } else
447 {
448 int t;
449 return (find_command(context, context->commands, words, nwords, 0, &t)!=NULL)?ERROR_OK:ERROR_FAIL;
450 }
451 else
452 return ERROR_INVALID_ARGUMENTS;
453
454 for (i = 0; i < nwords; i++)
455 free(words[i]);
456
457 return retval;
458 }
459
460 int command_run_line_internal(command_context_t *context, char *line)
461 {
462 return command_run_line_internal_op(context, line, 1);
463 }
464
465 int command_run_line(command_context_t *context, char *line)
466 {
467 int retval;
468 if ((!context) || (!line))
469 return ERROR_INVALID_ARGUMENTS;
470
471 if (command_run_line_internal_op(context, line, 0)!=ERROR_OK)
472 {
473 /* If we can't find a command, then try the interpreter.
474 * If there is no interpreter implemented, then this will
475 * simply print a syntax error.
476 *
477 * These hooks were left in to reduce patch size for
478 * wip to add scripting language.
479 */
480 return jim_command(context, line);
481 }
482
483 return command_run_line_internal(context, line);
484 }
485
486 int command_run_file(command_context_t *context, FILE *file, enum command_mode mode)
487 {
488 int retval = ERROR_OK;
489 int old_command_mode;
490 char *buffer=malloc(4096);
491 if (buffer==NULL)
492 {
493 return ERROR_INVALID_ARGUMENTS;
494 }
495
496 old_command_mode = context->mode;
497 context->mode = mode;
498
499 while (fgets(buffer, 4096, file))
500 {
501 char *p;
502 char *cmd, *end;
503
504 /* stop processing line after a comment (#, !) or a LF, CR were encountered */
505 if ((p = strpbrk(buffer, "#!\r\n")))
506 *p = 0;
507
508 /* skip over leading whitespace */
509 cmd = buffer;
510 while (isspace(*cmd))
511 cmd++;
512
513 /* empty (all whitespace) line? */
514 if (!*cmd)
515 continue;
516
517 /* search the end of the current line, ignore trailing whitespace */
518 for (p = end = cmd; *p; p++)
519 if (!isspace(*p))
520 end = p;
521
522 /* terminate end */
523 *++end = 0;
524 if (strcasecmp(cmd, "quit") == 0)
525 break;
526
527 /* run line */
528 if ((retval = command_run_line(context, cmd)) == ERROR_COMMAND_CLOSE_CONNECTION)
529 break;
530 }
531
532 context->mode = old_command_mode;
533
534
535 free(buffer);
536
537 return retval;
538 }
539
540 int command_run_linef(command_context_t *context, char *format, ...)
541 {
542 int retval=ERROR_FAIL;
543 char *string;
544 va_list ap;
545 va_start(ap, format);
546 string = alloc_vprintf(format, ap);
547 if (string!=NULL)
548 {
549 retval=command_run_line(context, string);
550 }
551 va_end(ap);
552 return retval;
553 }
554
555 void command_print_help_line(command_context_t* context, struct command_s *command, int indent)
556 {
557 command_t *c;
558 char *indent_text=malloc(indent + 2);
559
560 char *help = "no help available";
561 char name_buf[64];
562
563 if (indent)
564 {
565 indent_text[0] = ' ';
566 memset(indent_text + 1, '-', indent);
567 indent_text[indent + 1] = 0;
568 }
569
570 if (command->help)
571 help = command->help;
572
573 snprintf(name_buf, 64, command->name);
574
575 if (indent)
576 strncat(name_buf, indent_text, 64);
577
578 command_print(context, "%20s\t%s", name_buf, help, indent);
579
580 if (command->children)
581 {
582 for (c = command->children; c; c = c->next)
583 {
584 command_print_help_line(context, c, indent + 1);
585 }
586 }
587 free(indent_text);
588 }
589
590 int command_print_help_match(command_context_t* context, command_t* c_first, char* name, char** args, int argc)
591 {
592 command_t * c;
593
594 for (c = c_first; c; c = c->next)
595 {
596 if (argc > 0)
597 {
598 if (strncasecmp(c->name, args[0], c->unique_len))
599 continue;
600
601 if (strncasecmp(c->name, args[0], strlen(args[0])))
602 continue;
603
604 if (argc > 1)
605 {
606 command_print_help_match(context, c->children, name, args + 1, argc - 1);
607 continue;
608 }
609 }
610
611 command_print_help_line(context, c, 0);
612 }
613
614 return ERROR_OK;
615 }
616
617 int command_print_help(command_context_t* context, char* name, char** args, int argc)
618 {
619 return command_print_help_match(context, context->commands, name, args, argc);
620 }
621
622 void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, char* line), void *priv)
623 {
624 context->output_handler = output_handler;
625 context->output_handler_priv = priv;
626 }
627
628 command_context_t* copy_command_context(command_context_t* context)
629 {
630 command_context_t* copy_context = malloc(sizeof(command_context_t));
631
632 *copy_context = *context;
633
634 return copy_context;
635 }
636
637 int command_done(command_context_t *context)
638 {
639 free(context);
640 context = NULL;
641
642 return ERROR_OK;
643 }
644
645 command_context_t* command_init()
646 {
647 command_context_t* context = malloc(sizeof(command_context_t));
648
649 context->mode = COMMAND_EXEC;
650 context->commands = NULL;
651 context->current_target = 0;
652 context->output_handler = NULL;
653 context->output_handler_priv = NULL;
654
655 register_command(context, NULL, "help", command_print_help,
656 COMMAND_EXEC, "display this help");
657
658 register_command(context, NULL, "sleep", handle_sleep_command,
659 COMMAND_ANY, "sleep for <n> milliseconds");
660
661 register_command(context, NULL, "time", handle_time_command,
662 COMMAND_ANY, "time <cmd + args> - execute <cmd + args> and print time it took");
663
664 register_command(context, NULL, "fast", handle_fast_command,
665 COMMAND_ANY, "fast <enable/disable> - place at beginning of config files. Sets defaults to fast and dangerous.");
666
667 return context;
668 }
669
670 /* sleep command sleeps for <n> miliseconds
671 * this is useful in target startup scripts
672 */
673 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
674 {
675 unsigned long duration = 0;
676
677 if (argc == 1)
678 {
679 duration = strtoul(args[0], NULL, 0);
680 usleep(duration * 1000);
681 }
682
683 return ERROR_OK;
684 }
685
686 int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
687 {
688 if (argc!=1)
689 return ERROR_COMMAND_SYNTAX_ERROR;
690
691 fast_and_dangerous = strcmp("enable", args[0])==0;
692
693 return ERROR_OK;
694 }
695
696
697 int handle_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
698 {
699 duration_t duration;
700 char *duration_text;
701 int retval;
702 float t;
703
704 if (argc<1)
705 return ERROR_COMMAND_SYNTAX_ERROR;
706
707 duration_start_measure(&duration);
708
709 retval = find_and_run_command(cmd_ctx, cmd_ctx->commands, args, argc);
710
711 duration_stop_measure(&duration, &duration_text);
712
713 t=duration.duration.tv_sec;
714 t+=((float)duration.duration.tv_usec / 1000000.0);
715 command_print(cmd_ctx, "%s took %fs", args[0], t);
716
717 free(duration_text);
718
719 return retval;
720 }

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)