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

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)