log: improve log_callback_fn signature
[fw/openocd] / 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)