我正在编写自己的shell,并且看起来有一些strcat()意外覆盖字符串的问题。
问题是尝试在本地目录中执行文件时。它应该搜索的路径中的第二个值是'。'并且第一个是/ bin,但是当将命令附加到/ bin以获得给execlp()
的绝对路径时,该命令也会被该命令覆盖。我知道MYPATH
的事情很奇怪,用#s分隔是很奇怪的,但这与问题无关。
如果您想运行它以查看我正在谈论的内容,我会添加一些有用的printf()
语句。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <sys/stat.h>
void execute(char**, int, char**, int);
int main (){
char *command, *mypath, *buffer, *arglist[1024], *pathlist[1024], **ap;
buffer = malloc(1024);
int loop = 1;
while (loop == 1){
int argnum = 0, pathnum = 0;
mypath = malloc(1024);
if(getenv("MYPATH") == NULL)
strcpy(mypath, "/bin#.");
else
strcpy(mypath, getenv("MYPATH"));
printf("myshell$ ");
command = readline("");
if(strcmp(command, "exit") == 0 || strcmp(command, "quit") == 0)
return 0;
if(strcmp(command, "") == 0)
continue;
/*Tokenizes Command*/
for(ap = arglist; (*ap = strsep(&command, " \t")) != NULL;){
argnum++;
if(**ap != '\0')
if(++ap >= &arglist[1024])
break;
}
/*Tokenizes Path*/
for(ap = pathlist; (*ap = strsep(&mypath, "#")) != NULL;){
pathnum++;
if(**ap != '\0')
if(++ap >= &pathlist[1024])
break;
}
execute(pathlist, pathnum, arglist, argnum);
}
return 0;
}
void execute(char *pathlist[], int pathnum, char *arglist[], int argnum){
pid_t pid;
int i;
int found = 0;
struct stat buf;
for(i = 0; i < pathnum; i++){
if (found == 1)
break;
printf("pathlist[0]: %s\n", pathlist[0]);
printf("pathlist[1]: %s\n", pathlist[1]);
strcat(pathlist[i], "/");
strcat(pathlist[i], arglist[0]);
printf("Pathlist[0] after strcat: %s\n", pathlist[0]);
printf("Pathlist[1] after strcat: %s\n", pathlist[1]);
if(stat(pathlist[i], &buf) == 0){
found = 1;
pid = fork();
if(pid == -1)
printf("Error: Fork Failed\n");
else if(pid == 0){
if(argnum == 0)
execlp(pathlist[i], arglist[0], (char *) NULL);
else if(argnum == 1)
execlp(pathlist[i], arglist[0], arglist[1], (char *) NULL);
else if(argnum == 2)
execlp(pathlist[i], arglist[0], arglist[1], arglist[2], (char *) NULL);
else if(argnum == 3)
execlp(pathlist[i], arglist[0], arglist[1], arglist[2], (char *) NULL);
}
else if(strcmp(arglist[argnum-1], "&") != 0){
wait(NULL);
}
}
else if(stat(pathlist[i], &buf) == -1 && i == pathnum-1 && found == 0)
printf("Error: Command '%s' not found.\n", arglist[0]);
}
}
答案 0 :(得分:2)
strsep()
不会创建它返回的令牌的独立副本。它修改原始字符串,用'#'
替换分隔符(在本例中为'\0'
字符),并返回指向原始字符串中标记开头的指针。
这意味着您的pathlist[]
指针都指向mypath
指向的字符串 - 在您调用execute()
时,它看起来像这样:
'/' 'b' 'i' 'n' '\0' '.' '\0'
^ ^
mypath ---------/ |
^ |
pathlist[0] ----/ |
|
pathlist[1] ----------------------------------/
pathlist[2] (null)
您现在可以看到为什么观察您的行为了 - 当您在strcat()
上调用pathlist[0]
时,它会从第一个'\0'
开始覆盖该数组。 pathlist[1]
仍然指向同一位置,但该位置的字符串内容已被strcat()
覆盖。
在execute()
函数中,您不应尝试直接连接到pathlist[i]
。而是在临时位置构造一个新字符串:
char execpath[4096];
if (snprintf(execpath, sizeof execpath, "%s/%s", pathlist[i], arglist[0]) >= sizeof execpath) {
/* Path was too long, handle the error */
}
if (stat(execpath, &buf) == 0) {
答案 1 :(得分:0)
我必须承认我没有测试过您的代码,甚至没有仔细阅读过,但我建议完全删除strcat
。这是不安全的,因为它可能会在没有通知的情况下写入缓冲区的末尾。
您可能想要定义一些安全字符串函数并改为使用它们:
char *xstrdup(char const *s)
{
char *copy = strdup(s);
if (copy == NULL) {
fprintf(stderr, "error: cannot copy string: %s\n", strerror(errno));
exit(1);
}
return copy;
}
char *xstrcat(char *s, char const *append)
{
size_t len = strlen(s) + strlen(append) + 1;
size_t news = realloc(s, len);
if (news == NULL) {
free(s);
fprintf(stderr, "error: cannot append strings: %s\n", strerror(errno));
exit(1);
}
strcat(news, append);
return news;
}