shell.c 12.9 KB
Newer Older
1 2 3 4 5 6 7
/*
 * File      : shell.c
 * This file is part of RT-Thread RTOS
 * COPYRIGHT (C) 2006, RT-Thread Development Team
 *
 * The license and distribution terms for this file may be
 * found in the file LICENSE in this distribution or at
8
 * http://www.rt-thread.org/license/LICENSE
9 10 11 12 13 14 15
 *
 * Change Logs:
 * Date           Author       Notes
 * 2006-04-30     Bernard      the first verion for FinSH
 * 2006-05-08     Bernard      change finsh thread stack to 2048
 * 2006-06-03     Bernard      add support for skyeye
 * 2006-09-24     Bernard      remove the code related with hardware
B
bernard.xiong 已提交
16
 * 2010-01-18     Bernard      fix down then up key bug.
17
 * 2010-03-19     Bernard      fix backspace issue and fix device read in shell.
18
 * 2010-04-01     Bernard      add prompt output when start and remove the empty history
19 20
 * 2011-02-23     Bernard      fix variable section end issue of finsh shell
 *                             initialization when use GNU GCC compiler.
21 22 23 24 25 26
 */

#include <rtthread.h>
#include <rthw.h>

#include "finsh.h"
27
#include "shell.h"
28 29

/* finsh thread */
30
static struct rt_thread finsh_thread;
31
ALIGN(RT_ALIGN_SIZE)
32 33
static char finsh_thread_stack[FINSH_THREAD_STACK_SIZE];
struct finsh_shell* shell;
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54

#if !defined (RT_USING_NEWLIB) && !defined (RT_USING_MINILIBC)
int strcmp (const char *s1, const char *s2)
{
	while (*s1 && *s1 == *s2) s1++, s2++;

	return (*s1 - *s2);
}

#ifdef RT_USING_HEAP
char *strdup(const char *s)
{
	size_t len = strlen(s) + 1;
	char *tmp = (char *)rt_malloc(len);

	if(!tmp) return NULL;

	rt_memcpy(tmp, s, len);
	return tmp;
}
#endif
B
bernard.xiong 已提交
55

56
#if !defined(__CC_ARM) && !defined(__IAR_SYSTEMS_ICC__) && !defined(__ADSPBLACKFIN__) && !defined(_MSC_VER)
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
int isalpha( int ch )
{
	return (unsigned int)((ch | 0x20) - 'a') < 26u;
}

int atoi(const char* s)
{
	long int v=0;
	int sign=1;
	while ( *s == ' '  ||  (unsigned int)(*s - 9) < 5u) s++;

	switch (*s)
	{
	case '-': sign=-1;
	case '+': ++s;
	}

	while ((unsigned int) (*s - '0') < 10u)
	{
		v=v*10+*s-'0'; ++s;
	}

	return sign==-1?-v:v;
}

int isprint(unsigned char ch)
{
    return (unsigned int)(ch - ' ') < 127u - ' ';
B
bernard.xiong 已提交
85
}
B
bernard.xiong 已提交
86
#endif
87 88
#endif

89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
#if defined(RT_USING_DFS) && defined(DFS_USING_WORKDIR)
#include <dfs_posix.h>
const char* finsh_get_prompt()
{
	#define _PROMPT "finsh "
	static char finsh_prompt[RT_CONSOLEBUF_SIZE + 1] = {_PROMPT};
	
	/* get current working directory */
	getcwd(&finsh_prompt[6], RT_CONSOLEBUF_SIZE - 8);
	strcat(finsh_prompt, ">");

	return finsh_prompt;
}
#endif

104 105
static rt_err_t finsh_rx_ind(rt_device_t dev, rt_size_t size)
{
106 107
	RT_ASSERT(shell != RT_NULL);

108
	/* release semaphore to let finsh thread rx data */
109
	rt_sem_release(&shell->rx_sem);
110 111 112 113

	return RT_EOK;
}

