- minimum autoconf 2.59 is now required and verified - due to issues with AS_HELP_STRING
[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 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)
80 {
81 command_t *c, *p;
82
83 if (!context || !name)
84 return NULL;
85
86 c = malloc(sizeof(command_t));
87
88 c->name = strdup(name);
89 c->parent = parent;
90 c->children = NULL;
91 c->handler = handler;
92 c->mode = mode;
93 if (help)
94 c->help = strdup(help);
95 else
96 c->help = NULL;
97 c->unique_len = 0;
98 c->next = NULL;
99
100 /* place command in tree */
101 if (parent)
102 {
103 if (parent->children)
104 {
105 /* find last child */
106 for (p = parent->children; p && p->next; p = p->next);
107 if (p)
108 p->next = c;
109 }
110 else
111 {
112 parent->children = c;
113 }
114 }
115 else
116 {
117 if (context->commands)
118 {
119 /* find last command */
120 for (p = context->commands; p && p->next; p = p->next);
121 if (p)
122 p->next = c;
123 }
124 else
125 {
126 context->commands = c;
127 }
128 }
129
130 /* update unique lengths */
131 build_unique_lengths(context, (parent) ? parent : context->commands);
132
133 return c;
134 }
135
136 int unregister_command(command_context_t *context, char *name)
137 {
138 command_t *c, *p = NULL, *c2;
139
140 if ((!context) || (!name))
141 return ERROR_INVALID_ARGUMENTS;
142
143 /* find command */
144 for (c = context->commands; c; c = c->next)
145 {
146 if (strcmp(name, c->name) == 0)
147 {
148 /* unlink command */
149 if (p)
150 {
151 p->next = c->next;
152 }
153 else
154 {
155 context->commands = c->next;
156 }
157
158 /* unregister children */
159 if (c->children)
160 {
161 for (c2 = c->children; c2; c2 = c2->next)
162 {
163 free(c2->name);
164 if (c2->help)
165 free(c2->help);
166 free(c2);
167 }
168 }
169
170 /* delete command */
171 free(c->name);
172 if (c->help)
173 free(c->help);
174 free(c);
175 }
176
177 /* remember the last command for unlinking */
178 p = c;
179 }
180
181 return ERROR_OK;
182 }
183
184 int parse_line(char *line, char *words[], int max_words)
185 {
186 int nwords = 0;
187 char *p = line;
188 char *word_start = line;
189 int inquote = 0;
190
191 while (nwords < max_words - 1)
192 {
193 /* check if we reached
194 * a terminating NUL
195 * a matching closing quote character " or '
196 * we're inside a word but not a quote, and the current character is whitespace
197 */
198 if (!*p || *p == inquote || (word_start && !inquote && isspace(*p)))
199 {
200 /* we're inside a word or quote, and reached its end*/
201 if (word_start)
202 {
203 int len;
204 char *word_end=p;
205
206 /* This will handle extra whitespace within quotes */
207 while (isspace(*word_start)&&(word_start<word_end))
208 word_start++;
209 while (isspace(*(word_end-1))&&(word_start<word_end))
210 word_end--;
211 len = word_end - word_start;
212
213 if (len>0)
214 {
215 /* copy the word */
216 memcpy(words[nwords] = malloc(len + 1), word_start, len);
217 /* add terminating NUL */
218 words[nwords++][len] = 0;
219 }
220 }
221 /* we're done parsing the line */
222 if (!*p)
223 break;
224
225 /* skip over trailing quote or whitespace*/
226 if (inquote || isspace(*p))
227 p++;
228 while (isspace(*p))
229 p++;
230
231 inquote = 0;
232 word_start = 0;
233 }
234 else if (*p == '"' || *p == '\'')
235 {
236 /* we've reached the beginning of a quote */
237 inquote = *p++;
238 word_start = p;
239 }
240 else
241 {
242 /* we've reached the beginning of a new word */
243 if (!word_start)
244 word_start = p;
245
246 /* normal character, skip */
247 p++;
248 }
249 }
250
251 return nwords;
252 }
253
254 void command_print(command_context_t *context, char *format, ...)
255 {
256 va_list ap;
257 char *buffer = NULL;
258 int n, size = 0;
259 char *p;
260
261 va_start(ap, format);
262
263 /* process format string */
264 /* TODO: possible bug. va_list is undefined after the first call to vsnprintf */
265 while (!buffer || (n = vsnprintf(buffer, size, format, ap)) >= size)
266 {
267 /* increase buffer until it fits the whole string */
268 if (!(p = realloc(buffer, size += 4096)))
269 {
270 /* gotta free up */
271 if (buffer)
272 free(buffer);
273 return;
274 }
275
276 buffer = p;
277 }
278
279 /* vsnprintf failed */
280 if (n < 0)
281 {
282 if (buffer)
283 free(buffer);
284 return;
285 }
286
287 p = buffer;
288
289 /* process lines in buffer */
290 do {
291 char *next = strchr(p, '\n');
292
293 if (next)
294 *next++ = 0;
295
296 if (context->output_handler)
297 context->output_handler(context, p);
298
299 p = next;
300 } while (p);
301
302 if (buffer)
303 free(buffer);
304
305 va_end(ap);
306 }
307
308 int find_and_run_command(command_context_t *context, command_t *commands, char *words[], int num_words, int start_word)
309 {
310 command_t *c;
311
312 for (c = commands; c; c = c->next)
313 {
314 if (strncasecmp(c->name, words[start_word], c->unique_len))
315 continue;
316
317 if (strncasecmp(c->name, words[start_word], strlen(words[start_word])))
318 continue;
319
320 if ((c->mode == context->mode) || (c->mode == COMMAND_ANY))
321 {
322 if (!c->children)
323 {
324 if (!c->handler)
325 {
326 command_print(context, "No handler for command");
327 break;
328 }
329 else
330 {
331 return c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1);
332 }
333 }
334 else
335 {
336 if (start_word == num_words - 1)
337 {
338 command_print(context, "Incomplete command");
339 break;
340 }
341 return find_and_run_command(context, c->children, words, num_words, start_word + 1);
342 }
343 }
344 }
345
346 command_print(context, "Command %s not found", words[start_word]);
347 return ERROR_OK;
348 }
349
350 int command_run_line(command_context_t *context, char *line)
351 {
352 int nwords;
353 char *words[128] = {0};
354 int retval;
355 int i;
356
357 if ((!context) || (!line))
358 return ERROR_INVALID_ARGUMENTS;
359
360 /* skip preceding whitespace */
361 while (isspace(*line))
362 line++;
363
364 /* empty line, ignore */
365 if (!*line)
366 return ERROR_OK;
367
368 /* ignore comments */
369 if (*line && (line[0] == '#'))
370 return ERROR_OK;
371
372 if (context->echo)
373 {
374 command_print(context, "%s", line);
375 }
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 indents[32] = {0};
448 char *help = "no help available";
449 char name_buf[64];
450 int i;
451
452 for (i = 0; i < indent; i+=2)
453 {
454 indents[i*2] = ' ';
455 indents[i*2+1] = '-';
456 }
457 indents[i*2] = 0;
458
459 if ((command->mode == COMMAND_EXEC) || (command->mode == COMMAND_ANY))
460 {
461 if (command->help)
462 help = command->help;
463
464 snprintf(name_buf, 64, command->name);
465 strncat(name_buf, indents, 64);
466 command_print(context, "%20s\t%s", name_buf, help);
467 }
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(command_context_t* context, char* name, char** args, int argc)
479 {
480 command_t *c;
481
482 for (c = context->commands; c; c = c->next)
483 {
484 if (argc == 1)
485 {
486 if (strncasecmp(c->name, args[0], c->unique_len))
487 continue;
488
489 if (strncasecmp(c->name, args[0], strlen(args[0])))
490 continue;
491 }
492
493 command_print_help_line(context, c, 0);
494 }
495
496 return ERROR_OK;
497 }
498
499 void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, char* line), void *priv)
500 {
501 context->output_handler = output_handler;
502 context->output_handler_priv = priv;
503 }
504
505 command_context_t* copy_command_context(command_context_t* context)
506 {
507 command_context_t* copy_context = malloc(sizeof(command_context_t));
508
509 *copy_context = *context;
510
511 return copy_context;
512 }
513
514 int command_done(command_context_t *context)
515 {
516 free(context);
517 context = NULL;
518
519 return ERROR_OK;
520 }
521
522 command_context_t* command_init()
523 {
524 command_context_t* context = malloc(sizeof(command_context_t));
525
526 context->mode = COMMAND_EXEC;
527 context->commands = NULL;
528 context->current_target = 0;
529 context->echo = 0;
530 context->output_handler = NULL;
531 context->output_handler_priv = NULL;
532
533 register_command(context, NULL, "help", command_print_help,
534 COMMAND_EXEC, "display this help");
535
536 register_command(context, NULL, "sleep", handle_sleep_command,
537 COMMAND_ANY, "sleep for <n> milliseconds");
538
539 return context;
540 }
541
542 /* sleep command sleeps for <n> miliseconds
543 * this is useful in target startup scripts
544 */
545 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
546 {
547 unsigned long duration = 0;
548
549 if (argc == 1)
550 {
551 duration = strtoul(args[0], NULL, 0);
552 usleep(duration * 1000);
553 }
554
555 return ERROR_OK;
556 }

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)