ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Unix Programming : 명령어 만들기 : 2/3
    Computer Science/etc 2020. 6. 15. 16:30
    728x90

    2.      mycp source dest 또는 source … directory

    1)      구현 아이디어 및 설명

     

    • 1번에서 사용한 getopt함수를 포함하여, stat와 폴더 관련 함수들을 주로 사용하였다. stat함수의 구조는 다음과 같다.  

    • path에 검색할 파일의 경로를 지정하고, 검색한 정보를 buf에 저장한다. 작성하면서 다시 알게된 점은 path 부분에 파일 이름을 받은 인자가 올 수 있는데, 이 파일이 코드 상의 현재 디렉토리에서 파악이 가능한 파일이어야 적용된다는 점이다. 
    • 때문에 cp시 하위 폴더를 방문 후, 새로운 폴더를 방문하려면, stat함수를 사용하기 전에 상위 폴더로 변경 후 사용해야 적용된다. 
    • 폴더 관련 함수 중 주로 사용한 함수는 mkdir과 chdir 이다. mkdir과 chdir함수의 구조는 다음과 같다. 

    • 이 두 함수를 사용하기 위해, 현재 디렉토리의 위치(path)를 문자열로 저장하기 위해, strcat함수를 이용하여 문자열로 고정하였다. 필요할 때 고정한 문자열을 가져옴으로써 복잡함을 줄였다. 
    • cp를 구현하면서 가장 헷갈렸던 점은 코드 상 현재 디렉토리가 어디인지 파악하는 것이었다. 이를 쉽게 해결하기 위해, strcat함수를 이용해 문자열로 위치를 고정하고, chdir함수를 이용하여 상위 폴더와 하위 폴더를 옮기며 수행하였다.

    2)     프로그램 소스 및 설명문

    #include <stdio.h>
    
    #include <stdlib.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <dirent.h>
     
     
    int main(int argc, char* argv[]){
        
        char ans, buf[BUFSIZ], *path, *pathog, *filename;
        DIR *dir=NULL;
        struct dirent *entry=NULL;
        int rfd, wfd, n, filenum=0, overwrite=0;
        struct stat st, ft, tt;
        
        // 주석문 작성 순서 : 옵션이 없는 경우 -> 옵션이 있는 경우
        // 옵션이 있는 경우
        while((n=getopt(argc, argv, "firv"))!=-1){
            // copy a(argv[2]) to b([argv[3])라고 하자
            stat(argv[2], &st); // 파일 a에 대한 정보를 st에 저장한다
            switch(n){
                case 'f':
                    rfd = open( argv[2], O_RDONLY );
                    if(rfd==-1){
                        perror("open");
                        exit(1);
                    }
                    path=getcwd(NULL,BUFSIZ);   // 현재의 경로를 path 문자열에 저장한다
                    if((dir=opendir(path))==NULL)
                    // opendir함수를 통해 path 경로에 해당하는 디렉토리를 dir 포인터로 한다
                    {
                            perror("opendir");
                            exit(1);
                    }
                    while((entry=readdir(dir))!=NULL)
                    // dir 포인터에 해당되는 파일을 entry로 하여 순차 접근한다
                    {
                        if(strcmp(argv[3], entry->d_name))
                        // argv[3] 과 dir 포인터가 가리키는 파일의 이름이 같다면,
                        {
                            remove(entry->d_name); // 해당 파일의 제거한다
                            break;
                        }
                    }
                    wfd = open(argv[3], O_WRONLY | O_CREAT | O_TRUNC, st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
                    if(wfd==-1){
                        perror("open");
                        exit(1);
                    }
                    while((n=read(rfd, buf, BUFSIZ))>0){
                    if(write(wfd, buf, n)!=n)
                            perror("write");
                        if(n==-1)
                            perror("read");
                    }
                    close(rfd);
                    close(wfd);
                    break;
                case 'i':
                    rfd = open( argv[2], O_RDONLY );
                    if(rfd==-1){
                        perror("open");
                        exit(1);
                    }
                    path=getcwd(NULL,BUFSIZ);
                    if((dir=opendir(path))==NULL)
                    // opendir함수를 통해 cwd 경로에 해당하는 디렉토리를 dir 포인터로 한다
                    {
                            perror("opendir");
                            exit(1);
                    }
                    while((entry=readdir(dir))!=NULL){
                        if(strcmp(argv[3], entry->d_name))
                        // argv[3]과 dir 포인터가 가리키는 파일의 이름과 같다면
                        {
                            overwrite=1; // overwrite를 1로 한다 (overwrite를 해야한다는 의미)
                            break;
                        }
                    }
                    if(overwrite==1){
                        printf("mycp: overwrite ‘test2.txt’? "); // overwrite를 할지 사용자에게 묻는다
                        scanf("%c", &ans);
                        if(ans=='y') // yes일 경우
                        {
                            wfd = open(argv[3], O_WRONLY | O_CREAT | O_TRUNC, st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
                            if(wfd==-1){
                                perror("open");
                                exit(1);
                            }
                            while((n=read(rfd, buf, BUFSIZ))>0){
                            if(write(wfd, buf, n)!=n)
                                perror("write");
                            if(n==-1)
                                perror("read");
                            }
                            close(rfd);
                            close(wfd);
                        }
                        else
                            break;
                    }
                    else{
                        wfd = open(argv[3], O_WRONLY | O_CREAT | O_TRUNC, st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
                        if(wfd==-1){
                            perror("open");
                            exit(1);
                        }
                        while((n=read(rfd, buf, BUFSIZ))>0){
                        if(write(wfd, buf, n)!=n)
                                perror("write");
                            if(n==-1)
                                perror("read");
                        }
                        close(rfd);
                        close(wfd);
                    }
                    break;
                case 'v':
                    stat(argv[3], &st); // 파일 b에 대한 정보를 st에 저장한다
                    if(S_ISDIR(st.st_mode))
                    // 현재 파일이 디렉토리 파일인 경우
                    {
                        stat(argv[2], &st); // 파일 a에 대한 정보를 st에 저장한다
                        rfd = open( argv[2], O_RDONLY );    // 파일 a를 읽기 전용으로 열고, rfd에 파일기술자를 저장한다
                        if(rfd==-1){
                            perror("open");
                            exit(1);
                        }
                        chdir(argv[3]); // 파일 b 내부를 현재 디렉토리로 한다
                        wfd = open( argv[2], O_WRONLY | O_CREAT | O_TRUNC, st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
                        // 아래와 같이, 마스크를 이용하여 접근 권한을 가져온다
                        if(wfd==-1){
                            perror("open");
                            exit(1);
                        }
                        while((n=read(rfd, buf, BUFSIZ))>0){
                            if(write(wfd, buf, n)!=n)
                                perror("write");
                            if(n==-1)
                                perror("read");
                        }
                        close(rfd);
                        close(wfd);
                        printf("'%s' -> '%s'\n", argv[2], getcwd(NULL, BUFSIZ));
                        // 복사가 성공한다면, cp -v 와 같은 형식의 출력문을 출력한다
                    }
                    else{
                        stat(argv[2], &st); // 파일 a에 대한 정보를 st에 저장한다
                        rfd = open( argv[2], O_RDONLY );    // 파일 a를 읽기 전용으로 열고, rfd에 파일기술자를 저장한다
                        if(rfd==-1){
                            perror("open");
                            exit(1);
                        }
                        wfd = open( argv[3], O_WRONLY | O_CREAT | O_TRUNC, st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
                        // 아래와 같이, 마스크를 이용하여 접근 권한을 가져온다
                        if(wfd==-1){
                            perror("open");
                            exit(1);
                        }
                        while((n=read(rfd, buf, BUFSIZ))>0){
                            if(write(wfd, buf, n)!=n)
                                perror("write");
                            if(n==-1)
                                perror("read");
                        }
                        close(rfd);
                        close(wfd);
                        printf("'%s' -> '%s'\n", argv[2], argv[3]);
                        // 복사가 성공한다면, cp -v 와 같은 형식의 출력문을 출력한다
                    }
                    break;
                             case 'r':
                                 if(S_ISDIR(st.st_mode))
                                 // 파일 a가 디렉토리 파일인 경우
                                 {
                                     pathog=getcwd(NULL, BUFSIZ);    // pathog를 상위 폴더 경로로 하여 저장한다 
                        chdir(argv[2]); // 현재 디렉토리를 디렉토리 파일 a 내부로 한다
                        path=getcwd(NULL, BUFSIZ);  //path를 현재 경로로 하여 저장한다
                        if((dir=opendir(path))==NULL)
                        // 현재 디렉토리 내의 파일을 순차 접근하기 위해, 현재 디렉토리를 가리키는 포인터를 dir로 한다
                        {
                            perror("opendir");
                            exit(1);
                        }
                         while((entry=readdir(dir))!=NULL)
                         // readdir함수를 이용하여 포인터가 가리킨 파일을 entry라고 한다
                         // 이제부터는 현재 파일이라 함은, 포인터가 가리킨 현재 파일 entry를 뜻한다
                         {
                            stat(entry->d_name, &ft);   // 현재 파일에 대한 정보를 ft에 저장한다 
                            if(S_ISREG(ft.st_mode))
                            // 현재 파일이 일반 파일인 경우
                            {
                                filename=entry->d_name; // 현재 파일의 이름을 filename으로 한다
                                stat(filename, &tt);    // filename에 대한 정보를 tt에 저장한다
                                rfd = open(filename, O_RDONLY); // filename 파일을 읽기 전용으로 연다
                                if(rfd==-1){
                                    perror("open");
                                    exit(1);
                                }
                                filenum++;  // 디렉토리 내부의 파일의 갯수를 파악하기 위한 count이다 
                                if(filenum==1)
                                // 디렉토리 내부의 파일의 갯수가 1인 경우에만 새 폴더를 만들고,
                                // filenum이 그 이상이라면 이 과정은 거치지 않는다
                                {
                                    chdir(pathog);  //pathog를 현재 디렉토리로 한다
                                    path=getcwd(NULL, BUFSIZ);
                                    strcat(path, "/");
                                    strcat(path, argv[3]);  // 새 폴더를 만들 경로를 문자열로 만들기 위한 과정이다
                                    mkdir(path, st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
                                    // 새 폴더를 만들고, 이 폴더에 대한 접근 권한은 상위 폴더의 정보가 담긴 st와 동일하게 한다
                                    pathog=getcwd(NULL, BUFSIZ);    // pathog에 현재 디렉토리의 문자열을 저장한다
                                }
                                chdir(pathog);  // pathog를 현재 디렉토리로 한다. 이 과정을 거쳐야 컴파일러가 파일 b의 위치를 알 수 있다
                                chdir(argv[3]); // 디렉토리 파일 b 내부를 현재 디렉토리로 한다
                                stat(filename, &tt);    // filename에 대한 정보를 tt에 저장한다 (생략 가능))
                                wfd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, tt.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
                                // 옵션 없는 경우와 동일한 방식이다.
                                // 주의할 점은 filename에 대한 정보는 tt에 저장되어 있기 때문에 마스크 플래그와 tt의 퍼미션을 &연산해야 한다
                                if(wfd==-1){
                                    perror("open");
                                    exit(1);
                                }
                                while((n=read(rfd, buf, BUFSIZ))>0){
                                    if(write(wfd, buf, n)!=n)
                                        perror("write");
                                    if(n==-1)
                                       perror("read");
                                }
                                chdir(pathog);  // 상위 폴더를 현재 디렉토리로 한다. 이 과정을 거쳐야 컴파일러가 파일 a의 위치를 알 수 있다
                                chdir(argv[2]); // 디렉토리 파일 a 내부를 현재 위치로 한다. 다른 파일의 복사를 위함이다
                            }
                            close(rfd);
                            close(wfd);
                        }
                    }
                                 break;
            }
        }
        
        if(n==-1 && optind==1)
        // 옵션이 없고, 옵션의 인덱스의 변화 역시 없다면 다음을 실행한다
        {
            // copy a(argv[1]) to b([argv[2])라고 하자
            stat(argv[2], &st); // 파일 b에 대한 정보를 st에 저장한다
            if(S_ISDIR(st.st_mode))
            // 파일 b가 디렉토리 파일인 경우
            {
                stat(argv[1], &st); // 파일 a에 대한 정보를 st에 저장한다
                rfd = open( argv[1], O_RDONLY );    
                // 파일 a를 open함수를 통해 연다
                // O_RDONLY는 읽기 전용을 의미하는 플래그 값이다
                // 파일 기술자를 리턴한다
                if(rfd==-1) // open함수의 오류가 있다면 파일 기술자 값이 -1�� 된다
                {
                    perror("open");
                    exit(1);
                }
                chdir(argv[2]); // 파일 b의 위치(디렉토리 파일 내부)를 현재 위치로 한다
                wfd = open( argv[1], O_WRONLY | O_CREAT | O_TRUNC, st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
                // st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)는 파일 a의 접근 권한을 그대로 복사하기 위해 마스크(S_IRWXU | S_IRWXG | S_IRWXO) 플래그와
                // 현재 파일의 접근 권한을 & 연산한다. 결과로 어떤 접근 권한을 허용/비허용 했는지 알 수 있다
                if(wfd==-1){
                    perror("open");
                    exit(1);
                }
                while((n=read(rfd, buf, BUFSIZ))>0)
                // read함수를 이용하고, 문자를 읽어 드릴 임시 공간 buf에 BUFSIZ만큼씩 저장한다
                {
                    if(write(wfd, buf, n)!=n)   // write함수를 이용하고, buf에 저장된 내용을 쓴다
                        perror("write");
                    if(n==-1)
                        perror("read");
                }
                close(rfd);
                close(wfd);
            }
            // 파일 b가 일반 파일인 경우
            // 일반 파일인 경우, 위와 같은 방식을 사용하되 디렉토리의 변화가 없다는 점에 차이가 있다 
            else{
                stat(argv[1], &st); 
                rfd = open( argv[1], O_RDONLY );
                if(rfd==-1){
                    perror("open");
                    exit(1);
                }
                wfd = open( argv[2], O_WRONLY | O_CREAT | O_TRUNC, st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
                if(wfd==-1){
                    perror("open");
                    exit(1);
                }
                while((n=read(rfd, buf, BUFSIZ))>0){
                    if(write(wfd, buf, n)!=n)
                        perror("write");
                    if(n==-1)
                        perror("read");
                }
                close(rfd);
                close(wfd);
            }
        }
        return 0;
    }

    3)     기본 기능

     

    (1)    cp [file1] [file2]

     

    (2)   cp [file1] [dri1]


    4)    옵션 기능

     

    (1)    -r : 하위 파일을 복사한다.

     

    (2)   -v : 복사의 진행 작업을 표시한다.

     

    (3)   -i : 복사한 대상이 이미 있다면, 사용자에게 확인한다.

     

    (4)   -f : 복사한 대상이 이미 있다면, 기존의 것을 삭제하고 복사한다.

     

    728x90

    댓글

kxmjhwn@gmail.com