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

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)