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

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)