command.c 23.1 KB
Newer Older
1
2
3
4
/***************************************************************************
 *   Copyright (C) 2005 by Dominic Rath                                    *
 *   Dominic.Rath@gmx.de                                                   *
 *                                                                         *
5
 *   Copyright (C) 2007,2008 yvind Harboe                                 *
6
 *   oyvind.harboe@zylin.com                                               *
7
 *                                                                         *
8
9
 *   Copyright (C) 2008, Duane Ellis                                       *
 *   openocd@duaneeellis.com                                               *
10
 *                                                                         *
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
 *   part of this file is taken from libcli (libcli.sourceforge.net)       *
 *   Copyright (C) David Parrish (david@dparrish.com)                      *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

33
34
35
36
37
#if !BUILD_ECOSBOARD
/* see Embedder-HOWTO.txt in Jim Tcl project hosted on BerliOS*/
#define JIM_EMBEDDED
#endif

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
43
#include "log.h"
#include "time_support.h"
44
#include "jim-eventloop.h"
45

oharboe's avatar
oharboe committed
46

oharboe's avatar
oharboe committed
47
int fast_and_dangerous = 0;
48
Jim_Interp *interp = NULL;
oharboe's avatar
oharboe committed
49

50
int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
oharboe's avatar
oharboe committed
51
int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
52
53
54

int run_command(command_context_t *context, command_t *c, char *words[], int num_words);

55
static void tcl_output(void *privData, const char *file, int line, const char *function, const char *string)
56
{
zwelch's avatar
zwelch committed
57
	Jim_Obj *tclOutput = (Jim_Obj *)privData;
58

59
60
61
	Jim_AppendString(interp, tclOutput, string, strlen(string));
}

62
63
extern command_context_t *global_cmd_ctx;

zwelch's avatar
zwelch committed
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
void script_debug(Jim_Interp *interp, const char *name, int argc, Jim_Obj *const *argv)
{
	int i;

	LOG_DEBUG("command - %s", name);
	for (i = 0; i < argc; i++) {
		int len;
		const char *w = Jim_GetString(argv[i], &len);

		/* end of line comment? */
		if (*w == '#')
			break;

		LOG_DEBUG("%s - argv[%d]=%s", name, i, w);
	}
}

81
82
83
84
85
static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
	/* the private data is stashed in the interp structure */
	command_t *c;
	command_context_t *context;
86
	int retval;
87
88
89
90
	int i;
	int nwords;
	char **words;

91
92
93
94
95
96
	/* DANGER!!!! be careful what we invoke here, since interp->cmdPrivData might
	 * get overwritten by running other Jim commands! Treat it as an
	 * emphemeral global variable that is used in lieu of an argument
	 * to the fn and fish it out manually.
	 */
	c = interp->cmdPrivData;
zwelch's avatar
zwelch committed
97
	if (c == NULL)
98
	{
zwelch's avatar
zwelch committed
99
		LOG_ERROR("BUG: interp->cmdPrivData == NULL");
100
101
		return JIM_ERR;
	}
102
	target_call_timer_callbacks_now();
103
104
	LOG_USER_N("%s", ""); /* Keep GDB connection alive*/

zwelch's avatar
zwelch committed
105
	script_debug(interp, c->name, argc, argv);
106

107
108
	words = malloc(sizeof(char *) * argc);
	for (i = 0; i < argc; i++)
109
110
	{
		int len;
zwelch's avatar
zwelch committed
111
		const char *w = Jim_GetString(argv[i], &len);
112
113
114
115
116
117
		if (*w=='#')
		{
			/* hit an end of line comment */
			break;
		}
		words[i] = strdup(w);
118
		if (words[i] == NULL)
119
120
121
122
		{
			return JIM_ERR;
		}
	}
123
	nwords = i;
124
125
126

	/* grab the command context from the associated data */
	context = Jim_GetAssocData(interp, "context");
127
	if (context == NULL)
128
	{
129
130
131
132
		/* Tcl can invoke commands directly instead of via command_run_line(). This would
		 * happen when the Jim Tcl interpreter is provided by eCos.
		 */
		context = global_cmd_ctx;
133
	}
134

