Проблема с перезаписью моей строки strcat () - PullRequest
1 голос
/ 28 февраля 2011

Я пишу свою собственную оболочку и, похоже, у меня возникла какая-то проблема с тем, что 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]);
  }
}

Ответы [ 2 ]

2 голосов
/ 01 марта 2011

strsep() не создает независимую копию возвращаемого токена.Он изменяет исходную строку, заменяя разделитель (в данном случае символ '#') на '\0', и возвращает указатель, который указывает на начало токена в исходной строке.

Thisозначает, что ваши 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) {
0 голосов
/ 28 февраля 2011

Должен признать, что я не проверял ваш код или даже очень внимательно его читал, но я бы рекомендовал полностью удалить 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;
}
...