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

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)