135
136
137
138
	/* capture log output and return it */
	Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0);
	/* a garbage collect can happen, so we need a reference count to this object */
	Jim_IncrRefCount(tclOutput);
139

140
	log_add_callback(tcl_output, tclOutput);
141

142
	retval = run_command(context, c, words, nwords);
143

144
145
146
	log_remove_callback(tcl_output, tclOutput);

	/* We dump output into this local variable */
147
	Jim_SetResult(interp, tclOutput);
148
	Jim_DecrRefCount(interp, tclOutput);
ntfreak's avatar
ntfreak committed
149

150
151
152
	for (i = 0; i < nwords; i++)
		free(words[i]);
	free(words);
153

154
155
156
157
158
	int *return_retval = Jim_GetAssocData(interp, "retval");
	if (return_retval != NULL)
	{
		*return_retval = retval;
	}
159

zwelch's avatar
zwelch committed
160
	return (retval == ERROR_OK)?JIM_OK:JIM_ERR;
161
}
162

oharboe's avatar
oharboe committed
163
164
165
/* nice short description of source file */
#define __THIS__FILE__ "command.c"

166
167
168
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)
{
	command_t *c, *p;
169

170
171
	if (!context || !name)
		return NULL;
172

173
	c = malloc(sizeof(command_t));
174

175
176
177
178
179
	c->name = strdup(name);
	c->parent = parent;
	c->children = NULL;
	c->handler = handler;
	c->mode = mode;
oharboe's avatar
oharboe committed
180
181
	if (!help)
		help="";
182
	c->next = NULL;
183

184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
	/* place command in tree */
	if (parent)
	{
		if (parent->children)
		{
			/* find last child */
			for (p = parent->children; p && p->next; p = p->next);
			if (p)
				p->next = c;
		}
		else
		{
			parent->children = c;
		}
	}
	else
	{
		if (context->commands)
		{
			/* find last command */
			for (p = context->commands; p && p->next; p = p->next);
			if (p)
				p->next = c;
		}
		else
		{
			context->commands = c;
		}
	}
213

214
	/* just a placeholder, no handler */
zwelch's avatar
zwelch committed
215
	if (c->handler == NULL)
216
217
218
219
		return c;

	/* If this is a two level command, e.g. "flash banks", then the
	 * "unknown" proc in startup.tcl must redirect to  this command.
220
	 *
221
222
223
224
225
226
227
228
	 * "flash banks" is translated by "unknown" to "flash_banks"
	 * if such a proc exists
	 */
	/* Print help for command */
	const char *t1="";
	const char *t2="";
	const char *t3="";
	/* maximum of two levels :-) */
zwelch's avatar
zwelch committed
229
	if (c->parent != NULL)
230
	{
zwelch's avatar
zwelch committed
231
		t1 = c->parent->name;
232
233
		t2="_";
	}
zwelch's avatar
zwelch committed
234
235
	t3 = c->name;
	const char *full_name = alloc_printf("ocd_%s%s%s", t1, t2, t3);
236
237
	Jim_CreateCommand(interp, full_name, script_command, c, NULL);
	free((void *)full_name);
238

239
	/* we now need to add an overrideable proc */
zwelch's avatar
zwelch committed
240
	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);
oharboe's avatar
oharboe committed
241
	Jim_Eval_Named(interp, override_name, __THIS__FILE__, __LINE__ );
242
	free((void *)override_name);
243

oharboe's avatar
oharboe committed
244
	/* accumulate help text in Tcl helptext list.  */
zwelch's avatar
zwelch committed
245
	Jim_Obj *helptext = Jim_GetGlobalVariableStr(interp, "ocd_helptext", JIM_ERRMSG);
246
247
	if (Jim_IsShared(helptext))
		helptext = Jim_DuplicateObj(interp, helptext);
zwelch's avatar
zwelch committed
248
	Jim_Obj *cmd_entry = Jim_NewListObj(interp, NULL, 0);
249

zwelch's avatar
zwelch committed
250
	Jim_Obj *cmd_list = Jim_NewListObj(interp, NULL, 0);
oharboe's avatar
oharboe committed
251
252

	/* maximum of two levels :-) */
