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

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)