added fast option.
[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_command(command_context_t *context, char *name)
147 {
148 command_t *c, *p = NULL, *c2;
149
150 unique_length_dirty = 1;
151
152 if ((!context) || (!name))
153 return ERROR_INVALID_ARGUMENTS;
154
155 /* find command */
156 for (c = context->commands; c; c = c->next)
157 {
158 if (strcmp(name, c->name) == 0)
159 {
160 /* unlink command */
161 if (p)
162 {
163 p->next = c->next;
164 }
165 else
166 {
167 context->commands = c->next;
168 }
169
170 /* unregister children */
171 if (c->children)
172 {
173 for (c2 = c->children; c2; c2 = c2->next)
174 {
175 free(c2->name);
176 if (c2->help)
177 free(c2->help);
178 free(c2);
179 }
180 }
181
182 /* delete command */
183 free(c->name);
184 if (c->help)
185 free(c->help);
186 free(c);
187 }
188
189 /* remember the last command for unlinking */
190 p = c;
191 }
192
193 return ERROR_OK;
194 }
195
196 int parse_line(char *line, char *words[], int max_words)
197 {
198 int nwords = 0;
199 char *p = line;
200 char *word_start = line;
201 int inquote = 0;
202
203 while (nwords < max_words - 1)
204 {
205 /* check if we reached
206 * a terminating NUL
207 * a matching closing quote character " or '
208 * we're inside a word but not a quote, and the current character is whitespace
209 */
210 if (!*p || *p == inquote || (word_start && !inquote && isspace(*p)))
211 {
212 /* we're inside a word or quote, and reached its end*/
213 if (word_start)
214 {
215 int len;
216 char *word_end=p;
217
218 /* This will handle extra whitespace within quotes */
219 while (isspace(*word_start)&&(word_start<word_end))
220 word_start++;
221 while (isspace(*(word_end-1))&&(word_start<word_end))
222 word_end--;
223 len = word_end - word_start;
224
225 if (len>0)
226 {
227 /* copy the word */
228 memcpy(words[nwords] = malloc(len + 1), word_start, len);
229 /* add terminating NUL */
230 words[nwords++][len] = 0;
231 }
232 }
233 /* we're done parsing the line */
234 if (!*p)
235 break;
236
237 /* skip over trailing quote or whitespace*/
238 if (inquote || isspace(*p))
239 p++;
240 while (isspace(*p))
241 p++;
242
243 inquote = 0;
244 word_start = 0;
245 }
246 else if (*p == '"' || *p == '\'')
247 {
248 /* we've reached the beginning of a quote */
249 inquote = *p++;
250 word_start = p;
251 }
252 else
253 {
254 /* we've reached the beginning of a new word */
255 if (!word_start)
256 word_start = p;
257
258 /* normal character, skip */
259 p++;
260 }
261 }
262
263 return nwords;
264 }
265
266 void command_print_n(command_context_t *context, char *format, ...)
267 {
268 char *string;
269
270 va_list ap;
271 va_start(ap, format);
272
273 string = alloc_vprintf(format, ap);
274 if (string != NULL)
275 {
276 context->output_handler(context, string);
277 free(string);
278 }
279
280 va_end(ap);
281 }
282
283 void command_print(command_context_t *context, char *format, ...)
284 {
285 char *string;
286
287 va_list ap;
288 va_start(ap, format);
289
290 string = alloc_vprintf(format, ap);
291 if (string != NULL)
292 {
293 strcat(string, "\n"); /* alloc_vprintf guaranteed the buffer to be at least one char longer */
294 context->output_handler(context, string);
295 free(string);
296 }
297
298 va_end(ap);
299 }
300
301 int find_and_run_command(command_context_t *context, command_t *commands, char *words[], int num_words, int start_word)
302 {
303 command_t *c;
304 int retval = ERROR_COMMAND_SYNTAX_ERROR;
305
306 if (unique_length_dirty)
307 {
308 unique_length_dirty = 0;
309 /* update unique lengths */
310 build_unique_lengths(context, context->commands);
311 }
312
313 for (c = commands; c; c = c->next)
314 {
315 if (strncasecmp(c->name, words[start_word], c->unique_len))
316 continue;
317
318 if (strncasecmp(c->name, words[start_word], strlen(words[start_word])))
319 continue;
320
321 if ((context->mode == COMMAND_CONFIG) || (c->mode == COMMAND_ANY) || (c->mode == context->mode) )
322 {
323 if (!c->children)
324 {
325 if (!c->handler)
326 {
327 command_print(context, "No handler for command");
328 retval = ERROR_COMMAND_SYNTAX_ERROR;
329 break;
330 }
331 else
332 {
333 int retval = c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1);
334 if (retval == ERROR_COMMAND_SYNTAX_ERROR)
335 {
336 command_print(context, "Syntax error:");
337 command_print_help_line(context, c, 0);
338 } else if (retval != ERROR_OK)
339 {
340 /* we do not print out an error message because the command *should*
341 * have printed out an error
342 */
343 LOG_DEBUG("Command failed with error code %d", retval);
344 }
345 return retval;
346 }
347 }
348 else
349 {
350 if (start_word == num_words - 1)
351 {
352 command_print(context, "Incomplete command");
353 break;
354 }
355 return find_and_run_command(context, c->children, words, num_words, start_word + 1);
356 }
357 }
358 }
359
360 command_print(context, "Command %s not found", words[start_word]);
361 return retval;
362 }
363
364 int command_run_line(command_context_t *context, char *line)
365 {
366 int nwords;
367 char *words[128] = {0};
368 int retval;
369 int i;
370
371 if ((!context) || (!line))
372 return ERROR_INVALID_ARGUMENTS;
373
374 /* skip preceding whitespace */
375 while (isspace(*line))
376 line++;
377
378 /* empty line, ignore */
379 if (!*line)
380 return ERROR_OK;
381
382 /* ignore comments */
383 if (*line && (line[0] == '#'))
384 return ERROR_OK;
385
386 LOG_DEBUG("%s", line);
387
388 nwords = parse_line(line, words, sizeof(words) / sizeof(words[0]));
389
390 if (nwords > 0)
391 retval = find_and_run_command(context, context->commands, words, nwords, 0);
392 else
393 return ERROR_INVALID_ARGUMENTS;
394
395 for (i = 0; i < nwords; i++)
396 free(words[i]);
397
398 return retval;
399 }
400
401 int command_run_file(command_context_t *context, FILE *file, enum command_mode mode)
402 {
403 int retval = ERROR_OK;
404 int old_command_mode;
405 char *buffer=malloc(4096);
406 if (buffer==NULL)
407 {
408 return ERROR_INVALID_ARGUMENTS;
409 }
410
411 old_command_mode = context->mode;
412 context->mode = mode;
413
414 while (fgets(buffer, 4096, file))
415 {
416 char *p;
417 char *cmd, *end;
418
419 /* stop processing line after a comment (#, !) or a LF, CR were encountered */
420 if ((p = strpbrk(buffer, "#!\r\n")))
421 *p = 0;
422
423 /* skip over leading whitespace */
424 cmd = buffer;
425 while (isspace(*cmd))
426 cmd++;
427
428 /* empty (all whitespace) line? */
429 if (!*cmd)
430 continue;
431
432 /* search the end of the current line, ignore trailing whitespace */
433 for (p = end = cmd; *p; p++)
434 if (!isspace(*p))
435 end = p;
436
437 /* terminate end */
438 *++end = 0;
439 if (strcasecmp(cmd, "quit") == 0)
440 break;
441
442 /* run line */
443 if ((retval = command_run_line(context, cmd)) == ERROR_COMMAND_CLOSE_CONNECTION)
444 break;
445 }
446
447 context->mode = old_command_mode;
448
449
450 free(buffer);
451
452 return retval;
453 }
454
455 void command_print_help_line(command_context_t* context, struct command_s *command, int indent)
456 {
457 command_t *c;
458 char *indent_text=malloc(indent + 2);
459
460 char *help = "no help available";
461 char name_buf[64];
462
463 if (indent)
464 {
465 indent_text[0] = ' ';
466 memset(indent_text + 1, '-', indent);
467 indent_text[indent + 1] = 0;
468 }
469
470 if (command->help)
471 help = command->help;
472
473 snprintf(name_buf, 64, command->name);
474
475 if (indent)
476 strncat(name_buf, indent_text, 64);
477
478 command_print(context, "%20s\t%s", name_buf, help, indent);
479
480 if (command->children)
481 {
482 for (c = command->children; c; c = c->next)
483 {
484 command_print_help_line(context, c, indent + 1);
485 }
486 }
487 free(indent_text);
488 }
489
490 int command_print_help_match(command_context_t* context, command_t* c_first, char* name, char** args, int argc)
491 {
492 command_t * c;
493
494 for (c = c_first; c; c = c->next)
495 {
496 if (argc > 0)
497 {
498 if (strncasecmp(c->name, args[0], c->unique_len))
499 continue;
500
501 if (strncasecmp(c->name, args[0], strlen(args[0])))
502 continue;
503
504 if (argc > 1)
505 {
506 command_print_help_match(context, c->children, name, args + 1, argc - 1);
507 continue;
508 }
509 }
510
511 command_print_help_line(context, c, 0);
512 }
513
514 return ERROR_OK;
515 }
516
517 int command_print_help(command_context_t* context, char* name, char** args, int argc)
518 {
519 return command_print_help_match(context, context->commands, name, args, argc);
520 }
521
522
523 void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, char* line), void *priv)
524 {
525 context->output_handler = output_handler;
526 context->output_handler_priv = priv;
527 }
528
529 command_context_t* copy_command_context(command_context_t* context)
530 {
531 command_context_t* copy_context = malloc(sizeof(command_context_t));
532
533 *copy_context = *context;
534
535 return copy_context;
536 }
537
538 int command_done(command_context_t *context)
539 {
540 free(context);
541 context = NULL;
542
543 return ERROR_OK;
544 }
545
546 command_context_t* command_init()
547 {
548 command_context_t* context = malloc(sizeof(command_context_t));
549
550 context->mode = COMMAND_EXEC;
551 context->commands = NULL;
552 context->current_target = 0;
553 context->output_handler = NULL;
554 context->output_handler_priv = NULL;
555
556 register_command(context, NULL, "help", command_print_help,
557 COMMAND_EXEC, "display this help");
558
559 register_command(context, NULL, "sleep", handle_sleep_command,
560 COMMAND_ANY, "sleep for <n> milliseconds");
561
562 register_command(context, NULL, "time", handle_time_command,
563 COMMAND_ANY, "time <cmd + args> - execute <cmd + args> and print time it took");
564
565 register_command(context, NULL, "fast", handle_fast_command,
566 COMMAND_ANY, "fast <enable/disable> - place at beginning of config files. Sets defaults to fast and dangerous.");
567
568 return context;
569 }
570
571 /* sleep command sleeps for <n> miliseconds
572 * this is useful in target startup scripts
573 */
574 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
575 {
576 unsigned long duration = 0;
577
578 if (argc == 1)
579 {
580 duration = strtoul(args[0], NULL, 0);
581 usleep(duration * 1000);
582 }
583
584 return ERROR_OK;
585 }
586
587 int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
588 {
589 if (argc!=1)
590 return ERROR_COMMAND_SYNTAX_ERROR;
591
592 fast_and_dangerous = strcmp("enable", args[0])==0;
593
594 return ERROR_OK;
595 }
596
597
598 int handle_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
599 {
600 duration_t duration;
601 char *duration_text;
602 int retval;
603 float t;
604
605 if (argc<1)
606 return ERROR_COMMAND_SYNTAX_ERROR;
607
608 duration_start_measure(&duration);
609
610 retval = find_and_run_command(cmd_ctx, cmd_ctx->commands, args, argc, 0);
611
612 duration_stop_measure(&duration, &duration_text);
613
614 t=duration.duration.tv_sec;
615 t+=((float)duration.duration.tv_usec / 1000000.0);
616 command_print(cmd_ctx, "%s took %fs", args[0], t);
617
618 free(duration_text);
619
620 return retval;
621 }

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)