zwelch's avatar
zwelch committed
253
	if (c->parent != NULL)
oharboe's avatar
oharboe committed
254
255
	{
		Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->parent->name, -1));
256
	}
oharboe's avatar
oharboe committed
257
	Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->name, -1));
258

oharboe's avatar
oharboe committed
259
	Jim_ListAppendElement(interp, cmd_entry, cmd_list);
260
	Jim_ListAppendElement(interp, cmd_entry, Jim_NewStringObj(interp, help, -1));
oharboe's avatar
oharboe committed
261
	Jim_ListAppendElement(interp, helptext, cmd_entry);
262
263
264
	return c;
}

265
266
267
int unregister_all_commands(command_context_t *context)
{
	command_t *c, *c2;
268

269
270
	if (context == NULL)
		return ERROR_OK;
271

zwelch's avatar
zwelch committed
272
	while (NULL != context->commands)
273
274
	{
		c = context->commands;
275

zwelch's avatar
zwelch committed
276
		while (NULL != c->children)
277
278
279
280
281
282
283
284
		{
			c2 = c->children;
			c->children = c->children->next;
			free(c2->name);
			c2->name = NULL;
			free(c2);
			c2 = NULL;
		}
285

286
		context->commands = context->commands->next;
287

288
289
290
		free(c->name);
		c->name = NULL;
		free(c);
291
		c = NULL;
292
	}
293

294
295
296
	return ERROR_OK;
}

297
298
299
int unregister_command(command_context_t *context, char *name)
{
	command_t *c, *p = NULL, *c2;
300

301
302
	if ((!context) || (!name))
		return ERROR_INVALID_ARGUMENTS;
303

304
	/* find command */
305
	c = context->commands;
306

zwelch's avatar
zwelch committed
307
	while (NULL != c)
308
	{
309
310
311
312
313
314
315
316
317
		if (strcmp(name, c->name) == 0)
		{
			/* unlink command */
			if (p)
			{
				p->next = c->next;
			}
			else
			{
318
				/* first element in command list */
319
320
				context->commands = c->next;
			}
321

322
			/* unregister children */
zwelch's avatar
zwelch committed
323
			while (NULL != c->children)
324
			{
325
326
327
328
329
330
				c2 = c->children;
				c->children = c->children->next;
				free(c2->name);
				c2->name = NULL;
				free(c2);
				c2 = NULL;
331
			}
332

333
334
			/* delete command */
			free(c->name);
335
			c->name = NULL;
336
			free(c);
337
338
			c = NULL;
			return ERROR_OK;
339
		}
340

341
342
		/* remember the last command for unlinking */
		p = c;
343
		c = c->next;
344
	}
345

346
347
348
	return ERROR_OK;
}

ntfreak's avatar
ntfreak committed
349
void command_output_text(command_context_t *context, const char *data)
oharboe's avatar
oharboe committed
350
{
zwelch's avatar
zwelch committed
351
	if ( context && context->output_handler && data  ){
oharboe's avatar
oharboe committed
352
353
354
355
		context->output_handler( context, data );
	}
}

zwelch's avatar
zwelch committed
356
void command_print_sameline(command_context_t *context, const char *format, ...)
357
{
358
	char *string;
359

360
361
362
	va_list ap;
	va_start(ap, format);

oharboe's avatar
oharboe committed
363
	string = alloc_vprintf(format, ap);
364
	if (string != NULL)
365
	{
366
367
		/* we want this collected in the log + we also want to pick it up as a tcl return
		 * value.
368
		 *
369
370
371
		 * The latter bit isn't precisely neat, but will do for now.
		 */
		LOG_USER_N("%s", string);
372
373
		/* We already printed it above */
		/* command_output_text(context, string); */
374
		free(string);
375
376
	}

377
378
	va_end(ap);
}
379

