1

このコード、特にparse_argsを理解したいと思います。このコードは、pwdcatなどの基本的な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;
}
4

3 に答える 3

4

That's fun. The function separates buffer to an array of strings, where it hits a white-space delimiter.

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;

Initialization code...

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

Set all pointers to the beginning of the buffer

    for(cp=buf_args; (*cp=strsep(&wbuf, " \n\t")) != NULL ;){
  1. Set the write pointer cp to point to the first element in the buf_args array.
  2. Set *cp to the next instance of a non-whitespace token in the buffer, and advance wbuf to its ending position

        if ((*cp != '\0') && (++cp >= &buf_args[args_size]))
            break;
    

Stop when strsep returned NULL, or when it points to the end of the buffer, or when it points beyond the end of buf_args[]

    }

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

This loop creates a condensed copy of the buf_args[] array: it copies only the arguments which aren't empty strings. I guess that could happen if your source buffer had a series of consecutive white-space characters.

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

Put a NULL at the end of the output array and set the nargs output argument.

于 2012-11-22T07:38:47.950 に答える
3

では、parse 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ステートメントの中間部分は*cpequalsまで続きNULLます。

したがって、ポインターcpは最初は の文字列 (ポインター) を指しますbuf_args[0]*cp文字列区切り関数は、トークンのアドレスで埋めます。次に、ifステートメントは、1)ポインタが最後の要素を超えたかどうかをチェックすることによって、forループが の容量を超えたかどうか、および 2) 返されたトークンの最初の文字が文字列文字の末尾であるかどうかをチェックします。buf_argscp

注:その行は*(*cp) != '\0'

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

buf_args次に、1 つが見つかるまですべてをループしますNULL。が指す文字列buf_argsが 0 より長い場合、つまり文字を含む場合、配列argsはトークンへのポインタのコピーを取得します。

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

args入力されたエントリの後の最後の要素は に設定されNULLます。の値は、配列nargs内の埋められた要素の数の長さに設定されます。args

于 2012-11-22T07:45:51.653 に答える
2

コードのこのセクションで

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

" \n\t" 区切り文字 (最初はスペース) の wbuf 配列を確認し、その位置を buf_args に保存してから

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

関数から結果を取得するために引数を args 配列に抽出するセクション

于 2012-11-22T07:43:57.367 に答える