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

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)