zwelch's avatar
zwelch committed
380
void command_print(command_context_t *context, const char *format, ...)
381
{
382
383
	char *string;

384
385
	va_list ap;
	va_start(ap, format);
386

oharboe's avatar
oharboe committed
387
	string = alloc_vprintf(format, ap);
388
389
	if (string != NULL)
	{
oharboe's avatar
oharboe committed
390
		strcat(string, "\n"); /* alloc_vprintf guaranteed the buffer to be at least one char longer */
391
392
		/* we want this collected in the log + we also want to pick it up as a tcl return
		 * value.
393
		 *
394
395
396
		 * The latter bit isn't precisely neat, but will do for now.
		 */
		LOG_USER_N("%s", string);
397
398
		/* We already printed it above */
		/* command_output_text(context, string); */
399
400
401
		free(string);
	}

402
	va_end(ap);
403
404
}

405
int run_command(command_context_t *context, command_t *c, char *words[], int num_words)
406
{
zwelch's avatar
zwelch committed
407
	int start_word = 0;
408
	if (!((context->mode == COMMAND_CONFIG) || (c->mode == COMMAND_ANY) || (c->mode == context->mode) ))
409
	{
410
		/* Config commands can not run after the config stage */
zwelch's avatar
zwelch committed
411
		LOG_ERROR("Command '%s' only runs during configuration stage", c->name);
412
		return ERROR_FAIL;
413
	}
414

415
416
417
	int retval = c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1);
	if (retval == ERROR_COMMAND_SYNTAX_ERROR)
	{
418
419
420
421
422
		/* Print help for command */
		const char *t1="";
		const char *t2="";
		const char *t3="";
		/* maximum of two levels :-) */
zwelch's avatar
zwelch committed
423
		if (c->parent != NULL)
424
		{
zwelch's avatar
zwelch committed
425
			t1 = c->parent->name;
426
427
			t2=" ";
		}
zwelch's avatar
zwelch committed
428
		t3 = c->name;
429
		command_run_linef(context, "help {%s%s%s}", t1, t2, t3);
430
431
432
433
434
435
436
437
438
439
	}
	else if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
	{
		/* just fall through for a shutdown request */
	}
	else if (retval != ERROR_OK)
	{
		/* we do not print out an error message because the command *should*
		 * have printed out an error
		 */
440
		LOG_DEBUG("Command failed with error code %d", retval);
441
	}
442
443

	return retval;
444
445
}

446
int command_run_line(command_context_t *context, char *line)
447
{
448
449
450
451
452
	/* all the parent commands have been registered with the interpreter
	 * so, can just evaluate the line as a script and check for
	 * results
	 */
	/* run the line thru a script engine */
zwelch's avatar
zwelch committed
453
	int retval = ERROR_FAIL;
454
	int retcode;
455
456
457
458
459
460
	/* Beware! This code needs to be reentrant. It is also possible
	 * for OpenOCD commands to be invoked directly from Tcl. This would
	 * happen when the Jim Tcl interpreter is provided by eCos for
	 * instance.
	 */
	Jim_DeleteAssocData(interp, "context");
461
	retcode = Jim_SetAssocData(interp, "context", NULL, context);
462
463
464
465
466
467
468
	if (retcode == JIM_OK)
	{
		/* associated the return value */
		Jim_DeleteAssocData(interp, "retval");
		retcode = Jim_SetAssocData(interp, "retval", NULL, &retval);
		if (retcode == JIM_OK)
		{
oharboe's avatar
oharboe committed
469
			retcode = Jim_Eval_Named(interp, line, __THIS__FILE__, __LINE__ );
470

471
			Jim_DeleteAssocData(interp, "retval");
472
		}
473
474
		Jim_DeleteAssocData(interp, "context");
	}
475
	if (retcode == JIM_ERR) {
zwelch's avatar
zwelch committed
476
		if (retval != ERROR_COMMAND_CLOSE_CONNECTION)
477
478
479
480
		{
			/* We do not print the connection closed error message */
			Jim_PrintErrorMessage(interp);
		}
zwelch's avatar
zwelch committed
481
		if (retval == ERROR_OK)
482
483
		{
			/* It wasn't a low level OpenOCD command that failed */
484
			return ERROR_FAIL;
485
486
		}
		return retval;
487
488
489
490
491
492
493
494
	} else if (retcode == JIM_EXIT) {
		/* ignore. */
		/* exit(Jim_GetExitCode(interp)); */
	} else {
		const char *result;
		int reslen;

		result = Jim_GetString(Jim_GetResult(interp), &reslen);
oharboe's avatar
oharboe committed
495
		if (reslen>0)
496
		{
oharboe's avatar
oharboe committed
497
			int i;
zwelch's avatar
zwelch committed
498
			char buff[256 + 1];
oharboe's avatar
oharboe committed
499
500
501
502
503
504
			for (i = 0; i < reslen; i += 256)
			{
				int chunk;
				chunk = reslen - i;
				if (chunk > 256)
					chunk = 256;
zwelch's avatar
zwelch committed
505
				strncpy(buff, result + i, chunk);
oharboe's avatar
oharboe committed
506
507
508
509
				buff[chunk] = 0;
				LOG_USER_N("%s", buff);
			}
			LOG_USER_N("%s", "\n");
510
		}
zwelch's avatar
zwelch committed
511
		retval = ERROR_OK;
ntfreak's avatar
ntfreak committed
512
	}
513
514
515
	return retval;
}

