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