log: improve log_callback_fn signature
[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, 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 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 #elif defined(__ECOS)
774 HostOs = "ecos";
775 #else
776 #warn unrecognized host OS...
777 HostOs = "other";
778 #endif
779 Jim_SetGlobalVariableStr(interp, "ocd_HOSTOS",
780 Jim_NewStringObj(interp, HostOs , strlen(HostOs)));
781
782 Jim_CreateCommand(interp, "ocd_find", jim_find, NULL, NULL);
783 Jim_CreateCommand(interp, "echo", jim_echo, NULL, NULL);
784 Jim_CreateCommand(interp, "capture", jim_capture, NULL, NULL);
785
786 /* Set Jim's STDIO */
787 interp->cookie_stdin = interp;
788 interp->cookie_stdout = interp;
789 interp->cookie_stderr = interp;
790 interp->cb_fwrite = openocd_jim_fwrite;
791 interp->cb_fread = openocd_jim_fread ;
792 interp->cb_vfprintf = openocd_jim_vfprintf;
793 interp->cb_fflush = openocd_jim_fflush;
794 interp->cb_fgets = openocd_jim_fgets;
795
796 #if !BUILD_ECOSBOARD
797 Jim_EventLoopOnLoad(interp);
798 #endif
799 if (Jim_Eval_Named(interp, startup_tcl, "embedded:startup.tcl",1) == JIM_ERR)
800 {
801 LOG_ERROR("Failed to run startup.tcl (embedded into OpenOCD)");
802 Jim_PrintErrorMessage(interp);
803 exit(-1);
804 }
805
806 register_command(context, NULL, "sleep",
807 handle_sleep_command, COMMAND_ANY,
808 "<n> [busy] - sleep for n milliseconds. "
809 "\"busy\" means busy wait");
810 register_command(context, NULL, "fast",
811 handle_fast_command, COMMAND_ANY,
812 "fast <enable/disable> - place at beginning of "
813 "config files. Sets defaults to fast and dangerous.");
814
815 return context;
816 }
817
818 int command_context_mode(command_context_t *cmd_ctx, enum command_mode mode)
819 {
820 if (!cmd_ctx)
821 return ERROR_INVALID_ARGUMENTS;
822
823 cmd_ctx->mode = mode;
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)