print syntax for command upon syntax error.
[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 #include "time_support.h"
33
34 #include <stdlib.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <unistd.h>
40
41 #include <openocd_tcl.h>
42
43 int fast_and_dangerous = 0;
44
45 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
46 int handle_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
47 int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
48
49 /* forward declaration of jim_command */
50 extern int jim_command(command_context_t *context, char *line);
51
52
53
54 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)
55 {
56 command_t *c, *p;
57
58 if (!context || !name)
59 return NULL;
60
61 c = malloc(sizeof(command_t));
62
63 c->name = strdup(name);
64 c->parent = parent;
65 c->children = NULL;
66 c->handler = handler;
67 c->mode = mode;
68 if (!help)
69 help="";
70 c->next = NULL;
71
72 /* place command in tree */
73 if (parent)
74 {
75 if (parent->children)
76 {
77 /* find last child */
78 for (p = parent->children; p && p->next; p = p->next);
79 if (p)
80 p->next = c;
81 }
82 else
83 {
84 parent->children = c;
85 }
86 }
87 else
88 {
89 if (context->commands)
90 {
91 /* find last command */
92 for (p = context->commands; p && p->next; p = p->next);
93 if (p)
94 p->next = c;
95 }
96 else
97 {
98 context->commands = c;
99 }
100 }
101 /* accumulate help text in Tcl helptext list. */
102 Jim_Obj *helptext=Jim_GetGlobalVariableStr(interp, "ocd_helptext", JIM_ERRMSG);
103 if (Jim_IsShared(helptext))
104 helptext = Jim_DuplicateObj(interp, helptext);
105 Jim_Obj *cmd_entry=Jim_NewListObj(interp, NULL, 0);
106
107 Jim_Obj *cmd_list=Jim_NewListObj(interp, NULL, 0);
108
109 /* maximum of two levels :-) */
110 if (c->parent!=NULL)
111 {
112 Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->parent->name, -1));
113 }
114 Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->name, -1));
115
116 Jim_ListAppendElement(interp, cmd_entry, cmd_list);
117 Jim_ListAppendElement(interp, cmd_entry, Jim_NewStringObj(interp, help, -1));
118 Jim_ListAppendElement(interp, helptext, cmd_entry);
119 return c;
120 }
121
122 int unregister_all_commands(command_context_t *context)
123 {
124 command_t *c, *c2;
125
126 if (context == NULL)
127 return ERROR_OK;
128
129
130 while(NULL != context->commands)
131 {
132 c = context->commands;
133
134 while(NULL != c->children)
135 {
136 c2 = c->children;
137 c->children = c->children->next;
138 free(c2->name);
139 c2->name = NULL;
140 free(c2);
141 c2 = NULL;
142 }
143
144 context->commands = context->commands->next;
145
146 free(c->name);
147 c->name = NULL;
148 free(c);
149 c = NULL;
150 }
151
152 return ERROR_OK;
153 }
154
155 int unregister_command(command_context_t *context, char *name)
156 {
157 command_t *c, *p = NULL, *c2;
158
159 if ((!context) || (!name))
160 return ERROR_INVALID_ARGUMENTS;
161
162 /* find command */
163 for (c = context->commands; c; c = c->next)
164 {
165 if (strcmp(name, c->name) == 0)
166 {
167 /* unlink command */
168 if (p)
169 {
170 p->next = c->next;
171 }
172 else
173 {
174 context->commands = c->next;
175 }
176
177 /* unregister children */
178 if (c->children)
179 {
180 for (c2 = c->children; c2; c2 = c2->next)
181 {
182 free(c2->name);
183 free(c2);
184 }
185 }
186
187 /* delete command */
188 free(c->name);
189 free(c);
190 }
191
192 /* remember the last command for unlinking */
193 p = c;
194 }
195
196 return ERROR_OK;
197 }
198
199 int parse_line(char *line, char *words[], int max_words)
200 {
201 int nwords = 0;
202 char *p = line;
203 char *word_start = line;
204 int inquote = 0;
205
206 while (nwords < max_words - 1)
207 {
208 /* check if we reached
209 * a terminating NUL
210 * a matching closing quote character " or '
211 * we're inside a word but not a quote, and the current character is whitespace
212 */
213 if (!*p || *p == inquote || (word_start && !inquote && isspace(*p)))
214 {
215 /* we're inside a word or quote, and reached its end*/
216 if (word_start)
217 {
218 int len;
219 char *word_end=p;
220
221 /* This will handle extra whitespace within quotes */
222 while (isspace(*word_start)&&(word_start<word_end))
223 word_start++;
224 while (isspace(*(word_end-1))&&(word_start<word_end))
225 word_end--;
226 len = word_end - word_start;
227
228 if (len>0)
229 {
230 /* copy the word */
231 memcpy(words[nwords] = malloc(len + 1), word_start, len);
232 /* add terminating NUL */
233 words[nwords++][len] = 0;
234 }
235 }
236 /* we're done parsing the line */
237 if (!*p)
238 break;
239
240 /* skip over trailing quote or whitespace*/
241 if (inquote || isspace(*p))
242 p++;
243 while (isspace(*p))
244 p++;
245
246 inquote = 0;
247 word_start = 0;
248 }
249 else if (*p == '"' || *p == '\'')
250 {
251 /* we've reached the beginning of a quote */
252 inquote = *p++;
253 word_start = p;
254 }
255 else
256 {
257 /* we've reached the beginning of a new word */
258 if (!word_start)
259 word_start = p;
260
261 /* normal character, skip */
262 p++;
263 }
264 }
265
266 return nwords;
267 }
268
269 void command_output_text(command_context_t *context, const char *data)
270 {
271 if( context && context->output_handler && data ){
272 context->output_handler( context, data );
273 }
274 }
275
276 void command_print_n(command_context_t *context, char *format, ...)
277 {
278 char *string;
279
280 va_list ap;
281 va_start(ap, format);
282
283 string = alloc_vprintf(format, ap);
284 if (string != NULL)
285 {
286 context->output_handler(context, string);
287 free(string);
288 }
289
290 va_end(ap);
291 }
292
293 void command_print(command_context_t *context, char *format, ...)
294 {
295 char *string;
296
297 va_list ap;
298 va_start(ap, format);
299
300 string = alloc_vprintf(format, ap);
301 if (string != NULL)
302 {
303 strcat(string, "\n"); /* alloc_vprintf guaranteed the buffer to be at least one char longer */
304 context->output_handler(context, string);
305 free(string);
306 }
307
308 va_end(ap);
309 }
310
311 command_t *find_command(command_context_t *context, command_t *commands, char *words[], int num_words, int start_word, int *new_start_word)
312 {
313 command_t *c;
314
315 for (c = commands; c; c = c->next)
316 {
317 if (strcasecmp(c->name, words[start_word]))
318 continue;
319
320 if ((context->mode == COMMAND_CONFIG) || (c->mode == COMMAND_ANY) || (c->mode == context->mode) )
321 {
322 if (!c->children)
323 {
324 if (!c->handler)
325 {
326 return NULL;
327 }
328 else
329 {
330 *new_start_word=start_word;
331 return c;
332 }
333 }
334 else
335 {
336 if (start_word == num_words - 1)
337 {
338 return NULL;
339 }
340 return find_command(context, c->children, words, num_words, start_word + 1, new_start_word);
341 }
342 }
343 }
344 return NULL;
345 }
346
347 int find_and_run_command(command_context_t *context, command_t *commands, char *words[], int num_words)
348 {
349 int start_word=0;
350 command_t *c;
351 c = find_command(context, commands, words, num_words, start_word, &start_word);
352 if (c == NULL)
353 {
354 /* just return command not found */
355 return ERROR_COMMAND_NOTFOUND;
356 }
357
358 int retval = c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1);
359 if (retval == ERROR_COMMAND_SYNTAX_ERROR)
360 {
361 /* Print help for command */
362 const char *t1="";
363 const char *t2="";
364 const char *t3="";
365 /* maximum of two levels :-) */
366 if (c->parent!=NULL)
367 {
368 t1=c->parent->name;
369 t2=" ";
370 }
371 t3=c->name;
372 command_run_linef(context, "help {%s%s%s}", t1, t2, t3);
373
374
375 }
376 else if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
377 {
378 /* just fall through for a shutdown request */
379 }
380 else if (retval != ERROR_OK)
381 {
382 /* we do not print out an error message because the command *should*
383 * have printed out an error
384 */
385 LOG_DEBUG("Command failed with error code %d", retval);
386 }
387
388 return retval;
389 }
390
391 int command_run_line_internal(command_context_t *context, char *line)
392 {
393 LOG_USER_N("%s", ""); /* Keep GDB connection alive*/
394
395 int nwords;
396 char *words[128] = {0};
397 int retval;
398 int i;
399
400 /* skip preceding whitespace */
401 while (isspace(*line))
402 line++;
403
404 /* empty line, ignore */
405 if (!*line)
406 return ERROR_OK;
407
408 /* ignore comments */
409 if (*line && (line[0] == '#'))
410 return ERROR_OK;
411
412 LOG_DEBUG("%s", line);
413
414 nwords = parse_line(line, words, sizeof(words) / sizeof(words[0]));
415
416 if (nwords > 0)
417 {
418 retval = find_and_run_command(context, context->commands, words, nwords);
419 }
420 else
421 return ERROR_INVALID_ARGUMENTS;
422
423 for (i = 0; i < nwords; i++)
424 free(words[i]);
425
426 return retval;
427 }
428
429 int command_run_line(command_context_t *context, char *line)
430 {
431 /* if a command is unknown to the "unknown" proc in tcl/commands.tcl will
432 * redirect it to OpenOCD.
433 *
434 * This avoids having to type the "openocd" prefix and makes OpenOCD
435 * commands "native" to Tcl.
436 */
437 return jim_command(context, line);
438 }
439
440
441 int command_run_linef(command_context_t *context, char *format, ...)
442 {
443 int retval=ERROR_FAIL;
444 char *string;
445 va_list ap;
446 va_start(ap, format);
447 string = alloc_vprintf(format, ap);
448 if (string!=NULL)
449 {
450 retval=command_run_line(context, string);
451 }
452 va_end(ap);
453 return retval;
454 }
455
456
457
458 void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, const char* line), void *priv)
459 {
460 context->output_handler = output_handler;
461 context->output_handler_priv = priv;
462 }
463
464 command_context_t* copy_command_context(command_context_t* context)
465 {
466 command_context_t* copy_context = malloc(sizeof(command_context_t));
467
468 *copy_context = *context;
469
470 return copy_context;
471 }
472
473 int command_done(command_context_t *context)
474 {
475 free(context);
476 context = NULL;
477
478 return ERROR_OK;
479 }
480
481 command_context_t* command_init()
482 {
483 command_context_t* context = malloc(sizeof(command_context_t));
484
485 context->mode = COMMAND_EXEC;
486 context->commands = NULL;
487 context->current_target = 0;
488 context->output_handler = NULL;
489 context->output_handler_priv = NULL;
490
491 register_command(context, NULL, "sleep", handle_sleep_command,
492 COMMAND_ANY, "sleep for <n> milliseconds");
493
494 register_command(context, NULL, "time", handle_time_command,
495 COMMAND_ANY, "time <cmd + args> - execute <cmd + args> and print time it took");
496
497 register_command(context, NULL, "fast", handle_fast_command,
498 COMMAND_ANY, "fast <enable/disable> - place at beginning of config files. Sets defaults to fast and dangerous.");
499
500 return context;
501 }
502
503 /* sleep command sleeps for <n> miliseconds
504 * this is useful in target startup scripts
505 */
506 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
507 {
508 unsigned long duration = 0;
509
510 if (argc == 1)
511 {
512 duration = strtoul(args[0], NULL, 0);
513 usleep(duration * 1000);
514 }
515
516 return ERROR_OK;
517 }
518
519 int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
520 {
521 if (argc!=1)
522 return ERROR_COMMAND_SYNTAX_ERROR;
523
524 fast_and_dangerous = strcmp("enable", args[0])==0;
525
526 return ERROR_OK;
527 }
528
529
530 int handle_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
531 {
532 duration_t duration;
533 char *duration_text;
534 int retval;
535 float t;
536
537 if (argc<1)
538 return ERROR_COMMAND_SYNTAX_ERROR;
539
540 duration_start_measure(&duration);
541
542 retval = find_and_run_command(cmd_ctx, cmd_ctx->commands, args, argc);
543 if (retval == ERROR_COMMAND_NOTFOUND)
544 {
545 command_print(cmd_ctx, "Command %s not found", args[0]);
546 }
547
548 duration_stop_measure(&duration, &duration_text);
549
550 t=duration.duration.tv_sec;
551 t+=((float)duration.duration.tv_usec / 1000000.0);
552 command_print(cmd_ctx, "%s took %fs", args[0], t);
553
554 free(duration_text);
555
556 return retval;
557 }

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)