B
bernard.xiong@gmail.com 已提交
114 115 116 117 118 119 120
/**
 * @ingroup finsh
 *
 * This function sets the input device of finsh shell.
 *
 * @param device_name the name of new input device.
 */
121
void finsh_set_device(const char* device_name)
122 123 124
{
	rt_device_t dev = RT_NULL;

125
	RT_ASSERT(shell != RT_NULL);
126 127 128
	dev = rt_device_find(device_name);
	if (dev != RT_NULL && rt_device_open(dev, RT_DEVICE_OFLAG_RDWR) == RT_EOK)
	{
129
		if (shell->device != RT_NULL)
130 131
		{
			/* close old finsh device */
132
			rt_device_close(shell->device);
133 134
		}

135
		shell->device = dev;
136 137 138 139 140 141 142 143
		rt_device_set_rx_indicate(dev, finsh_rx_ind);
	}
	else
	{
		rt_kprintf("finsh: can not find device:%s\n", device_name);
	}
}

B
bernard.xiong@gmail.com 已提交
144 145 146 147 148 149 150
/**
 * @ingroup finsh
 *
 * This function returns current finsh shell input device.
 *
 * @return the finsh shell input device name is returned.
 */
151 152 153 154 155 156
const char* finsh_get_device()
{
	RT_ASSERT(shell != RT_NULL);
	return shell->device->parent.name;
}

B
bernard.xiong@gmail.com 已提交
157 158 159 160 161 162 163 164 165 166
/**
 * @ingroup finsh
 *
 * This function set the echo mode of finsh shell.
 *
 * FINSH_OPTION_ECHO=0x01 is echo mode, other values are none-echo mode.
 *
 * @param echo the echo mode
 */
void finsh_set_echo(rt_uint32_t echo)
167 168
{
	RT_ASSERT(shell != RT_NULL);
B
bernard.xiong@gmail.com 已提交
169
	shell->echo_mode = echo;
170 171
}

B
bernard.xiong@gmail.com 已提交
172 173 174 175 176 177 178
/**
 * @ingroup finsh
 *
 * This function gets the echo mode of finsh shell.
 *
 * @return the echo mode
 */
179 180 181 182 183 184 185
rt_uint32_t finsh_get_echo()
{
	RT_ASSERT(shell != RT_NULL);

	return shell->echo_mode;
}

186 187 188 189 190 191
void finsh_auto_complete(char* prefix)
{
	extern void list_prefix(char* prefix);

	rt_kprintf("\n");
	list_prefix(prefix);
192
	rt_kprintf("%s%s", FINSH_PROMPT, prefix);
193
}
194

195
void finsh_run_line(struct finsh_parser* parser, const char *line)
196
{
B
bernard.xiong 已提交
197 198
	const char* err_str;

199 200 201 202 203 204 205 206 207 208
	rt_kprintf("\n");
	finsh_parser_run(parser, (unsigned char*)line);

	/* compile node root */
	if (finsh_errno() == 0)
	{
		finsh_compiler_run(parser->root);
	}
	else
	{
B
bernard.xiong 已提交
209 210
		err_str = finsh_error_string(finsh_errno());
		rt_kprintf("%s\n", err_str);
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
	}

	/* run virtual machine */
	if (finsh_errno() == 0)
	{
		char ch;
		finsh_vm_run();

		ch = (unsigned char)finsh_stack_bottom();
		if (ch > 0x20 && ch < 0x7e)
		{
			rt_kprintf("\t'%c', %d, 0x%08x\n",
				(unsigned char)finsh_stack_bottom(),
				(unsigned int)finsh_stack_bottom(),
				(unsigned int)finsh_stack_bottom());
		}
		else
		{
			rt_kprintf("\t%d, 0x%08x\n",
				(unsigned int)finsh_stack_bottom(),
				(unsigned int)finsh_stack_bottom());
		}
	}
234

235 236 237
    finsh_flush(parser);
}

