Michael Bruck:
[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 static void command_printv(command_context_t *context, char *format, va_list ap)
264 {
265 char *buffer = NULL;
266 int n, size = 0;
267 char *p;
268
269 /* process format string */
270 for (;;)
271 {
272 if (!buffer || (n = vsnprintf(buffer, size, format, ap)) >= size)
273 {
274 /* increase buffer until it fits the whole string */
275 if (!(p = realloc(buffer, size += 4096)))
276 {
277 /* gotta free up */
278 if (buffer)
279 free(buffer);
280 return;
281 }
282
283 buffer = p;
284
285 continue;
286 }
287 break;
288 }
289
290 /* vsnprintf failed */
291 if (n < 0)
292 {
293 if (buffer)
294 free(buffer);
295 return;
296 }
297
298 context->output_handler(context, buffer);
299
300 if (buffer)
301 free(buffer);
302 }
303
304 void command_print_sameline(command_context_t *context, char *format, ...)
305 {
306 va_list ap;
307 va_start(ap, format);
308 command_printv(context, format, ap);
309 va_end(ap);
310 }
311
312 void command_print(command_context_t *context, char *format, ...)
313 {
314 char *t=malloc(strlen(format)+2);
315 strcpy(t, format);
316 strcat(t, "\n");
317 va_list ap;
318 va_start(ap, format);
319 command_printv(context, t, ap);
320 va_end(ap);
321 free(t);
322
323 }
324
325 int find_and_run_command(command_context_t *context, command_t *commands, char *words[], int num_words, int start_word)
326 {
327 command_t *c;
328
329 if (unique_length_dirty)
330 {
331 unique_length_dirty = 0;
332 /* update unique lengths */
333 build_unique_lengths(context, context->commands);
334 }
335
336 for (c = commands; c; c = c->next)
337 {
338 if (strncasecmp(c->name, words[start_word], c->unique_len))
339 continue;
340
341 if (strncasecmp(c->name, words[start_word], strlen(words[start_word])))
342 continue;
343
344 if ((context->mode == COMMAND_CONFIG) || (c->mode == COMMAND_ANY) || (c->mode == context->mode) )
345 {
346 if (!c->children)
347 {
348 if (!c->handler)
349 {
350 command_print(context, "No handler for command");
351 break;
352 }
353 else
354 {
355 int retval = c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1);
356 if (retval == ERROR_COMMAND_SYNTAX_ERROR)
357 {
358 command_print(context, "Syntax error:");
359 command_print_help_line(context, c, 0);
360 }
361 return retval;
362 }
363 }
364 else
365 {
366 if (start_word == num_words - 1)
367 {
368 command_print(context, "Incomplete command");
369 break;
370 }
371 return find_and_run_command(context, c->children, words, num_words, start_word + 1);
372 }
373 }
374 }
375
376 command_print(context, "Command %s not found", words[start_word]);
377 return ERROR_OK;
378 }
379
380 int command_run_line(command_context_t *context, char *line)
381 {
382 int nwords;
383 char *words[128] = {0};
384 int retval;
385 int i;
386
387 if ((!context) || (!line))
388 return ERROR_INVALID_ARGUMENTS;
389
390 /* skip preceding whitespace */
391 while (isspace(*line))
392 line++;
393
394 /* empty line, ignore */
395 if (!*line)
396 return ERROR_OK;
397
398 /* ignore comments */
399 if (*line && (line[0] == '#'))
400 return ERROR_OK;
401
402 DEBUG("%s", line);
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_file(command_context_t *context, FILE *file, enum command_mode mode)
418 {
419 int retval = ERROR_OK;
420 int old_command_mode;
421 char *buffer=malloc(4096);
422 if (buffer==NULL)
423 {
424 return ERROR_INVALID_ARGUMENTS;
425 }
426
427 old_command_mode = context->mode;
428 context->mode = mode;
429
430 while (fgets(buffer, 4096, file))
431 {
432 char *p;
433 char *cmd, *end;
434
435 /* stop processing line after a comment (#, !) or a LF, CR were encountered */
436 if ((p = strpbrk(buffer, "#!\r\n")))
437 *p = 0;
438
439 /* skip over leading whitespace */
440 cmd = buffer;
441 while (isspace(*cmd))
442 cmd++;
443
444 /* empty (all whitespace) line? */
445 if (!*cmd)
446 continue;
447
448 /* search the end of the current line, ignore trailing whitespace */
449 for (p = end = cmd; *p; p++)
450 if (!isspace(*p))
451 end = p;
452
453 /* terminate end */
454 *++end = 0;
455 if (strcasecmp(cmd, "quit") == 0)
456 break;
457
458 /* run line */
459 if ((retval = command_run_line(context, cmd)) == ERROR_COMMAND_CLOSE_CONNECTION)
460 break;
461 }
462
463 context->mode = old_command_mode;
464
465
466 free(buffer);
467
468 return retval;
469 }
470
471 void command_print_help_line(command_context_t* context, struct command_s *command, int indent)
472 {
473 command_t *c;
474 char indent_text[indent + 2];
475 char *help = "no help available";
476 char name_buf[64];
477 int i;
478
479 if (indent)
480 {
481 indent_text[0] = ' ';
482 memset(indent_text + 1, '-', indent);
483 indent_text[indent + 1] = 0;
484 }
485
486 if (command->help)
487 help = command->help;
488
489 snprintf(name_buf, 64, command->name);
490
491 if (indent)
492 strncat(name_buf, indent_text, 64);
493
494 command_print(context, "%20s\t%s", name_buf, help, indent);
495
496 if (command->children)
497 {
498 for (c = command->children; c; c = c->next)
499 {
500 command_print_help_line(context, c, indent + 1);
501 }
502 }
503 }
504
505 int command_print_help_match(command_context_t* context, command_t* c_first, char* name, char** args, int argc)
506 {
507 command_t * c;
508 int i;
509
510 for (c = c_first; c; c = c->next)
511 {
512 if (argc > 0)
513 {
514 if (strncasecmp(c->name, args[0], c->unique_len))
515 continue;
516
517 if (strncasecmp(c->name, args[0], strlen(args[0])))
518 continue;
519
520 if (argc > 1)
521 {
522 command_print_help_match(context, c->children, name, args + 1, argc - 1);
523 continue;
524 }
525 }
526
527 command_print_help_line(context, c, 0);
528 }
529
530 return ERROR_OK;
531 }
532
533 int command_print_help(command_context_t* context, char* name, char** args, int argc)
534 {
535 return command_print_help_match(context, context->commands, name, args, argc);
536 }
537
538
539 void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, char* line), void *priv)
540 {
541 context->output_handler = output_handler;
542 context->output_handler_priv = priv;
543 }
544
545 command_context_t* copy_command_context(command_context_t* context)
546 {
547 command_context_t* copy_context = malloc(sizeof(command_context_t));
548
549 *copy_context = *context;
550
551 return copy_context;
552 }
553
554 int command_done(command_context_t *context)
555 {
556 free(context);
557 context = NULL;
558
559 return ERROR_OK;
560 }
561
562 command_context_t* command_init()
563 {
564 command_context_t* context = malloc(sizeof(command_context_t));
565
566 context->mode = COMMAND_EXEC;
567 context->commands = NULL;
568 context->current_target = 0;
569 context->output_handler = NULL;
570 context->output_handler_priv = NULL;
571
572 register_command(context, NULL, "help", command_print_help,
573 COMMAND_EXEC, "display this help");
574
575 register_command(context, NULL, "sleep", handle_sleep_command,
576 COMMAND_ANY, "sleep for <n> milliseconds");
577
578 register_command(context, NULL, "time", handle_time_command,
579 COMMAND_ANY, "time <cmd + args> - execute <cmd + args> and print time it took");
580
581 return context;
582 }
583
584 /* sleep command sleeps for <n> miliseconds
585 * this is useful in target startup scripts
586 */
587 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
588 {
589 unsigned long duration = 0;
590
591 if (argc == 1)
592 {
593 duration = strtoul(args[0], NULL, 0);
594 usleep(duration * 1000);
595 }
596
597 return ERROR_OK;
598 }
599
600 int handle_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
601 {
602 if (argc<1)
603 return ERROR_COMMAND_SYNTAX_ERROR;
604
605 duration_t duration;
606 char *duration_text;
607 int retval;
608
609 duration_start_measure(&duration);
610
611 retval = find_and_run_command(cmd_ctx, cmd_ctx->commands, args, argc, 0);
612
613 duration_stop_measure(&duration, &duration_text);
614
615 float t=duration.duration.tv_sec;
616 t+=((float)duration.duration.tv_usec / 1000000.0);
617 command_print(cmd_ctx, "%s took %fs", args[0], t);
618
619 free(duration_text);
620
621 return retval;
622 }

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)