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

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)