better error messages for target event scripts.
[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
362 }
363 else if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
364 {
365 /* just fall through for a shutdown request */
366 }
367 else if (retval != ERROR_OK)
368 {
369 /* we do not print out an error message because the command *should*
370 * have printed out an error
371 */
372 LOG_DEBUG("Command failed with error code %d", retval);
373 }
374
375 return retval;
376 }
377
378 int command_run_line_internal(command_context_t *context, char *line)
379 {
380 LOG_USER_N("%s", ""); /* Keep GDB connection alive*/
381
382 int nwords;
383 char *words[128] = {0};
384 int retval;
385 int i;
386
387 /* skip preceding whitespace */
388 while (isspace(*line))
389 line++;
390
391 /* empty line, ignore */
392 if (!*line)
393 return ERROR_OK;
394
395 /* ignore comments */
396 if (*line && (line[0] == '#'))
397 return ERROR_OK;
398
399 LOG_DEBUG("%s", line);
400
401 nwords = parse_line(line, words, sizeof(words) / sizeof(words[0]));
402
403 if (nwords > 0)
404 {
405 retval = find_and_run_command(context, context->commands, words, nwords);
406 }
407 else
408 return ERROR_INVALID_ARGUMENTS;
409
410 for (i = 0; i < nwords; i++)
411 free(words[i]);
412
413 return retval;
414 }
415
416 int command_run_line(command_context_t *context, char *line)
417 {
418 /* if a command is unknown to the "unknown" proc in tcl/commands.tcl will
419 * redirect it to OpenOCD.
420 *
421 * This avoids having to type the "openocd" prefix and makes OpenOCD
422 * commands "native" to Tcl.
423 */
424 return jim_command(context, line);
425 }
426
427
428 int command_run_linef(command_context_t *context, char *format, ...)
429 {
430 int retval=ERROR_FAIL;
431 char *string;
432 va_list ap;
433 va_start(ap, format);
434 string = alloc_vprintf(format, ap);
435 if (string!=NULL)
436 {
437 retval=command_run_line(context, string);
438 }
439 va_end(ap);
440 return retval;
441 }
442
443
444
445 void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, const char* line), void *priv)
446 {
447 context->output_handler = output_handler;
448 context->output_handler_priv = priv;
449 }
450
451 command_context_t* copy_command_context(command_context_t* context)
452 {
453 command_context_t* copy_context = malloc(sizeof(command_context_t));
454
455 *copy_context = *context;
456
457 return copy_context;
458 }
459
460 int command_done(command_context_t *context)
461 {
462 free(context);
463 context = NULL;
464
465 return ERROR_OK;
466 }
467
468 command_context_t* command_init()
469 {
470 command_context_t* context = malloc(sizeof(command_context_t));
471
472 context->mode = COMMAND_EXEC;
473 context->commands = NULL;
474 context->current_target = 0;
475 context->output_handler = NULL;
476 context->output_handler_priv = NULL;
477
478 register_command(context, NULL, "sleep", handle_sleep_command,
479 COMMAND_ANY, "sleep for <n> milliseconds");
480
481 register_command(context, NULL, "time", handle_time_command,
482 COMMAND_ANY, "time <cmd + args> - execute <cmd + args> and print time it took");
483
484 register_command(context, NULL, "fast", handle_fast_command,
485 COMMAND_ANY, "fast <enable/disable> - place at beginning of config files. Sets defaults to fast and dangerous.");
486
487 return context;
488 }
489
490 /* sleep command sleeps for <n> miliseconds
491 * this is useful in target startup scripts
492 */
493 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
494 {
495 unsigned long duration = 0;
496
497 if (argc == 1)
498 {
499 duration = strtoul(args[0], NULL, 0);
500 usleep(duration * 1000);
501 }
502
503 return ERROR_OK;
504 }
505
506 int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
507 {
508 if (argc!=1)
509 return ERROR_COMMAND_SYNTAX_ERROR;
510
511 fast_and_dangerous = strcmp("enable", args[0])==0;
512
513 return ERROR_OK;
514 }
515
516
517 int handle_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
518 {
519 duration_t duration;
520 char *duration_text;
521 int retval;
522 float t;
523
524 if (argc<1)
525 return ERROR_COMMAND_SYNTAX_ERROR;
526
527 duration_start_measure(&duration);
528
529 retval = find_and_run_command(cmd_ctx, cmd_ctx->commands, args, argc);
530 if (retval == ERROR_COMMAND_NOTFOUND)
531 {
532 command_print(cmd_ctx, "Command %s not found", args[0]);
533 }
534
535 duration_stop_measure(&duration, &duration_text);
536
537 t=duration.duration.tv_sec;
538 t+=((float)duration.duration.tv_usec / 1000000.0);
539 command_print(cmd_ctx, "%s took %fs", args[0], t);
540
541 free(duration_text);
542
543 return retval;
544 }

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)