zwelch's avatar
zwelch committed
516
int command_run_linef(command_context_t *context, const char *format, ...)
oharboe's avatar
oharboe committed
517
{
zwelch's avatar
zwelch committed
518
	int retval = ERROR_FAIL;
oharboe's avatar
oharboe committed
519
520
521
522
	char *string;
	va_list ap;
	va_start(ap, format);
	string = alloc_vprintf(format, ap);
zwelch's avatar
zwelch committed
523
	if (string != NULL)
oharboe's avatar
oharboe committed
524
	{
zwelch's avatar
zwelch committed
525
		retval = command_run_line(context, string);
oharboe's avatar
oharboe committed
526
527
528
529
530
	}
	va_end(ap);
	return retval;
}

oharboe's avatar
oharboe committed
531
void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, const char* line), void *priv)
532
533
534
535
536
537
538
539
540
541
{
	context->output_handler = output_handler;
	context->output_handler_priv = priv;
}

command_context_t* copy_command_context(command_context_t* context)
{
	command_context_t* copy_context = malloc(sizeof(command_context_t));

	*copy_context = *context;
542

543
544
545
546
547
548
549
	return copy_context;
}

int command_done(command_context_t *context)
{
	free(context);
	context = NULL;
550

551
552
553
	return ERROR_OK;
}

554
555
556
557
558
559
560
561
562
563
564
/* find full path to file */
static int jim_find(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
	if (argc != 2)
		return JIM_ERR;
	const char *file = Jim_GetString(argv[1], NULL);
	char *full_path = find_file(file);
	if (full_path == NULL)
		return JIM_ERR;
	Jim_Obj *result = Jim_NewStringObj(interp, full_path, strlen(full_path));
	free(full_path);
565

566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
	Jim_SetResult(interp, result);
	return JIM_OK;
}

static int jim_echo(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
	if (argc != 2)
		return JIM_ERR;
	const char *str = Jim_GetString(argv[1], NULL);
	LOG_USER("%s", str);
	return JIM_OK;
}

static size_t openocd_jim_fwrite(const void *_ptr, size_t size, size_t n, void *cookie)
{
	size_t nbytes;
	const char *ptr;
	Jim_Interp *interp;

	/* make it a char easier to read code */
	ptr = _ptr;
	interp = cookie;
	nbytes = size * n;
	if (ptr == NULL || interp == NULL || nbytes == 0) {
		return 0;
	}

	/* do we have to chunk it? */
	if (ptr[nbytes] == 0)
	{
		/* no it is a C style string */
597
		LOG_USER_N("%s", ptr);
598
599
600
601
		return strlen(ptr);
	}
	/* GRR we must chunk - not null terminated */
	while (nbytes) {
zwelch's avatar
zwelch committed
602
		char chunk[128 + 1];
603
604
605
606
607
608
609
610
611
612
613
		int x;

		x = nbytes;
		if (x > 128) {
			x = 128;
		}
		/* copy it */
		memcpy(chunk, ptr, x);
		/* terminate it */
		chunk[n] = 0;
		/* output it */
614
		LOG_USER_N("%s", chunk);
615
616
617
		ptr += x;
		nbytes -= x;
	}
618

619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
	return n;
}

