d8b78759e28ae0ccf0123ae969d88fb911ec6c98
[openocd.git] / src / helper / command.c
1 /***************************************************************************
2 * Copyright (C) 2005 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
4 * *
5 * Copyright (C) 2007,2008 Øyvind Harboe *
6 * oyvind.harboe@zylin.com *
7 * *
8 * Copyright (C) 2008, Duane Ellis *
9 * openocd@duaneeellis.com *
10 * *
11 * part of this file is taken from libcli (libcli.sourceforge.net) *
12 * Copyright (C) David Parrish (david@dparrish.com) *
13 * *
14 * This program is free software; you can redistribute it and/or modify *
15 * it under the terms of the GNU General Public License as published by *
16 * the Free Software Foundation; either version 2 of the License, or *
17 * (at your option) any later version. *
18 * *
19 * This program is distributed in the hope that it will be useful, *
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
22 * GNU General Public License for more details. *
23 * *
24 * You should have received a copy of the GNU General Public License *
25 * along with this program; if not, write to the *
26 * Free Software Foundation, Inc., *
27 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
28 ***************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #if !BUILD_ECOSBOARD
34 /* see Embedder-HOWTO.txt in Jim Tcl project hosted on BerliOS*/
35 #define JIM_EMBEDDED
36 #endif
37
38 // @todo the inclusion of target.h here is a layering violation
39 #include "target.h"
40 #include "command.h"
41 #include "configuration.h"
42 #include "log.h"
43 #include "time_support.h"
44 #include "jim-eventloop.h"
45
46
47 Jim_Interp *interp = NULL;
48
49 static int run_command(struct command_context *context,
50 struct command *c, const char *words[], unsigned num_words);
51
52 static void tcl_output(void *privData, const char *file, unsigned line,
53 const char *function, const char *string)
54 {
55 Jim_Obj *tclOutput = (Jim_Obj *)privData;
56 Jim_AppendString(interp, tclOutput, string, strlen(string));
57 }
58
59 extern struct command_context *global_cmd_ctx;
60
61 void script_debug(Jim_Interp *interp, const char *name,
62 unsigned argc, Jim_Obj *const *argv)
63 {
64 LOG_DEBUG("command - %s", name);
65 for (unsigned i = 0; i < argc; i++)
66 {
67 int len;
68 const char *w = Jim_GetString(argv[i], &len);
69
70 /* end of line comment? */
71 if (*w == '#')
72 break;
73
74 LOG_DEBUG("%s - argv[%d]=%s", name, i, w);
75 }
76 }
77
78 static void script_command_args_free(const char **words, unsigned nwords)
79 {
80 for (unsigned i = 0; i < nwords; i++)
81 free((void *)words[i]);
82 free(words);
83 }
84 static const char **script_command_args_alloc(
85 unsigned argc, Jim_Obj *const *argv, unsigned *nwords)
86 {
87 const char **words = malloc(argc * sizeof(char *));
88 if (NULL == words)
89 return NULL;
90
91 unsigned i;
92 for (i = 0; i < argc; i++)
93 {
94 int len;
95 const char *w = Jim_GetString(argv[i], &len);
96 /* a comment may end the line early */
97 if (*w == '#')
98 break;
99
100 words[i] = strdup(w);
101 if (words[i] == NULL)
102 {
103 script_command_args_free(words, i);
104 return NULL;
105 }
106 }
107 *nwords = i;
108 return words;
109 }
110
111 static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
112 {
113 /* the private data is stashed in the interp structure */
114 struct command *c;
115 struct command_context *context;
116 int retval;
117
118 /* DANGER!!!! be careful what we invoke here, since interp->cmdPrivData might
119 * get overwritten by running other Jim commands! Treat it as an
120 * emphemeral global variable that is used in lieu of an argument
121 * to the fn and fish it out manually.
122 */
123 c = interp->cmdPrivData;
124 if (c == NULL)
125 {
126 LOG_ERROR("BUG: interp->cmdPrivData == NULL");
127 return JIM_ERR;
128 }
129 target_call_timer_callbacks_now();
130 LOG_USER_N("%s", ""); /* Keep GDB connection alive*/
131
132 script_debug(interp, c->name, argc, argv);
133
134 unsigned nwords;
135 const char **words = script_command_args_alloc(argc, argv, &nwords);
136 if (NULL == words)
137 return JIM_ERR;
138
139 /* grab the command context from the associated data */
140 context = Jim_GetAssocData(interp, "context");
141 if (context == NULL)
142 {
143 /* Tcl can invoke commands directly instead of via command_run_line(). This would
144 * happen when the Jim Tcl interpreter is provided by eCos.
145 */
146 context = global_cmd_ctx;
147 }
148
149 /* capture log output and return it */
150 Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0);
151 /* a garbage collect can happen, so we need a reference count to this object */
152 Jim_IncrRefCount(tclOutput);
153
154 log_add_callback(tcl_output, tclOutput);
155
156 retval = run_command(context, c, (const char **)words, nwords);
157
158 log_remove_callback(tcl_output, tclOutput);
159
160 /* We dump output into this local variable */
161 Jim_SetResult(interp, tclOutput);
162 Jim_DecrRefCount(interp, tclOutput);
163
164 script_command_args_free(words, nwords);
165
166 int *return_retval = Jim_GetAssocData(interp, "retval");
167 if (return_retval != NULL)
168 {
169 *return_retval = retval;
170 }
171
172 return (retval == ERROR_OK)?JIM_OK:JIM_ERR;
173 }
174
175 /* nice short description of source file */
176 #define __THIS__FILE__ "command.c"
177
178 /**
179 * Find a command by name from a list of commands.
180 * @returns The named command if found, or NULL.
181 */
182 static struct command *command_find(struct command *head, const char *name)
183 {
184 for (struct command *cc = head; cc; cc = cc->next)
185 {
186 if (strcmp(cc->name, name) == 0)
187 return cc;
188 }
189 return NULL;
190 }
191
192 /**
193 * Add the command to the end of linked list.
194 * @returns Returns false if the named command already exists in the list.
195 * Returns true otherwise.
196 */
197 static void command_add_child(struct command **head, struct command *c)
198 {
199 assert(head);
200 if (NULL == *head)
201 {
202 *head = c;
203 return;
204 }
205 struct command *cc = *head;
206 while (cc->next) cc = cc->next;
207 cc->next = c;
208 }
209
210 static struct command **command_list_for_parent(
211 struct command_context *cmd_ctx, struct command *parent)
212 {
213 return parent ? &parent->children : &cmd_ctx->commands;
214 }
215
216 static struct command *command_new(struct command_context *cmd_ctx,
217 struct command *parent, const char *name,
218 command_handler_t handler, enum command_mode mode,
219 const char *help)
220 {
221 assert(name);
222
223 struct command *c = malloc(sizeof(struct command));
224 memset(c, 0, sizeof(struct command));
225
226 c->name = strdup(name);
227 if (help)
228 c->help = strdup(help);
229 c->parent = parent;
230 c->handler = handler;
231 c->mode = mode;
232
233 command_add_child(command_list_for_parent(cmd_ctx, parent), c);
234
235 return c;
236 }
237 static void command_free(struct command *c)
238 {
239 /// @todo if command has a handler, unregister its jim command!
240
241 while (NULL != c->children)
242 {
243 struct command *tmp = c->children;
244 c->children = tmp->next;
245 command_free(tmp);
246 }
247
248 if (c->name)
249 free(c->name);
250 if (c->help)
251 free((void*)c->help);
252 free(c);
253 }
254
255 struct command* register_command(struct command_context *context,
256 struct command *parent, const char *name,
257 command_handler_t handler, enum command_mode mode,
258 const char *help)
259 {
260 if (!context || !name)
261 return NULL;
262
263 struct command **head = command_list_for_parent(context, parent);
264 struct command *c = command_find(*head, name);
265 if (NULL != c)
266 {
267 LOG_ERROR("command '%s' is already registered in '%s' context",
268 name, parent ? parent->name : "<global>");
269 return c;
270 }
271
272 c = command_new(context, parent, name, handler, mode, help);
273 /* if allocation failed or it is a placeholder (no handler), we're done */
274 if (NULL == c || NULL == c->handler)
275 return c;
276
277 const char *full_name = command_name(c, '_');
278
279 const char *ocd_name = alloc_printf("ocd_%s", full_name);
280 Jim_CreateCommand(interp, ocd_name, script_command, c, NULL);
281 free((void *)ocd_name);
282
283 /* we now need to add an overrideable proc */
284 const char *override_name = alloc_printf("proc %s {args} {"
285 "if {[catch {eval ocd_%s $args}] == 0} "
286 "{return \"\"} else {return -code error}}",
287 full_name, full_name);
288 Jim_Eval_Named(interp, override_name, __THIS__FILE__, __LINE__);
289 free((void *)override_name);
290
291 free((void *)full_name);
292
293 return c;
294 }
295
296 int unregister_all_commands(struct command_context *context,
297 struct command *parent)
298 {
299 if (context == NULL)
300 return ERROR_OK;
301
302 struct command **head = command_list_for_parent(context, parent);
303 while (NULL != *head)
304 {
305 struct command *tmp = *head;
306 *head = tmp->next;
307 command_free(tmp);
308 }
309
310 return ERROR_OK;
311 }
312
313 int unregister_command(struct command_context *context,
314 struct command *parent, const char *name)
315 {
316 if ((!context) || (!name))
317 return ERROR_INVALID_ARGUMENTS;
318
319 struct command *p = NULL;
320 struct command **head = command_list_for_parent(context, parent);
321 for (struct command *c = *head; NULL != c; p = c, c = c->next)
322 {
323 if (strcmp(name, c->name) != 0)
324 continue;
325
326 if (p)
327 p->next = c->next;
328 else
329 *head = c->next;
330
331 command_free(c);
332 return ERROR_OK;
333 }
334
335 return ERROR_OK;
336 }
337
338 void command_output_text(struct command_context *context, const char *data)
339 {
340 if (context && context->output_handler && data) {
341 context->output_handler(context, data);
342 }
343 }
344
345 void command_print_sameline(struct command_context *context, const char *format, ...)
346 {
347 char *string;
348
349 va_list ap;
350 va_start(ap, format);
351
352 string = alloc_vprintf(format, ap);
353 if (string != NULL)
354 {
355 /* we want this collected in the log + we also want to pick it up as a tcl return
356 * value.
357 *
358 * The latter bit isn't precisely neat, but will do for now.
359 */
360 LOG_USER_N("%s", string);
361 /* We already printed it above */
362 /* command_output_text(context, string); */
363 free(string);
364 }
365
366 va_end(ap);
367 }
368
369 void command_print(struct command_context *context, const char *format, ...)
370 {
371 char *string;
372
373 va_list ap;
374 va_start(ap, format);
375
376 string = alloc_vprintf(format, ap);
377 if (string != NULL)
378 {
379 strcat(string, "\n"); /* alloc_vprintf guaranteed the buffer to be at least one char longer */
380 /* we want this collected in the log + we also want to pick it up as a tcl return
381 * value.
382 *
383 * The latter bit isn't precisely neat, but will do for now.
384 */
385 LOG_USER_N("%s", string);
386 /* We already printed it above */
387 /* command_output_text(context, string); */
388 free(string);
389 }
390
391 va_end(ap);
392 }
393
394 static char *__command_name(struct command *c, char delim, unsigned extra)
395 {
396 char *name;
397 unsigned len = strlen(c->name);
398 if (NULL == c->parent) {
399 // allocate enough for the name, child names, and '\0'
400 name = malloc(len + extra + 1);
401 strcpy(name, c->name);
402 } else {
403 // parent's extra must include both the space and name
404 name = __command_name(c->parent, delim, 1 + len + extra);
405 char dstr[2] = { delim, 0 };
406 strcat(name, dstr);
407 strcat(name, c->name);
408 }
409 return name;
410 }
411 char *command_name(struct command *c, char delim)
412 {
413 return __command_name(c, delim, 0);
414 }
415
416 static int run_command(struct command_context *context,
417 struct command *c, const char *words[], unsigned num_words)
418 {
419 if (!((context->mode == COMMAND_CONFIG) || (c->mode == COMMAND_ANY) || (c->mode == context->mode)))
420 {
421 /* Config commands can not run after the config stage */
422 LOG_ERROR("Command '%s' only runs during configuration stage", c->name);
423 return ERROR_FAIL;
424 }
425
426 struct command_invocation cmd = {
427 .ctx = context,
428 .name = c->name,
429 .argc = num_words - 1,
430 .argv = words + 1,
431 };
432 int retval = c->handler(&cmd);
433 if (retval == ERROR_COMMAND_SYNTAX_ERROR)
434 {
435 /* Print help for command */
436 char *full_name = command_name(c, ' ');
437 if (NULL != full_name) {
438 command_run_linef(context, "help %s", full_name);
439 free(full_name);
440 } else
441 retval = -ENOMEM;
442 }
443 else if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
444 {
445 /* just fall through for a shutdown request */
446 }
447 else if (retval != ERROR_OK)
448 {
449 /* we do not print out an error message because the command *should*
450 * have printed out an error
451 */
452 LOG_DEBUG("Command failed with error code %d", retval);
453 }
454
455 return retval;
456 }
457
458 int command_run_line(struct command_context *context, char *line)
459 {
460 /* all the parent commands have been registered with the interpreter
461 * so, can just evaluate the line as a script and check for
462 * results
463 */
464 /* run the line thru a script engine */
465 int retval = ERROR_FAIL;
466 int retcode;
467 /* Beware! This code needs to be reentrant. It is also possible
468 * for OpenOCD commands to be invoked directly from Tcl. This would
469 * happen when the Jim Tcl interpreter is provided by eCos for
470 * instance.
471 */
472 Jim_DeleteAssocData(interp, "context");
473 retcode = Jim_SetAssocData(interp, "context", NULL, context);
474 if (retcode == JIM_OK)
475 {
476 /* associated the return value */
477 Jim_DeleteAssocData(interp, "retval");
478 retcode = Jim_SetAssocData(interp, "retval", NULL, &retval);
479 if (retcode == JIM_OK)
480 {
481 retcode = Jim_Eval_Named(interp, line, __THIS__FILE__, __LINE__);
482
483 Jim_DeleteAssocData(interp, "retval");
484 }
485 Jim_DeleteAssocData(interp, "context");
486 }
487 if (retcode == JIM_ERR) {
488 if (retval != ERROR_COMMAND_CLOSE_CONNECTION)
489 {
490 /* We do not print the connection closed error message */
491 Jim_PrintErrorMessage(interp);
492 }
493 if (retval == ERROR_OK)
494 {
495 /* It wasn't a low level OpenOCD command that failed */
496 return ERROR_FAIL;
497 }
498 return retval;
499 } else if (retcode == JIM_EXIT) {
500 /* ignore. */
501 /* exit(Jim_GetExitCode(interp)); */
502 } else {
503 const char *result;
504 int reslen;
505
506 result = Jim_GetString(Jim_GetResult(interp), &reslen);
507 if (reslen > 0)
508 {
509 int i;
510 char buff[256 + 1];
511 for (i = 0; i < reslen; i += 256)
512 {
513 int chunk;
514 chunk = reslen - i;
515 if (chunk > 256)
516 chunk = 256;
517 strncpy(buff, result + i, chunk);
518 buff[chunk] = 0;
519 LOG_USER_N("%s", buff);
520 }
521 LOG_USER_N("%s", "\n");
522 }
523 retval = ERROR_OK;
524 }
525 return retval;
526 }
527
528 int command_run_linef(struct command_context *context, const char *format, ...)
529 {
530 int retval = ERROR_FAIL;
531 char *string;
532 va_list ap;
533 va_start(ap, format);
534 string = alloc_vprintf(format, ap);
535 if (string != NULL)
536 {
537 retval = command_run_line(context, string);
538 }
539 va_end(ap);
540 return retval;
541 }
542
543 void command_set_output_handler(struct command_context* context,
544 command_output_handler_t output_handler, void *priv)
545 {
546 context->output_handler = output_handler;
547 context->output_handler_priv = priv;
548 }
549
550 struct command_context* copy_command_context(struct command_context* context)
551 {
552 struct command_context* copy_context = malloc(sizeof(struct command_context));
553
554 *copy_context = *context;
555
556 return copy_context;
557 }
558
559 int command_done(struct command_context *context)
560 {
561 free(context);
562 context = NULL;
563
564 return ERROR_OK;
565 }
566
567 /* find full path to file */
568 static int jim_find(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
569 {
570 if (argc != 2)
571 return JIM_ERR;
572 const char *file = Jim_GetString(argv[1], NULL);
573 char *full_path = find_file(file);
574 if (full_path == NULL)
575 return JIM_ERR;
576 Jim_Obj *result = Jim_NewStringObj(interp, full_path, strlen(full_path));
577 free(full_path);
578
579 Jim_SetResult(interp, result);
580 return JIM_OK;
581 }
582
583 static int jim_echo(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
584 {
585 if (argc != 2)
586 return JIM_ERR;
587 const char *str = Jim_GetString(argv[1], NULL);
588 LOG_USER("%s", str);
589 return JIM_OK;
590 }
591
592 static size_t openocd_jim_fwrite(const void *_ptr, size_t size, size_t n, void *cookie)
593 {
594 size_t nbytes;
595 const char *ptr;
596 Jim_Interp *interp;
597
598 /* make it a char easier to read code */
599 ptr = _ptr;
600 interp = cookie;
601 nbytes = size * n;
602 if (ptr == NULL || interp == NULL || nbytes == 0) {
603 return 0;
604 }
605
606 /* do we have to chunk it? */
607 if (ptr[nbytes] == 0)
608 {
609 /* no it is a C style string */
610 LOG_USER_N("%s", ptr);
611 return strlen(ptr);
612 }
613 /* GRR we must chunk - not null terminated */
614 while (nbytes) {
615 char chunk[128 + 1];
616 int x;
617
618 x = nbytes;
619 if (x > 128) {
620 x = 128;
621 }
622 /* copy it */
623 memcpy(chunk, ptr, x);
624 /* terminate it */
625 chunk[n] = 0;
626 /* output it */
627 LOG_USER_N("%s", chunk);
628 ptr += x;
629 nbytes -= x;
630 }
631
632 return n;
633 }
634
635 static size_t openocd_jim_fread(void *ptr, size_t size, size_t n, void *cookie)
636 {
637 /* TCL wants to read... tell him no */
638 return 0;
639 }
640
641 static int openocd_jim_vfprintf(void *cookie, const char *fmt, va_list ap)
642 {
643 char *cp;
644 int n;
645 Jim_Interp *interp;
646
647 n = -1;
648 interp = cookie;
649 if (interp == NULL)
650 return n;
651
652 cp = alloc_vprintf(fmt, ap);
653 if (cp)
654 {
655 LOG_USER_N("%s", cp);
656 n = strlen(cp);
657 free(cp);
658 }
659 return n;
660 }
661
662 static int openocd_jim_fflush(void *cookie)
663 {
664 /* nothing to flush */
665 return 0;
666 }
667
668 static char* openocd_jim_fgets(char *s, int size, void *cookie)
669 {
670 /* not supported */
671 errno = ENOTSUP;
672 return NULL;
673 }
674
675 static int jim_capture(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
676 {
677 if (argc != 2)
678 return JIM_ERR;
679 int retcode;
680 const char *str = Jim_GetString(argv[1], NULL);
681
682 /* capture log output and return it */
683 Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0);
684 /* a garbage collect can happen, so we need a reference count to this object */
685 Jim_IncrRefCount(tclOutput);
686
687 log_add_callback(tcl_output, tclOutput);
688
689 retcode = Jim_Eval_Named(interp, str, __THIS__FILE__, __LINE__);
690
691 log_remove_callback(tcl_output, tclOutput);
692
693 /* We dump output into this local variable */
694 Jim_SetResult(interp, tclOutput);
695 Jim_DecrRefCount(interp, tclOutput);
696
697 return retcode;
698 }
699
700 static COMMAND_HELPER(command_help_find, struct command *head,
701 struct command **out)
702 {
703 if (0 == CMD_ARGC)
704 return ERROR_INVALID_ARGUMENTS;
705 *out = command_find(head, CMD_ARGV[0]);
706 if (NULL == *out)
707 return ERROR_INVALID_ARGUMENTS;
708 if (--CMD_ARGC == 0)
709 return ERROR_OK;
710 CMD_ARGV++;
711 return CALL_COMMAND_HANDLER(command_help_find, (*out)->children, out);
712 }
713
714 static COMMAND_HELPER(command_help_show, struct command *c, unsigned n);
715
716 static COMMAND_HELPER(command_help_show_list, struct command *head, unsigned n)
717 {
718 for (struct command *c = head; NULL != c; c = c->next)
719 CALL_COMMAND_HANDLER(command_help_show, c, n);
720 return ERROR_OK;
721 }
722 static COMMAND_HELPER(command_help_show, struct command *c, unsigned n)
723 {
724 command_run_linef(CMD_CTX, "cmd_help {%s} {%s} %d", command_name(c, ' '),
725 c->help ? : "no help available", n);
726
727 if (++n >= 2)
728 return ERROR_OK;
729
730 return CALL_COMMAND_HANDLER(command_help_show_list, c->children, n);
731 }
732 COMMAND_HANDLER(handle_help_command)
733 {
734 struct command *c = CMD_CTX->commands;
735
736 if (0 == CMD_ARGC)
737 return CALL_COMMAND_HANDLER(command_help_show_list, c, 0);
738
739 int retval = CALL_COMMAND_HANDLER(command_help_find, c, &c);
740 if (ERROR_OK != retval)
741 return retval;
742
743 return CALL_COMMAND_HANDLER(command_help_show, c, 0);
744 }
745
746
747 int help_add_command(struct command_context *cmd_ctx, struct command *parent,
748 const char *cmd_name, const char *help_text)
749 {
750 struct command **head = command_list_for_parent(cmd_ctx, parent);
751 struct command *nc = command_find(*head, cmd_name);
752 if (NULL == nc)
753 {
754 // add a new command with help text
755 nc = register_command(cmd_ctx, parent, cmd_name,
756 NULL, COMMAND_ANY, help_text);
757 if (NULL == nc)
758 {
759 LOG_ERROR("failed to add '%s' help text", cmd_name);
760 return ERROR_FAIL;
761 }
762 LOG_DEBUG("added '%s' help text", cmd_name);
763 }
764 else
765 {
766 bool replaced = false;
767 if (nc->help)
768 {
769 free((void *)nc->help);
770 replaced = true;
771 }
772 nc->help = strdup(help_text);
773
774 if (replaced)
775 LOG_INFO("replaced existing '%s' help", cmd_name);
776 else
777 LOG_DEBUG("added '%s' help text", cmd_name);
778 }
779 return ERROR_OK;
780 }
781
782 COMMAND_HANDLER(handle_help_add_command)
783 {
784 if (CMD_ARGC < 2)
785 {
786 LOG_ERROR("%s: insufficient arguments", CMD_NAME);
787 return ERROR_INVALID_ARGUMENTS;
788 }
789
790 // save help text and remove it from argument list
791 const char *help_text = CMD_ARGV[--CMD_ARGC];
792 // likewise for the leaf command name
793 const char *cmd_name = CMD_ARGV[--CMD_ARGC];
794
795 struct command *c = NULL;
796 if (CMD_ARGC > 0)
797 {
798 c = CMD_CTX->commands;
799 int retval = CALL_COMMAND_HANDLER(command_help_find, c, &c);
800 if (ERROR_OK != retval)
801 return retval;
802 }
803 return help_add_command(CMD_CTX, c, cmd_name, help_text);
804 }
805
806 /* sleep command sleeps for <n> miliseconds
807 * this is useful in target startup scripts
808 */
809 COMMAND_HANDLER(handle_sleep_command)
810 {
811 bool busy = false;
812 if (CMD_ARGC == 2)
813 {
814 if (strcmp(CMD_ARGV[1], "busy") == 0)
815 busy = true;
816 else
817 return ERROR_COMMAND_SYNTAX_ERROR;
818 }
819 else if (CMD_ARGC < 1 || CMD_ARGC > 2)
820 return ERROR_COMMAND_SYNTAX_ERROR;
821
822 unsigned long duration = 0;
823 int retval = parse_ulong(CMD_ARGV[0], &duration);
824 if (ERROR_OK != retval)
825 return retval;
826
827 if (!busy)
828 {
829 long long then = timeval_ms();
830 while (timeval_ms() - then < (long long)duration)
831 {
832 target_call_timer_callbacks_now();
833 usleep(1000);
834 }
835 }
836 else
837 busy_sleep(duration);
838
839 return ERROR_OK;
840 }
841
842 struct command_context* command_init(const char *startup_tcl)
843 {
844 struct command_context* context = malloc(sizeof(struct command_context));
845 const char *HostOs;
846
847 context->mode = COMMAND_EXEC;
848 context->commands = NULL;
849 context->current_target = 0;
850 context->output_handler = NULL;
851 context->output_handler_priv = NULL;
852
853 #if !BUILD_ECOSBOARD
854 Jim_InitEmbedded();
855 /* Create an interpreter */
856 interp = Jim_CreateInterp();
857 /* Add all the Jim core commands */
858 Jim_RegisterCoreCommands(interp);
859 #endif
860
861 #if defined(_MSC_VER)
862 /* WinXX - is generic, the forward
863 * looking problem is this:
864 *
865 * "win32" or "win64"
866 *
867 * "winxx" is generic.
868 */
869 HostOs = "winxx";
870 #elif defined(__linux__)
871 HostOs = "linux";
872 #elif defined(__DARWIN__)
873 HostOs = "darwin";
874 #elif defined(__CYGWIN__)
875 HostOs = "cygwin";
876 #elif defined(__MINGW32__)
877 HostOs = "mingw32";
878 #elif defined(__ECOS)
879 HostOs = "ecos";
880 #else
881 #warn unrecognized host OS...
882 HostOs = "other";
883 #endif
884 Jim_SetGlobalVariableStr(interp, "ocd_HOSTOS",
885 Jim_NewStringObj(interp, HostOs , strlen(HostOs)));
886
887 Jim_CreateCommand(interp, "ocd_find", jim_find, NULL, NULL);
888 Jim_CreateCommand(interp, "echo", jim_echo, NULL, NULL);
889 Jim_CreateCommand(interp, "capture", jim_capture, NULL, NULL);
890
891 /* Set Jim's STDIO */
892 interp->cookie_stdin = interp;
893 interp->cookie_stdout = interp;
894 interp->cookie_stderr = interp;
895 interp->cb_fwrite = openocd_jim_fwrite;
896 interp->cb_fread = openocd_jim_fread ;
897 interp->cb_vfprintf = openocd_jim_vfprintf;
898 interp->cb_fflush = openocd_jim_fflush;
899 interp->cb_fgets = openocd_jim_fgets;
900
901 register_command(context, NULL, "add_help_text",
902 handle_help_add_command, COMMAND_ANY,
903 "<command> [...] <help_text>] - "
904 "add new command help text");
905
906 #if !BUILD_ECOSBOARD
907 Jim_EventLoopOnLoad(interp);
908 #endif
909 Jim_SetAssocData(interp, "context", NULL, context);
910 if (Jim_Eval_Named(interp, startup_tcl, "embedded:startup.tcl",1) == JIM_ERR)
911 {
912 LOG_ERROR("Failed to run startup.tcl (embedded into OpenOCD)");
913 Jim_PrintErrorMessage(interp);
914 exit(-1);
915 }
916 Jim_DeleteAssocData(interp, "context");
917
918 register_command(context, NULL, "sleep",
919 handle_sleep_command, COMMAND_ANY,
920 "<n> [busy] - sleep for n milliseconds. "
921 "\"busy\" means busy wait");
922
923 register_command(context, NULL, "help",
924 &handle_help_command, COMMAND_ANY,
925 "[<command_name> ...] - show built-in command help");
926
927 return context;
928 }
929
930 int command_context_mode(struct command_context *cmd_ctx, enum command_mode mode)
931 {
932 if (!cmd_ctx)
933 return ERROR_INVALID_ARGUMENTS;
934
935 cmd_ctx->mode = mode;
936 return ERROR_OK;
937 }
938
939 void process_jim_events(void)
940 {
941 #if !BUILD_ECOSBOARD
942 static int recursion = 0;
943
944 if (!recursion)
945 {
946 recursion++;
947 Jim_ProcessEvents (interp, JIM_ALL_EVENTS | JIM_DONT_WAIT);
948 recursion--;
949 }
950 #endif
951 }
952
953 void register_jim(struct command_context *cmd_ctx, const char *name,
954 Jim_CmdProc cmd, const char *help)
955 {
956 Jim_CreateCommand(interp, name, cmd, NULL, NULL);
957
958 Jim_Obj *cmd_list = Jim_NewListObj(interp, NULL, 0);
959 Jim_ListAppendElement(interp, cmd_list,
960 Jim_NewStringObj(interp, name, -1));
961
962 help_add_command(cmd_ctx, NULL, name, help);
963 }
964
965 #define DEFINE_PARSE_NUM_TYPE(name, type, func, min, max) \
966 int parse##name(const char *str, type *ul) \
967 { \
968 if (!*str) \
969 { \
970 LOG_ERROR("Invalid command argument"); \
971 return ERROR_COMMAND_ARGUMENT_INVALID; \
972 } \
973 char *end; \
974 *ul = func(str, &end, 0); \
975 if (*end) \
976 { \
977 LOG_ERROR("Invalid command argument"); \
978 return ERROR_COMMAND_ARGUMENT_INVALID; \
979 } \
980 if ((max == *ul) && (ERANGE == errno)) \
981 { \
982 LOG_ERROR("Argument overflow"); \
983 return ERROR_COMMAND_ARGUMENT_OVERFLOW; \
984 } \
985 if (min && (min == *ul) && (ERANGE == errno)) \
986 { \
987 LOG_ERROR("Argument underflow"); \
988 return ERROR_COMMAND_ARGUMENT_UNDERFLOW; \
989 } \
990 return ERROR_OK; \
991 }
992 DEFINE_PARSE_NUM_TYPE(_ulong, unsigned long , strtoul, 0, ULONG_MAX)
993 DEFINE_PARSE_NUM_TYPE(_ullong, unsigned long long, strtoull, 0, ULLONG_MAX)
994 DEFINE_PARSE_NUM_TYPE(_long, long , strtol, LONG_MIN, LONG_MAX)
995 DEFINE_PARSE_NUM_TYPE(_llong, long long, strtoll, LLONG_MIN, LLONG_MAX)
996
997 #define DEFINE_PARSE_WRAPPER(name, type, min, max, functype, funcname) \
998 int parse##name(const char *str, type *ul) \
999 { \
1000 functype n; \
1001 int retval = parse##funcname(str, &n); \
1002 if (ERROR_OK != retval) \
1003 return retval; \
1004 if (n > max) \
1005 return ERROR_COMMAND_ARGUMENT_OVERFLOW; \
1006 if (min) \
1007 return ERROR_COMMAND_ARGUMENT_UNDERFLOW; \
1008 *ul = n; \
1009 return ERROR_OK; \
1010 }
1011
1012 #define DEFINE_PARSE_ULONG(name, type, min, max) \
1013 DEFINE_PARSE_WRAPPER(name, type, min, max, unsigned long, _ulong)
1014 DEFINE_PARSE_ULONG(_uint, unsigned, 0, UINT_MAX)
1015 DEFINE_PARSE_ULONG(_u32, uint32_t, 0, UINT32_MAX)
1016 DEFINE_PARSE_ULONG(_u16, uint16_t, 0, UINT16_MAX)
1017 DEFINE_PARSE_ULONG(_u8, uint8_t, 0, UINT8_MAX)
1018
1019 #define DEFINE_PARSE_LONG(name, type, min, max) \
1020 DEFINE_PARSE_WRAPPER(name, type, min, max, long, _long)
1021 DEFINE_PARSE_LONG(_int, int, n < INT_MIN, INT_MAX)
1022 DEFINE_PARSE_LONG(_s32, int32_t, n < INT32_MIN, INT32_MAX)
1023 DEFINE_PARSE_LONG(_s16, int16_t, n < INT16_MIN, INT16_MAX)
1024 DEFINE_PARSE_LONG(_s8, int8_t, n < INT8_MIN, INT8_MAX)
1025
1026 static int command_parse_bool(const char *in, bool *out,
1027 const char *on, const char *off)
1028 {
1029 if (strcasecmp(in, on) == 0)
1030 *out = true;
1031 else if (strcasecmp(in, off) == 0)
1032 *out = false;
1033 else
1034 return ERROR_COMMAND_SYNTAX_ERROR;
1035 return ERROR_OK;
1036 }
1037
1038 int command_parse_bool_arg(const char *in, bool *out)
1039 {
1040 if (command_parse_bool(in, out, "on", "off") == ERROR_OK)
1041 return ERROR_OK;
1042 if (command_parse_bool(in, out, "enable", "disable") == ERROR_OK)
1043 return ERROR_OK;
1044 if (command_parse_bool(in, out, "true", "false") == ERROR_OK)
1045 return ERROR_OK;
1046 if (command_parse_bool(in, out, "yes", "no") == ERROR_OK)
1047 return ERROR_OK;
1048 if (command_parse_bool(in, out, "1", "0") == ERROR_OK)
1049 return ERROR_OK;
1050 return ERROR_INVALID_ARGUMENTS;
1051 }
1052
1053 COMMAND_HELPER(handle_command_parse_bool, bool *out, const char *label)
1054 {
1055 switch (CMD_ARGC) {
1056 case 1: {
1057 const char *in = CMD_ARGV[0];
1058 if (command_parse_bool_arg(in, out) != ERROR_OK)
1059 {
1060 LOG_ERROR("%s: argument '%s' is not valid", CMD_NAME, in);
1061 return ERROR_INVALID_ARGUMENTS;
1062 }
1063 // fall through
1064 }
1065 case 0:
1066 LOG_INFO("%s is %s", label, *out ? "enabled" : "disabled");
1067 break;
1068 default:
1069 return ERROR_INVALID_ARGUMENTS;
1070 }
1071 return ERROR_OK;
1072 }

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)