238
#ifdef FINSH_USING_HISTORY
239 240 241 242 243 244 245 246 247 248 249 250
rt_bool_t finsh_handle_history(struct finsh_shell* shell, char ch)
{
	/*
	 * handle up and down key
	 * up key  : 0x1b 0x5b 0x41
	 * down key: 0x1b 0x5b 0x42
	 */
	if (ch == 0x1b)
	{
		shell->stat = WAIT_SPEC_KEY;
		return RT_TRUE;
	}
251

252 253 254 255 256 257 258
	if ((shell->stat == WAIT_SPEC_KEY))
	{
		if (ch == 0x5b)
		{
			shell->stat = WAIT_FUNC_KEY;
			return RT_TRUE;
		}
259

260 261 262
		shell->stat = WAIT_NORMAL;
		return RT_FALSE;
	}
263

264
	if (shell->stat == WAIT_FUNC_KEY)
265
	{
266
		shell->stat = WAIT_NORMAL;
267

268
		if (ch == 0x41) /* up key */
269
		{
270 271 272
			/* prev history */
			if (shell->current_history > 0)shell->current_history --;
			else
273
			{
274 275
				shell->current_history = 0;
				return RT_TRUE;
276
			}
277

278 279 280 281 282 283 284 285 286 287 288 289
			/* copy the history command */
			memcpy(shell->line, &shell->cmd_history[shell->current_history][0],
				FINSH_CMD_SIZE);
			shell->line_position = strlen(shell->line);
			shell->use_history = 1;
		}
		else if (ch == 0x42) /* down key */
		{
			/* next history */
			if (shell->current_history < shell->history_count - 1)
				shell->current_history ++;
			else
290
			{
291 292
				/* set to the end of history */
				if (shell->history_count != 0)
293
				{
294
					shell->current_history = shell->history_count - 1;
295
				}
296
				else return RT_TRUE;
297
			}
298

299 300 301 302 303
			memcpy(shell->line, &shell->cmd_history[shell->current_history][0],
				FINSH_CMD_SIZE);
			shell->line_position = strlen(shell->line);
			shell->use_history = 1;
		}
B
bernard.xiong 已提交
304

305 306
		if (shell->use_history)
		{
307 308 309 310 311 312 313 314 315
#if defined(_WIN32)
			int i;
			rt_kprintf("\r");

			for(i=0; i<= 60; i++)
				putchar(' ');
			rt_kprintf("\r");

#else
316
			rt_kprintf("\033[2K\r");
317
#endif
318 319 320 321
			rt_kprintf("%s%s", FINSH_PROMPT, shell->line);
			return RT_TRUE;;
		}
	}
322

323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
	return RT_FALSE;
}

void finsh_push_history(struct finsh_shell* shell)
{
	if ((shell->use_history == 0) && (shell->line_position != 0))
	{
		/* push history */
		if (shell->history_count >= FINSH_HISTORY_LINES)
		{
			/* move history */
			int index;
			for (index = 0; index < FINSH_HISTORY_LINES - 1; index ++)
			{
				memcpy(&shell->cmd_history[index][0],
					&shell->cmd_history[index + 1][0], FINSH_CMD_SIZE);
339
			}
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
			memset(&shell->cmd_history[index][0], 0, FINSH_CMD_SIZE);
			memcpy(&shell->cmd_history[index][0], shell->line, shell->line_position);

			/* it's the maximum history */
			shell->history_count = FINSH_HISTORY_LINES;
		}
		else
		{
			memset(&shell->cmd_history[shell->history_count][0], 0, FINSH_CMD_SIZE);
			memcpy(&shell->cmd_history[shell->history_count][0], shell->line, shell->line_position);

			/* increase count and set current history position */
			shell->history_count ++;
		}
	}
	shell->current_history = shell->history_count;
}
#endif

