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