【问题标题】:Problem with strcat() overwriting my stringstrcat() 覆盖我的字符串的问题
【发布时间】:2011-07-05 22:53:22
【问题描述】:

我正在编写自己的 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]);
  }
}

【问题讨论】:

  • 与您的问题无关,但您确实应该查找 execvp 函数。为每个可能的参数数量编写不同的execlp 调用虽然可能,但非常丑陋和低效。

标签: c operating-system posix


【解决方案1】:

strsep() 不会创建它返回的令牌的独立副本。它修改原始字符串,将分隔符(在本例中为'#' 字符)替换为'\0',并返回一个指向原始字符串中标记开头的指针。

这意味着您的pathlist[] 指针都指向mypath 指向的字符串内 - 在您调用execute() 的位置,它看起来像这样:

               '/'   'b'   'i'   'n'   '\0'  '.'   '\0'
                ^                             ^
mypath ---------/                             |
                ^                             |
pathlist[0] ----/                             |
                                              |
pathlist[1] ----------------------------------/

pathlist[2] (null)

您现在可以看到为什么会观察到您的行为 - 当您在 pathlist[0] 上调用 strcat() 时,它会开始覆盖该数组,从第一个 '\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) {

【讨论】:

  • 这很有趣,并且完全可以解释为什么我会遇到这个错误。我确实最终使用了一个临时字符串并让它工作,但现在我知道它为什么这样做了。非常感谢,这是一个非常好的解释,也是对这个问题的完美答案。
【解决方案2】:

我必须承认我没有测试过你的代码,甚至没有仔细阅读它,但我建议完全放弃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;
}

【讨论】:

  • xstrcat() 是 b0rken,它不会 strcat() 到新内存,但它确实返回它。
  • @unwind: 好点子,差点掉进我警告的陷阱:)
猜你喜欢
  • 1970-01-01
  • 2017-01-24
  • 1970-01-01
  • 2022-01-21
  • 1970-01-01
  • 2014-06-27
  • 1970-01-01
  • 1970-01-01
  • 2015-05-01
相关资源
最近更新 更多