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

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)