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

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)