这一天我们继续解决两个问题:
- 解决命令找不到导致“Segmentation fault”的问题
- 添加参数分割,能够把输入的字符串分割成Shell风格的参数,比如argv[0]代表命令,argv[1]~argv[n]代表这个命令的参数。
Segmentation fault直译是段错误,通常访问非法内存会出现这样的问题。我们把目光指向find_cmd()函数:
CMD* find_cmd(CMD* pcmd, char *inbuf)
{
CMD *p = cmdtable;
for(; p != NULL; p++) {
if(strncmp(inbuf, p->name, strlen(p->name)) == 0) {
/* TODO: arguments process */
p->argc = 1;
p->argv = NULL;
return p;
}
}
return NULL;
}
经过调试,发现即使p已经到达cmdtable最后一个元素了,p++也总是返回TRUE的,所以访问到了非法内存。我们这里有个快速的改法:那就是计算出cmdtable的大小,使用计数器跟踪遍历的过程。于是find_cmd()函数改写如下:
CMD* find_cmd(CMD* pcmd, char *inbuf)
{
CMD *p = cmdtable;
int i = sizeof(cmdtable) / sizeof(CMD);
while(p && (i-- > 0)) {
if(strncmp(inbuf, p->name, strlen(p->name)) == 0) {
/* TODO: arguments process */
p->argc = 1;
p->argv = NULL;
return p;
} else {
p++;
}
}
return NULL;
}
经过测试,该函数运行正常。可是有个问题,我们用了i和p两个增量来遍历数组,难道没有更好的办法了吗?//TODO
参数分割
鲁迅先生提到“拿来主义”,所以我们不妨也从成熟的项目里拿一些代码,比如U-boot的参数分割。U-boot的参数分割是由make_argv()实现的。它有三个参数,第一个是待分割的输入字符串,第二个是支持的最大参数个数,第三个指向保存参数的数组argv。
static char *argv[PARAM_MAXARGS + 1];
static int make_argv(char *s, int argvsz, char *argv[])
{
int argc = 0;
/* split into argv */
while (argc < argvsz - 1) {
/* skip any white space */
while (isblank(*s))
++s;
if (*s == '\0') /* end of s, no more args */
break;
argv[argc++] = s; /* begin of argument string */
/* find end of string */
while (*s && !isblank(*s))
++s;
if (*s == '\0') /* end of s, no more args */
break;
*s++ = '\0'; /* terminate current arg */
}
argv[argc] = NULL;
return argc;
}
argv我把它定义成了静态的全局变量,这样我们可以改写find_cmd()使之支持参数分割:
CMD* find_cmd(CMD* pcmd, char *inbuf)
{
CMD *p = cmdtable;
int i = sizeof(cmdtable) / sizeof(CMD);
while(p && (i-- > 0)) {
if(strncmp(inbuf, p->name, strlen(p->name)) == 0) {
p->argc = make_argv(inbuf, sizeof(argv) / sizeof(argv[0]), argv);
p->argv = argv;
return p;
} else {
p++;
}
}
return NULL;
}
OK,搞定!接下来我们添加一个do_shell()函数,看看它能否正常接收到参数了。
int do_shell(int argc, char **argv)
{
int i = 0;
while(i < argc) {
printf("argv[%d] = %s\n", i, argv[i]);
i++;
}
if(argc < 2) {
printf("Missing parameter!\n");
return 1;
} else {
system(argv[1]);
return 0;
}
}
编译运行,看看结果:
$ ./diag
---Serval's DIAG---
DIAG>> ver
version 1.0
-OK-
DIAG>> illegalcommand
-ERR: -1-
DIAG>> shell uname -a
argv[0] = shell
argv[1] = uname
argv[2] = -a
CYGWIN_NT-5.1
-OK-
DIAG>> quit
Quiting...
OK,看起来一切正常。