static size_t openocd_jim_fread(void *ptr, size_t size, size_t n, void *cookie)
{
	/* TCL wants to read... tell him no */
	return 0;
}

static int openocd_jim_vfprintf(void *cookie, const char *fmt, va_list ap)
{
	char *cp;
	int n;
	Jim_Interp *interp;

	n = -1;
	interp = cookie;
	if (interp == NULL)
		return n;

	cp = alloc_vprintf(fmt, ap);
	if (cp)
	{
642
		LOG_USER_N("%s", cp);
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
		n = strlen(cp);
		free(cp);
	}
	return n;
}

static int openocd_jim_fflush(void *cookie)
{
	/* nothing to flush */
	return 0;
}

static char* openocd_jim_fgets(char *s, int size, void *cookie)
{
	/* not supported */
	errno = ENOTSUP;
	return NULL;
}

662
663
664
665
666
667
668
669
670
671
672
673
674
675
static int jim_capture(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
	if (argc != 2)
		return JIM_ERR;
	int retcode;
	const char *str = Jim_GetString(argv[1], NULL);

	/* capture log output and return it */
	Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0);
	/* a garbage collect can happen, so we need a reference count to this object */
	Jim_IncrRefCount(tclOutput);

	log_add_callback(tcl_output, tclOutput);

oharboe's avatar
oharboe committed
676
	retcode = Jim_Eval_Named(interp, str, __THIS__FILE__, __LINE__ );
677
678
679
680
681
682
683
684
685
686

	log_remove_callback(tcl_output, tclOutput);

	/* We dump output into this local variable */
	Jim_SetResult(interp, tclOutput);
	Jim_DecrRefCount(interp, tclOutput);

	return retcode;
}

687
688
689
command_context_t* command_init()
{
	command_context_t* context = malloc(sizeof(command_context_t));
690
	extern const char startup_tcl[];
duane's avatar
duane committed
691
	const char *HostOs;
692

693
694
695
696
697
	context->mode = COMMAND_EXEC;
	context->commands = NULL;
	context->current_target = 0;
	context->output_handler = NULL;
	context->output_handler_priv = NULL;
698

699
#if !BUILD_ECOSBOARD
700
701
702
703
704
705
706
	Jim_InitEmbedded();
	/* Create an interpreter */
	interp = Jim_CreateInterp();
	/* Add all the Jim core commands */
	Jim_RegisterCoreCommands(interp);
#endif

duane's avatar
duane committed
707
708
709
#if defined( _MSC_VER )
	/* WinXX - is generic, the forward
	 * looking problem is this:
oharboe's avatar
oharboe committed
710
	 *
duane's avatar
duane committed
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
	 *   "win32" or "win64"
	 *
	 * "winxx" is generic.
	 */
	HostOs = "winxx";
#elif defined( __LINUX__)
	HostOs = "linux";
#elif defined( __DARWIN__ )
	HostOs = "darwin";
#elif defined( __CYGWIN__ )
	HostOs = "cygwin";
#elif defined( __MINGW32__ )
	HostOs = "mingw32";
#else
	HostOs = "other";
#endif
	Jim_SetGlobalVariableStr( interp, "ocd_HOSTOS", Jim_NewStringObj( interp, HostOs , strlen(HostOs)) );

729
	Jim_CreateCommand(interp, "ocd_find", jim_find, NULL, NULL);
730
	Jim_CreateCommand(interp, "echo", jim_echo, NULL, NULL);
731
	Jim_CreateCommand(interp, "capture", jim_capture, NULL, NULL);
732
733
734
735
736
737
738
739
740
741

	/* Set Jim's STDIO */
	interp->cookie_stdin = interp;
	interp->cookie_stdout = interp;
	interp->cookie_stderr = interp;
	interp->cb_fwrite = openocd_jim_fwrite;
	interp->cb_fread = openocd_jim_fread ;
	interp->cb_vfprintf = openocd_jim_vfprintf;
	interp->cb_fflush = openocd_jim_fflush;
	interp->cb_fgets = openocd_jim_fgets;
742

743
744
	add_default_dirs();

745
#if !BUILD_ECOSBOARD
746
	Jim_EventLoopOnLoad(interp);
747
#endif
zwelch's avatar
zwelch committed
748
	if (Jim_Eval_Named(interp, startup_tcl, "embedded:startup.tcl",1) == JIM_ERR)
749
750
751
752
753
754
	{
		LOG_ERROR("Failed to run startup.tcl (embedded into OpenOCD compile time)");
		Jim_PrintErrorMessage(interp);
		exit(-1);
	}

755
	register_command(context, NULL, "sleep", handle_sleep_command,
756
757
					 COMMAND_ANY, "<n> [busy] - sleep for n milliseconds. \"busy\" means busy wait");

oharboe's avatar
oharboe committed
758
759
	register_command(context, NULL, "fast", handle_fast_command,
					 COMMAND_ANY, "fast <enable/disable> - place at beginning of config files. Sets defaults to fast and dangerous.");
760

761
762
763
	return context;
}

