2014. 5. 15. 17:41

[유닉스고급프로그래밍] 4-22 디렉터리 계통구조를 재귀적으로 따라 내려가면서 파일 종류 통계를 구하는 프로그램

APUE2 소스해석


#include "apue.h"

#include <dirent.h>

#include <limits.h>


typedef int Myfunc(const char * , const struct stat * , int);


static Myfunc myfunc;

static int myftw(char *, Myfunc *);

static int dopath(Myfunc * );


static long nreg, ndir, nblk, nchr, nfifo, nslink, nsock, ntot;


int main(int argc, char *argv[])

{

    int ret;

    if (argc != 2)

        err_quit("usage : ftw <starting-pathname>");


    ret = myftw(argv[1], myfunc); /*실제 잡업을 수행*/


    ntot = nreg + ndir + nblk + nchr + nfifo + nslink + nsock;


    if ( ntot == 0 )

        ntot = 1;


    printf("regular files = %7ld, %5.2f %%\n", nreg, nreg*100.0/ntot);

    printf("directories = %7ld, %5.2f %%\n", ndir, ndir*100.0/ntot);

    printf("block special = %7ld, %5.2f %%\n", nblk, nblk*100.0/ntot);

    printf("char special = %7ld, %5.2f %%\n", nchr, nchr*100.0/ntot);

    printf("FIFOS files = %7ld, %5.2f %%\n", nfifo, nfifo*100.0/ntot);

    printf("symbolic links = %7ld, %5.2f %%\n", nslink, nslink*100.0/ntot);

    printf("sockets = %7ld, %5.2f %%\n", nsock, nsock*100.0/ntot);


    exit(ret);


}


#define FTW_F 1

#define FTW_D 2

#define FTW_DNR 3

#define FTW_NS 4


static char * fullpath; /* 각파일의 전체 경로이름을 담음*/


//여기서 수행

static int myftw(char * pathname, Myfunc *func) /*func()의 반환값을 그대로 돌려줌 */

{

    int len;

    fullpath = path_alloc(&len); /*PATH_MAX+1*/ //apue에서 제공해주는 함수. 운영체제 환경에 따라 사용할 수 있는 공간의 최대 크기를 리턴해 주는 함수인듯.


    strncpy(fullpath, pathname, len);

    printf("fullpath: %s\n", fullpath);

    fullpath[len-1] = 0;


    return(dopath(func));

}



/* fullpath에서부터 계통구조를 따라간다

   fullpath가 디렉터리가 아니면 lstat()으로 정보를 얻어서

   func()를 호출하고 반환. 디렉터리면 그 디렉터리의 각 이름마다 이 함수를 재귀호출

*/

static int dopath(Myfunc * func)

{

    struct stat statbuf;

    struct dirent *dirp;

    DIR *dp;

    int ret;

    char *ptr;


    if ( lstat(fullpath, &statbuf) < 0 )

        return(func(fullpath, &statbuf, FTW_NS));
    if ( S_ISDIR(statbuf.st_mode) == 0 )
        return(func(fullpath, &statbuf, FTW_F));


    /*
     디렉터리면 우선 그 디렉터리에 대해 func()를 호출하고
     그 디렉터리의 각 파일이름을 처리한다.
     */
    if (( ret = func(fullpath, &statbuf, FTW_D)) != 0 )
        return(ret);

    ptr = fullpath + strlen(fullpath);  /* fullpath의 끝을 가리킨다*/

    *ptr++ = '/';
    *ptr = 0;

    if (( dp = opendir(fullpath)) == NULL ) /* 디렉터리를 읽을 수 없음 */
        return(func(fullpath, &statbuf, FTW_DNR));

    while (( dirp = readdir(dp)) != NULL ) {
        if ( strcmp(dirp->d_name, ".") == 0 || strcmp(dirp->d_name, "..") == 0 )
            continue;
        strcpy(ptr, dirp->d_name); /*슬러시 다음에 이름을 추가 */

        if ( ( ret = dopath(func)) != 0 )
            break;

    }
    ptr[-1] = 0; /*슬래시 이후의 모든 것을 삭제 */

    if ( closedir(dp) < 0 )
        err_ret("can't close directory %s", fullpath);

    return(ret);
}

static int myfunc(const char * pathname, const struct stat * statptr, int type)
{
    printf("myfunc pathname : %s\n", pathname);
    switch(type) {
        case FTW_F:
            switch(statptr->st_mode & S_IFMT) //S_IFMT : bit mask for the file type bit fields! 00170000
            {
                case S_IFREG :  nreg++;     break;
                case S_IFBLK :  nblk++;     break;
                case S_IFCHR :  nchr++;     break;
                case S_IFIFO :  nfifo++;    break;
                case S_IFLNK :  nslink++;   break;
                case S_IFSOCK:  nsock++;    break;
                case S_IFDIR:
                    err_dump("for S_IFDIR for %s", pathname);
            }
            break;
        case FTW_D:
            ndir++;
            break;
        case FTW_DNR:
            err_ret("can't read directory %s", pathname);
            break;
        case FTW_NS:
            err_ret("stat error for %s", pathname);
            break;
        default:
            err_dump("unknown type %d for pathname %s", type, pathname);
    }
    return (0);
}

결과

./4-22-2 test

fullpath: test

myfunc pathname : test

myfunc pathname : test/Makefile

myfunc pathname : test/a.h

myfunc pathname : test/a.c

myfunc pathname : test/b.c

myfunc pathname : test/a.o

myfunc pathname : test/test

myfunc pathname : test/abc

myfunc pathname : test/abc/def

regular files =       6, 66.67 %

directories =       3, 33.33 %

block special =       0,  0.00 %

char special =       0,  0.00 %

FIFOS files =       0,  0.00 %

symbolic links =       0,  0.00 %

sockets =       0,  0.00 %



여기서 궁금했던 점은 

ret = dopath(func)에서 fullpath를 넘기지 않는데 어떻게 갱신이 될까? 하는 점이었다.


하지만 이 내용을 보면 알 수 있다.


#전역변수로 fullpath 선언

static char * fullpath; /* 각파일의 전체 경로이름을 담음*/


탐색할 디렉토리가 

다음의 구조와 같다고 가정하면

test / abd

     /  de


1. ptr = fullpath + strlen(fullpath);  /* fullpath의 끝을 가리킨다*/

2. *ptr++ = '/';
    *ptr = 0;

3.  strcpy(ptr, dirp->d_name); /*슬러시 다음에 이름을 추가 */


4. while문 돌고 나서 strcpy(ptr, dirp->d_name); /*슬러시 다음에 이름을 추가 */




이런식이랄까?

ptr 변수의 위치는 while문을 도는 내내 고정되 있어서 같은 디렉터리 내의 파일 이름을

덮어쓰는 형식으로 구현되어 있었다.

fullpath는 전역변수이기에 dopath할때 func 함수포인터만 넘겨도 되었던 것이고!


신기하기도 했던 내용이라 기록으로 남겨둔다.

Posted by Triany