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

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)