764
765
766
767
768
769
770
771
772
int command_context_mode(command_context_t *cmd_ctx, enum command_mode mode)
{
	if (!cmd_ctx)
		return ERROR_INVALID_ARGUMENTS;

	cmd_ctx->mode = mode;
	return ERROR_OK;
}

773
774
775
/* sleep command sleeps for <n> miliseconds
 * this is useful in target startup scripts
 */
zwelch's avatar
zwelch committed
776
777
int handle_sleep_command(struct command_context_s *cmd_ctx,
		char *cmd, char **args, int argc)
778
{
zwelch's avatar
zwelch committed
779
780
	bool busy = false;
	if (argc == 2)
781
	{
zwelch's avatar
zwelch committed
782
783
784
		if (strcmp(args[1], "busy") == 0)
			busy = true;
		else
785
786
			return ERROR_COMMAND_SYNTAX_ERROR;
	}
zwelch's avatar
zwelch committed
787
788
	else if (argc < 1 || argc > 2)
		return ERROR_COMMAND_SYNTAX_ERROR;
789

zwelch's avatar
zwelch committed
790
791
792
793
	unsigned long duration = 0;
	int retval = parse_ulong(args[0], &duration);
	if (ERROR_OK != retval)
		return retval;
794

zwelch's avatar
zwelch committed
795
	if (!busy)
796
	{
zwelch's avatar
zwelch committed
797
798
		long long then = timeval_ms();
		while (timeval_ms() - then < (long long)duration)
799
800
801
802
		{
			target_call_timer_callbacks_now();
			usleep(1000);
		}
803
	}
zwelch's avatar
zwelch committed
804
805
	else
		busy_sleep(duration);
806
807
808
809

	return ERROR_OK;
}

oharboe's avatar
oharboe committed
810
811
int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
{
zwelch's avatar
zwelch committed
812
	if (argc != 1)
oharboe's avatar
oharboe committed
813
		return ERROR_COMMAND_SYNTAX_ERROR;
814

zwelch's avatar
zwelch committed
815
	fast_and_dangerous = strcmp("enable", args[0]) == 0;
816

oharboe's avatar
oharboe committed
817
818
	return ERROR_OK;
}
819

820
void process_jim_events(void)
821
{
822
#if !BUILD_ECOSBOARD
823
824
	static int recursion = 0;

825
	if (!recursion)
826
827
	{
		recursion++;
zwelch's avatar
zwelch committed
828
		Jim_ProcessEvents (interp, JIM_ALL_EVENTS | JIM_DONT_WAIT);
829
830
		recursion--;
	}
831
#endif
832
833
}

834
835
836
837
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)
{
	Jim_CreateCommand(interp, name, cmd, NULL, NULL);

838
	/* FIX!!! it would be prettier to invoke add_help_text...
839
	 * accumulate help text in Tcl helptext list.  */
zwelch's avatar
zwelch committed
840
	Jim_Obj *helptext = Jim_GetGlobalVariableStr(interp, "ocd_helptext", JIM_ERRMSG);
841
842
	if (Jim_IsShared(helptext))
		helptext = Jim_DuplicateObj(interp, helptext);
843

zwelch's avatar
zwelch committed
844
	Jim_Obj *cmd_entry = Jim_NewListObj(interp, NULL, 0);
845

zwelch's avatar
zwelch committed
846
	Jim_Obj *cmd_list = Jim_NewListObj(interp, NULL, 0);
847
	Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, name, -1));
