为什么strtok偶尔会导致总线错误?

时间:2021-03-17 17:48:09

标签: c split c-strings

编辑:我已经使这里的信息更具体,并执行了评论中的一些建议。


我有一个用 C 编写的外壳,使用时就像一个魅力。但是,我为名为 pipe_exec 的函数编写了一些测试,该函数会导致总线错误。我以为它最初来自我的 strtok 函数中的 split(现在可能仍然如此)。

pipe_exec 函数基本上处理带有 ls -a | wc -l 之类的管道的命令。当我使用实际的 shell 时,它总是可以正常工作,但在测试中,如果管道命令涉及任何标志,总是会出现总线错误。

问题可能出在我的测试上。

但我不知道问题是什么。它追溯到我的 strtok 函数中的 split,但它只有测试的总线问题,而在任何实际等效情况下都没有。

感谢这里的任何帮助。抱歉要查看这么多代码。

shell_exec_tests.c

static char *args1[20] = {"ls ", " wc"};     // works
static char *args2[20] = {"ls -a", "wc -l"}; // causes bus error

static int a = 0;
static int b = 0;

void test_setup(void)
{   
    a = pipe_exec(args1);
    b = pipe_exec(args2);
}

void test_teardown(void)
{
    // nothing
}

MU_TEST(test_check) 
{
    mu_check(a == EXIT_SUCCESS);
    mu_check(b == EXIT_SUCCESS);
}

MU_TEST_SUITE(test_suite)
{
    MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
    MU_RUN_TEST(test_check);
}

int main() 
{
    MU_RUN_SUITE(test_suite);
    MU_REPORT();
    return MU_EXIT_CODE;
}

pipe_exec.c


// make_proc: determine if a process goes to stdout or takes in data from stdin
void make_proc(int in, int out, char **cmd)
{
    pid_t rc;
    int status;
    rc = fork();
    if (rc < 0) {
        perror("fork");
        exit(1);
    }
    if (rc  == 0) {
        if (in != STDIN_FILENO) {
            dup2(in, STDIN_FILENO);
            close(in);
        }
        if (out != STDOUT_FILENO) {
            dup2(out, STDOUT_FILENO);
            close(out);
        }
        execvp(*cmd, cmd);
        errmsg(*cmd);
        exit(1);
    }
    waitpid(rc, &status, WUNTRACED);
    return;
}


// pipe_exec: loop through each command, connecting each through a pipe
int pipe_exec(char **args)
{
    int in, status, return_val;
    int pipe_no; // keep track of no. of cmds seperated by pipes 
    int pfd[2];
    pid_t rc;
    char **cmd;
    
    return_val = EXIT_SUCCESS;
    in = 0;
    pipe_no = 0;
    while (*args) {
        cmd = split(*args, " \t\r\n");
        if (!args[1]) {
            break;
        }

        if (pipe(pfd) < 0) {
            perror("pipe");
        }

        make_proc(in, pfd[1], cmd);
        close(pfd[1]);
        in = pfd[0];
        args++;
        pipe_no++;
    }
    // move pointer back
    args -= pipe_no;

    rc = fork();
    if (rc < 0) {
        perror("fork");
        exit(1);
    }
    if (rc == 0) {
        if (in != 0) dup2(in, STDIN_FILENO);
        execvp(*cmd, cmd);
        errmsg(*cmd);
        return_val = EXIT_FAILURE;
        exit(1);
    }
    waitpid(rc, &status, WUNTRACED);

    // pretty sure i need a pipe to get the EXIT_FAILURE from
    // the child if the child fails, but for now im just working
    // on finding that bus error issue
    return return_val;
}

最后,我的 split 函数:

// trim: trim leading and trailing whitespace on a string
static char *trim(char *str)
{
  char *end;

  // Trim leading space
  while(isspace((unsigned char)*str)) str++;

  if(*str == 0)  // All spaces?
    return str;

  // Trim trailing space
  end = str + strlen(str) - 1;
  while(end > str && isspace((unsigned char)*end)) end--;

  // Write new null terminator character
  end[1] = '\0';

  return str;
}


// split: take a string and break it up into an array of strings based on delim
char **split(char *s, const char *delim)
{
    char **split_s;
    char *token;
    size_t len;
    int i;
    
    len = strlen(s);
    
    split_s = calloc(len*2, sizeof(char*));
    if (split_s == NULL) {
        fprintf(stderr, "split: could not allocate memory\n");
        exit(EXIT_FAILURE);
    }

    i = 0;
    token = strtok(s, delim);
    while (token != NULL) {
        split_s[i] = trim(token);
        token = strtok(NULL, delim);
        i++;
    }
    split_s[i] = NULL;
    return split_s;
}

0 个答案:

没有答案