shell.c 11.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__)
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 307 308 309 310 311
		if (shell->use_history)
		{
			rt_kprintf("\033[2K\r");
			rt_kprintf("%s%s", FINSH_PROMPT, shell->line);
			return RT_TRUE;;
		}
	}
312

313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
	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);
329
			}
330 331 332 333 334 335 336 337 338 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
			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;
373
			#endif
374

375 376 377 378 379 380 381 382 383
			/* handle CR key */
			if (ch == '\r')
			{
				char next;

				if (rt_device_read(shell->device, 0, &next, 1) == 1)
					ch = next;
				else ch = '\r';
			}
384
			/* handle tab key */
385
			else if (ch == '\t')
386
			{
387
				/* auto complete */
388
				finsh_auto_complete(&shell->line[0]);
389
				/* re-calculate position */
390
				shell->line_position = strlen(shell->line);
391 392 393 394 395
				continue;
			}
			/* handle backspace key */
			else if (ch == 0x7f || ch == 0x08)
			{
396
				if (shell->line_position != 0)
397
				{
398
					rt_kprintf("%c %c", ch, ch);
399
				}
400 401 402
				if (shell->line_position <= 0) shell->line_position = 0;
				else shell->line_position --;
				shell->line[shell->line_position] = 0;
403
				continue;
404
			}
405

406
			/* handle end of line, break */
407
			if (ch == '\r' || ch == '\n')
408
			{
409
				/* change to ';' and break */
410
				shell->line[shell->line_position] = ';';
411

412
				#ifdef FINSH_USING_HISTORY
413
				finsh_push_history(shell);
414 415
				#endif

416
				if (shell->line_position != 0) finsh_run_line(&shell->parser, shell->line);
417
				else rt_kprintf("\n");
418

419 420 421
				rt_kprintf(FINSH_PROMPT);
				memset(shell->line, 0, sizeof(shell->line));
				shell->line_position = 0;
422

423
				break;
424 425
			}

426
			/* it's a large line, discard it */
427
			if (shell->line_position >= FINSH_CMD_SIZE) shell->line_position = 0;
428 429

			/* normal character */
430 431 432 433
			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 */
434
		} /* end of device read */
435 436 437
	}
}

wuyangyong's avatar
wuyangyong 已提交
438
void finsh_system_function_init(const void* begin, const void* end)
439 440 441 442 443
{
	_syscall_table_begin = (struct finsh_syscall*) begin;
	_syscall_table_end = (struct finsh_syscall*) end;
}

wuyangyong's avatar
wuyangyong 已提交
444
void finsh_system_var_init(const void* begin, const void* end)
445 446 447 448 449
{
	_sysvar_table_begin = (struct finsh_sysvar*) begin;
	_sysvar_table_end = (struct finsh_sysvar*) end;
}

450 451 452 453 454
#if defined(__ICCARM__)               /* for IAR compiler */
  #ifdef FINSH_USING_SYMTAB
    #pragma section="FSymTab"
    #pragma section="VSymTab"
  #endif
455 456 457 458 459 460 461
#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
462
#endif
B
bernard.xiong@gmail.com 已提交
463 464 465 466 467 468

/*
 * @ingroup finsh
 *
 * This function will initialize finsh shell
 */
469
void finsh_system_init(void)
470
{
471
	rt_err_t result;
472 473 474

#ifdef FINSH_USING_SYMTAB
#ifdef __CC_ARM                 /* ARM C Compiler */
wuyangyong's avatar
wuyangyong 已提交
475 476 477 478
    extern const int FSymTab$$Base;
    extern const int FSymTab$$Limit;
    extern const int VSymTab$$Base;
    extern const int VSymTab$$Limit;
479 480 481 482 483 484 485 486
	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 已提交
487 488 489 490
	extern const int __fsymtab_start;
	extern const int __fsymtab_end;
	extern const int __vsymtab_start;
	extern const int __vsymtab_end;
491
	finsh_system_function_init(&__fsymtab_start, &__fsymtab_end);
492
	finsh_system_var_init(&__vsymtab_start, &__vsymtab_end);
493 494 495
#elif defined(__ADSPBLACKFIN__) /* for VisualDSP++ Compiler */
    finsh_system_function_init(&__fsymtab_start, &__fsymtab_end);
    finsh_system_var_init(&__vsymtab_start, &__vsymtab_end);
496 497 498
#endif
#endif

499 500 501 502 503
	/* create or set shell structure */
#ifdef RT_USING_HEAP
	shell = (struct finsh_shell*)rt_malloc(sizeof(struct finsh_shell));
#else
	shell = &_shell;
504 505 506 507 508 509
#endif
	if (shell == RT_NULL)
	{
		rt_kprintf("no memory for shell\n");
		return;
	}
B
bernard.xiong 已提交
510
	
511 512 513
	memset(shell, 0, sizeof(struct finsh_shell));

	rt_sem_init(&(shell->rx_sem), "shrx", 0, 0);
514
	result = rt_thread_init(&finsh_thread,
515 516 517
		"tshell",
		finsh_thread_entry, RT_NULL,
		&finsh_thread_stack[0], sizeof(finsh_thread_stack),
518
		FINSH_THREAD_PRIORITY, 10);
519

520 521
	if (result == RT_EOK)
		rt_thread_startup(&finsh_thread);
522
}