848

849
850
851
852
	Jim_ListAppendElement(interp, cmd_entry, cmd_list);
	Jim_ListAppendElement(interp, cmd_entry, Jim_NewStringObj(interp, help, -1));
	Jim_ListAppendElement(interp, helptext, cmd_entry);
}
853
854
855
856

/* return global variable long value or 0 upon failure */
long jim_global_long(const char *variable)
{
zwelch's avatar
zwelch committed
857
	Jim_Obj *objPtr = Jim_GetGlobalVariableStr(interp, variable, JIM_ERRMSG);
858
	long t;
zwelch's avatar
zwelch committed
859
	if (Jim_GetLong(interp, objPtr, &t) == JIM_OK)
860
861
862
863
864
	{
		return t;
	}
	return 0;
}
865

866
#define DEFINE_PARSE_NUM_TYPE(name, type, func, min, max) \
867
868
	int parse##name(const char *str, type *ul) \
	{ \
869
		if (!*str) \
870
			return ERROR_COMMAND_ARGUMENT_INVALID; \
871
872
		char *end; \
		*ul = func(str, &end, 0); \
873
		if (*end) \
874
			return ERROR_COMMAND_ARGUMENT_INVALID; \
875
		if ((max == *ul) && (ERANGE == errno)) \
876
			return ERROR_COMMAND_ARGUMENT_OVERFLOW; \
877
		if (min && (min == *ul) && (ERANGE == errno)) \
878
			return ERROR_COMMAND_ARGUMENT_UNDERFLOW; \
879
		return ERROR_OK; \
880
	}
881
882
883
884
DEFINE_PARSE_NUM_TYPE(_ulong, unsigned long , strtoul, 0, ULONG_MAX)
DEFINE_PARSE_NUM_TYPE(_ullong, unsigned long long, strtoull, 0, ULLONG_MAX)
DEFINE_PARSE_NUM_TYPE(_long, long , strtol, LONG_MIN, LONG_MAX)
DEFINE_PARSE_NUM_TYPE(_llong, long long, strtoll, LLONG_MIN, LLONG_MAX)
885
886

#define DEFINE_PARSE_WRAPPER(name, type, min, max, functype, funcname) \
887
	int parse##name(const char *str, type *ul) \
888
889
890
891
892
893
	{ \
		functype n; \
		int retval = parse##funcname(str, &n); \
		if (ERROR_OK != retval) \
			return retval; \
		if (n > max) \
894
			return ERROR_COMMAND_ARGUMENT_OVERFLOW; \
895
		if (min) \
896
			return ERROR_COMMAND_ARGUMENT_UNDERFLOW; \
897
898
899
900
901
902
		*ul = n; \
		return ERROR_OK; \
	}	

#define DEFINE_PARSE_ULONG(name, type, min, max) \
	DEFINE_PARSE_WRAPPER(name, type, min, max, unsigned long, _ulong)
903
904
905
906
DEFINE_PARSE_ULONG(_uint, unsigned, 0, UINT_MAX)
DEFINE_PARSE_ULONG(_u32, uint32_t, 0, UINT32_MAX)
DEFINE_PARSE_ULONG(_u16, uint16_t, 0, UINT16_MAX)
DEFINE_PARSE_ULONG(_u8, uint8_t, 0, UINT8_MAX)
907
908
909

#define DEFINE_PARSE_LONG(name, type, min, max) \
	DEFINE_PARSE_WRAPPER(name, type, min, max, long, _long)
910
911
912
913
DEFINE_PARSE_LONG(_int, int, n < INT_MIN, INT_MAX)
DEFINE_PARSE_LONG(_s32, int32_t, n < INT32_MIN, INT32_MAX)
DEFINE_PARSE_LONG(_s16, int16_t, n < INT16_MIN, INT16_MAX)
DEFINE_PARSE_LONG(_s8, int8_t, n < INT8_MIN, INT8_MAX)