[유닉스고급프로그래밍] 4-22 디렉터리 계통구조를 재귀적으로 따라 내려가면서 파일 종류 통계를 구하는 프로그램
[유닉스고급프로그래밍] 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
3. strcpy(ptr, dirp->d_name); /*슬러시 다음에 이름을 추가 */
4. while문 돌고 나서 strcpy(ptr, dirp->d_name); /*슬러시 다음에 이름을 추가 */
이런식이랄까?
ptr 변수의 위치는 while문을 도는 내내 고정되 있어서 같은 디렉터리 내의 파일 이름을
덮어쓰는 형식으로 구현되어 있었다.
fullpath는 전역변수이기에 dopath할때 func 함수포인터만 넘겨도 되었던 것이고!
신기하기도 했던 내용이라 기록으로 남겨둔다.