#ifndef RT_USING_HEAP
struct finsh_shell _shell;
#endif
void finsh_thread_entry(void* parameter)
{
    char ch;

	/* normal is echo mode */
	shell->echo_mode = 1;

    finsh_init(&shell->parser);
	rt_kprintf(FINSH_PROMPT);

	while (1)
	{
		/* wait receive */
		if (rt_sem_take(&shell->rx_sem, RT_WAITING_FOREVER) != RT_EOK) continue;

		/* read one character from device */
		while (rt_device_read(shell->device, 0, &ch, 1) == 1)
		{
			/* handle history key */
			#ifdef FINSH_USING_HISTORY
			if (finsh_handle_history(shell, ch) == RT_TRUE) continue;
383
			#endif
384

385 386 387 388 389 390 391 392 393
			/* handle CR key */
			if (ch == '\r')
			{
				char next;

				if (rt_device_read(shell->device, 0, &next, 1) == 1)
					ch = next;
				else ch = '\r';
			}
394
			/* handle tab key */
395
			else if (ch == '\t')
396
			{
397
				/* auto complete */
398
				finsh_auto_complete(&shell->line[0]);
399
				/* re-calculate position */
400
				shell->line_position = strlen(shell->line);
401 402 403 404 405
				continue;
			}
			/* handle backspace key */
			else if (ch == 0x7f || ch == 0x08)
			{
406
				if (shell->line_position != 0)
407
				{
408
					rt_kprintf("%c %c", ch, ch);
409
				}
410 411 412
				if (shell->line_position <= 0) shell->line_position = 0;
				else shell->line_position --;
				shell->line[shell->line_position] = 0;
413
				continue;
414
			}
415

416
			/* handle end of line, break */
417
			if (ch == '\r' || ch == '\n')
418
			{
419
				/* change to ';' and break */
420
				shell->line[shell->line_position] = ';';
421

422
				#ifdef FINSH_USING_HISTORY
423
				finsh_push_history(shell);
424 425
				#endif

426
				if (shell->line_position != 0) finsh_run_line(&shell->parser, shell->line);
427
				else rt_kprintf("\n");
428

429 430 431
				rt_kprintf(FINSH_PROMPT);
				memset(shell->line, 0, sizeof(shell->line));
				shell->line_position = 0;
432

433
				break;
434 435
			}

436
			/* it's a large line, discard it */
437
			if (shell->line_position >= FINSH_CMD_SIZE) shell->line_position = 0;
438 439

			/* normal character */
440 441 442 443
			shell->line[shell->line_position] = ch; ch = 0;
			if (shell->echo_mode) rt_kprintf("%c", shell->line[shell->line_position]);
			shell->line_position ++;
			shell->use_history = 0; /* it's a new command */
444
		} /* end of device read */
445 446 447
	}
}

wuyangyong's avatar
wuyangyong 已提交
448
void finsh_system_function_init(const void* begin, const void* end)
449 450 451 452 453
{
	_syscall_table_begin = (struct finsh_syscall*) begin;
	_syscall_table_end = (struct finsh_syscall*) end;
}

wuyangyong's avatar
wuyangyong 已提交
454
void finsh_system_var_init(const void* begin, const void* end)
455 456 457 458 459
{
	_sysvar_table_begin = (struct finsh_sysvar*) begin;
	_sysvar_table_end = (struct finsh_sysvar*) end;
}

460 461 462 463 464
#if defined(__ICCARM__)               /* for IAR compiler */
  #ifdef FINSH_USING_SYMTAB
    #pragma section="FSymTab"
    #pragma section="VSymTab"
  #endif
465 466 467 468 469 470 471
#elif defined(__ADSPBLACKFIN__) /* for VisaulDSP++ Compiler*/
  #ifdef FINSH_USING_SYMTAB
    extern "asm" int __fsymtab_start;
    extern "asm" int __fsymtab_end;
    extern "asm" int __vsymtab_start;
    extern "asm" int __vsymtab_end;
  #endif
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
#elif defined(_MSC_VER)
#pragma section("FSymTab$a", read)
const char __fsym_begin_name[] = "__start";
const char __fsym_begin_desc[] = "begin of finsh";
__declspec(allocate("FSymTab$a")) const struct finsh_syscall __fsym_begin = 
{
	__fsym_begin_name,
	__fsym_begin_desc,
	NULL
};

