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

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)