Language/C언어

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

Triany 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 함수포인터만 넘겨도 되었던 것이고!


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