#pragma section("FSymTab$z", read)
const char __fsym_end_name[] = "__end";
const char __fsym_end_desc[] = "end of finsh";
__declspec(allocate("FSymTab$z")) const struct finsh_syscall __fsym_end = 
{
	__fsym_end_name,
	__fsym_end_desc,
	NULL
};
492
#endif
B
bernard.xiong@gmail.com 已提交
493 494 495 496 497 498

/*
 * @ingroup finsh
 *
 * This function will initialize finsh shell
 */
499
void finsh_system_init(void)
500
{
501
	rt_err_t result;
502 503 504

#ifdef FINSH_USING_SYMTAB
#ifdef __CC_ARM                 /* ARM C Compiler */
wuyangyong's avatar
wuyangyong 已提交
505 506 507 508
    extern const int FSymTab$$Base;
    extern const int FSymTab$$Limit;
    extern const int VSymTab$$Base;
    extern const int VSymTab$$Limit;
509 510 511 512 513 514 515 516
	finsh_system_function_init(&FSymTab$$Base, &FSymTab$$Limit);
	finsh_system_var_init(&VSymTab$$Base, &VSymTab$$Limit);
#elif defined (__ICCARM__)      /* for IAR Compiler */
    finsh_system_function_init(__section_begin("FSymTab"),
                               __section_end("FSymTab"));
    finsh_system_var_init(__section_begin("VSymTab"),
                          __section_end("VSymTab"));
#elif defined (__GNUC__)        /* GNU GCC Compiler */
wuyangyong's avatar
wuyangyong 已提交
517 518 519 520
	extern const int __fsymtab_start;
	extern const int __fsymtab_end;
	extern const int __vsymtab_start;
	extern const int __vsymtab_end;
521
	finsh_system_function_init(&__fsymtab_start, &__fsymtab_end);
522
	finsh_system_var_init(&__vsymtab_start, &__vsymtab_end);
523 524 525
#elif defined(__ADSPBLACKFIN__) /* for VisualDSP++ Compiler */
    finsh_system_function_init(&__fsymtab_start, &__fsymtab_end);
    finsh_system_var_init(&__vsymtab_start, &__vsymtab_end);
526 527 528 529 530 531 532 533 534 535
#elif defined(_MSC_VER)
	unsigned int *ptr_begin, *ptr_end;

	ptr_begin = (unsigned int*)&__fsym_begin; ptr_begin += (sizeof(struct finsh_syscall)/sizeof(unsigned int));
	while (*ptr_begin == 0) ptr_begin ++;

	ptr_end = (unsigned int*) &__fsym_end; ptr_end --;
	while (*ptr_end == 0) ptr_end --;

	finsh_system_function_init(ptr_begin, ptr_end);
536 537 538
#endif
#endif

539 540 541 542 543
	/* create or set shell structure */
#ifdef RT_USING_HEAP
	shell = (struct finsh_shell*)rt_malloc(sizeof(struct finsh_shell));
#else
	shell = &_shell;
544 545 546 547 548 549
#endif
	if (shell == RT_NULL)
	{
		rt_kprintf("no memory for shell\n");
		return;
	}
B
bernard.xiong 已提交
550
	
551 552 553
	memset(shell, 0, sizeof(struct finsh_shell));

	rt_sem_init(&(shell->rx_sem), "shrx", 0, 0);
554
	result = rt_thread_init(&finsh_thread,
555 556 557
		"tshell",
		finsh_thread_entry, RT_NULL,
		&finsh_thread_stack[0], sizeof(finsh_thread_stack),
558
		FINSH_THREAD_PRIORITY, 10);
559

560 561
	if (result == RT_EOK)
		rt_thread_startup(&finsh_thread);
562
}