用C解释代码

时间:2012-11-22 07:21:00

标签: c linux shell code-analysis

我想特别了解这段代码的parse_args。这段代码就像pwd cat一样运行基本的linux命令等。我想了解parse_args是如何工作的。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#define BUFFER_SIZE 1<<16
#define ARR_SIZE 1<<16

void parse_args(char *buffer, char** args, 
                size_t args_size, size_t *nargs)
{
    char *buf_args[args_size]; /* You need C99 */
    char **cp;
    char *wbuf;
    size_t i, j;

    wbuf=buffer;
    buf_args[0]=buffer; 
    args[0] =buffer;

    for(cp=buf_args; (*cp=strsep(&wbuf, " \n\t")) != NULL ;){
        if ((*cp != '\0') && (++cp >= &buf_args[args_size]))
            break;
    }

    for (j=i=0; buf_args[i]!=NULL; i++){
        if(strlen(buf_args[i])>0)
            args[j++]=buf_args[i];
    }

    *nargs=j;
    args[j]=NULL;
}


int main(int argc, char *argv[], char *envp[]){
    char buffer[BUFFER_SIZE];
    char *args[ARR_SIZE];

    int *ret_status;
    size_t nargs;
    pid_t pid;

    while(1){
        printf("$ ");
        fgets(buffer, BUFFER_SIZE, stdin);
        parse_args(buffer, args, ARR_SIZE, &nargs); 

        if (nargs==0) continue;
        if (!strcmp(args[0], "exit" )) exit(0);       
        pid = fork();
        if (pid){
            printf("Waiting for child (%d)\n", pid);
            pid = wait(ret_status);
            printf("Child (%d) finished\n", pid);
        } else {
            if( execvp(args[0], args)) {
                puts(strerror(errno));
                exit(127);
            }
        }
    }    
    return 0;
}

3 个答案:

答案 0 :(得分:4)

这很有趣。该函数将缓冲区分隔为一个字符串数组,它会命中一个空格分隔符。

void parse_args(char *buffer, char** args, 
                size_t args_size, size_t *nargs)
{
    char *buf_args[args_size]; /* You need C99 */
    char **cp;
    char *wbuf;
    size_t i, j;

初始化代码......

    wbuf=buffer;
    buf_args[0]=buffer; 
    args[0] =buffer;

将所有指针设置为缓冲区的开头

    for(cp=buf_args; (*cp=strsep(&wbuf, " \n\t")) != NULL ;){
  1. 将写指针cp设置为指向buf_args数组中的第一个元素。
  2. 将* cp设置为缓冲区中非空白字符的下一个实例,并将wbuf前进到其结束位置

        if ((*cp != '\0') && (++cp >= &buf_args[args_size]))
            break;
    
  3. strsep返回NULL时,或者当它指向缓冲区的末尾时,或者当它指向buf_args []

    结束时停止
        }
    
        for (j=i=0; buf_args[i]!=NULL; i++){
            if(strlen(buf_args[i])>0)
                args[j++]=buf_args[i];
        }
    

    此循环创建buf_args []数组的精简副本:它仅复制非空字符串的参数。我想如果你的源缓冲区有一系列连续的空白字符,就会发生这种情况。

        *nargs=j;
        args[j]=NULL;
    }
    

    在输出数组的末尾放置一个NULL并设置nargs输出参数。

答案 1 :(得分:3)

好的,让我们来看看解析args中的一些行

wbuf=buffer;
buf_args[0]=buffer; 
args[0] =buffer;

这会将wbuf设置为指向buffer中的内容。它还将buf_args数组中的第一个指针设置为buffer。它也为args的第一个元素做了同样的事情。

for(cp=buf_args; (*cp=strsep(&wbuf, " \n\t")) != NULL ;){
    if ((*cp != '\0') && (++cp >= &buf_args[args_size]))
        break;
}

函数strsep通过wbuf查找分隔符空格,换行符或制表符,并返回指向令牌开头的指针,并将wbuf更新到令牌的末尾。如果字符串中没有更多分隔符,则函数返回NULL。因此,for语句的中间部分会一直持续到*cp等于NULL

因此,指针cp最初指向buf_args[0]中的字符串(指针)。字符串分隔符函数使用令牌的地址填充*cp。然后,if语句检查1)for循环是否已超过buf_args的容量,检查指针cp是否已超过最后一个元素,2)返回标记的第一个字符是字符串字符的结尾。

注意:我认为该行应为*(*cp) != '\0'

for (j=i=0; buf_args[i]!=NULL; i++){
    if(strlen(buf_args[i])>0)
        args[j++]=buf_args[i];
}

然后,它遍历所有buf_args,直到找到NULLbuf_args。如果args指向的字符串长于0,即它有字符,则数组*nargs=j; args[j]=NULL; 将获得指向该标记的指针的副本。

args

填充条目后NULL的最后一个元素设置为nargs。并且args的值设置为{{1}}数组中已填充元素数的长度。

答案 2 :(得分:2)

这段代码

for(cp=buf_args; (*cp=strsep(&wbuf, " \n\t")) != NULL ;){
        if ((*cp != '\0') && (++cp >= &buf_args[args_size]))
            break;
 }

检查wbuf数组中的“\ n \ t”分隔符(首先是空格)并将其位置保存在buf_args中

for (j=i=0; buf_args[i]!=NULL; i++){
        if(strlen(buf_args[i])>0)
            args[j++]=buf_args[i];
    }

部分将参数展开到args数组中以使结果脱离函数