- Added support for native MinGW builds (thanks to Spencer Oliver and Michael Fischer...
[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 = p - word_start;
204
205 /* copy the word */
206 memcpy(words[nwords] = malloc(len + 1), word_start, len);
207 /* add terminating NUL */
208 words[nwords++][len] = 0;
209 }
210
211 /* we're done parsing the line */
212 if (!*p)
213 break;
214
215 /* skip over trailing quote or whitespace*/
216 if (inquote || isspace(*p))
217 p++;
218
219 inquote = 0;
220 word_start = 0;
221 }
222 else if (*p == '"' || *p == '\'')
223 {
224 /* we've reached the beginning of a quote */
225 inquote = *p++;
226 word_start = p;
227 }
228 else
229 {
230 /* we've reached the beginning of a new word */
231 if (!word_start)
232 word_start = p;
233
234 /* normal character, skip */
235 p++;
236 }
237 }
238
239 return nwords;
240 }
241
242 void command_print(command_context_t *context, char *format, ...)
243 {
244 va_list ap;
245 char *buffer = NULL;
246 int n, size = 0;
247 char *p;
248
249 va_start(ap, format);
250
251 /* process format string */
252 /* TODO: possible bug. va_list is undefined after the first call to vsnprintf */
253 while (!buffer || (n = vsnprintf(buffer, size, format, ap)) >= size)
254 {
255 /* increase buffer until it fits the whole string */
256 if (!(p = realloc(buffer, size += 4096)))
257 return;
258
259 buffer = p;
260 }
261
262 /* vsnprintf failed */
263 if (n < 0)
264 return;
265
266 p = buffer;
267
268 /* process lines in buffer */
269 do {
270 char *next = strchr(p, '\n');
271
272 if (next)
273 *next++ = 0;
274
275 if (context->output_handler)
276 context->output_handler(context, p);
277
278 p = next;
279 } while (p);
280
281 if (buffer)
282 free(buffer);
283
284 va_end(ap);
285 }
286
287 int find_and_run_command(command_context_t *context, command_t *commands, char *words[], int num_words, int start_word)
288 {
289 command_t *c;
290
291 for (c = commands; c; c = c->next)
292 {
293 if (strncasecmp(c->name, words[start_word], c->unique_len))
294 continue;
295
296 if (strncasecmp(c->name, words[start_word], strlen(words[start_word])))
297 continue;
298
299 if ((c->mode == context->mode) || (c->mode == COMMAND_ANY))
300 {
301 if (!c->children)
302 {
303 if (!c->handler)
304 {
305 command_print(context, "No handler for command");
306 break;
307 }
308 else
309 {
310 return c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1);
311 }
312 }
313 else
314 {
315 if (start_word == num_words - 1)
316 {
317 command_print(context, "Incomplete command");
318 break;
319 }
320 return find_and_run_command(context, c->children, words, num_words, start_word + 1);
321 }
322 }
323 }
324
325 command_print(context, "Command %s not found", words[start_word]);
326 return ERROR_OK;
327 }
328
329 int command_run_line(command_context_t *context, char *line)
330 {
331 int nwords;
332 char *words[128] = {0};
333 int retval;
334 int i;
335
336 if ((!context) || (!line))
337 return ERROR_INVALID_ARGUMENTS;
338
339 /* skip preceding whitespace */
340 while (isspace(*line))
341 line++;
342
343 /* empty line, ignore */
344 if (!*line)
345 return ERROR_OK;
346
347 if (context->echo)
348 {
349 command_print(context, "%s", line);
350 }
351
352 nwords = parse_line(line, words, sizeof(words) / sizeof(words[0]));
353
354 if (nwords > 0)
355 retval = find_and_run_command(context, context->commands, words, nwords, 0);
356 else
357 return ERROR_INVALID_ARGUMENTS;
358
359 for (i = 0; i < nwords; i++)
360 free(words[i]);
361
362 return retval;
363 }
364
365 int command_run_file(command_context_t *context, FILE *file, enum command_mode mode)
366 {
367 int retval;
368 int old_command_mode;
369 char buffer[4096];
370
371 old_command_mode = context->mode;
372 context->mode = mode;
373
374 while (fgets(buffer, 4096, file))
375 {
376 char *p;
377 char *cmd, *end;
378
379 /* stop processing line after a comment (#, !) or a LF, CR were encountered */
380 if ((p = strpbrk(buffer, "#!\r\n")))
381 *p = 0;
382
383 /* skip over leading whitespace */
384 cmd = buffer;
385 while (isspace(*cmd))
386 cmd++;
387
388 /* empty (all whitespace) line? */
389 if (!*cmd)
390 continue;
391
392 /* search the end of the current line, ignore trailing whitespace */
393 for (p = end = cmd; *p; p++)
394 if (!isspace(*p))
395 end = p;
396
397 /* terminate end */
398 *++end = 0;
399 if (strcasecmp(cmd, "quit") == 0)
400 break;
401
402 /* run line */
403 if (command_run_line(context, cmd) == ERROR_COMMAND_CLOSE_CONNECTION)
404 break;
405 }
406
407 context->mode = old_command_mode;
408
409 return retval;
410 }
411
412 void command_print_help_line(command_context_t* context, struct command_s *command, int indent)
413 {
414 command_t *c;
415 char indents[32] = {0};
416 char *help = "no help available";
417 char name_buf[64];
418 int i;
419
420 for (i = 0; i < indent; i+=2)
421 {
422 indents[i*2] = ' ';
423 indents[i*2+1] = '-';
424 }
425 indents[i*2] = 0;
426
427 if ((command->mode == COMMAND_EXEC) || (command->mode == COMMAND_ANY))
428 {
429 if (command->help)
430 help = command->help;
431
432 snprintf(name_buf, 64, command->name);
433 strncat(name_buf, indents, 64);
434 command_print(context, "%20s\t%s", name_buf, help);
435 }
436
437 if (command->children)
438 {
439 for (c = command->children; c; c = c->next)
440 {
441 command_print_help_line(context, c, indent + 1);
442 }
443 }
444 }
445
446 int command_print_help(command_context_t* context, char* name, char** args, int argc)
447 {
448 command_t *c;
449
450 for (c = context->commands; c; c = c->next)
451 {
452 if (argc == 1)
453 {
454 if (strncasecmp(c->name, args[0], c->unique_len))
455 continue;
456
457 if (strncasecmp(c->name, args[0], strlen(args[0])))
458 continue;
459 }
460
461 command_print_help_line(context, c, 0);
462 }
463
464 return ERROR_OK;
465 }
466
467 void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, char* line), void *priv)
468 {
469 context->output_handler = output_handler;
470 context->output_handler_priv = priv;
471 }
472
473 command_context_t* copy_command_context(command_context_t* context)
474 {
475 command_context_t* copy_context = malloc(sizeof(command_context_t));
476
477 *copy_context = *context;
478
479 return copy_context;
480 }
481
482 int command_done(command_context_t *context)
483 {
484 free(context);
485
486 return ERROR_OK;
487 }
488
489 command_context_t* command_init()
490 {
491 command_context_t* context = malloc(sizeof(command_context_t));
492
493 context->mode = COMMAND_EXEC;
494 context->commands = NULL;
495 context->current_target = 0;
496 context->echo = 0;
497 context->output_handler = NULL;
498 context->output_handler_priv = NULL;
499
500 register_command(context, NULL, "help", command_print_help,
501 COMMAND_EXEC, "display this help");
502
503 register_command(context, NULL, "sleep", handle_sleep_command,
504 COMMAND_ANY, "sleep for <n> milliseconds");
505
506 return context;
507 }
508
509 /* sleep command sleeps for <n> miliseconds
510 * this is useful in target startup scripts
511 */
512 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
513 {
514 unsigned long duration = 0;
515
516 if (argc == 1)
517 {
518 duration = strtoul(args[0], NULL, 0);
519 usleep(duration * 1000);
520 }
521
522 return ERROR_OK;
523 }

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)