Linux 命令 "cp" 代码实现简介

本blog主要是模仿Linux的cp命令的功能,未实现参数,只是基础功能部分。
本文的主要目的在于练习 文件流目录流 中的函数的使用。

主要功能包括两种:

  • 源文件属性为文件,拷贝到其它文件(内容复制)或目录(作为目录子文件)
  • 源文件属性为目录,拷贝到其它目录(作为子目录存在)

其实现的流程图如下所示:
mark

本copy我们通过三个文件来实现:

  • main.c:流程的实现
  • copy.c:拷贝功能的实现
  • copy.h:必要的头文件
  1. main.c

    /*********************************************************
    *   File name:
    *   Description:
    *   Author: Jimmy_Nie
    *   Version     Modify      Time        
    *   v1.0        creat       2017-09-12
    *
    *********************************************************/
    
    #include "copy_file.h"
    
    int main(int argc, char *argv[])
    {
        //Check the arguments
        if( 3 != argc)
        {
            printf("%s(%d): arguments error!\n",__FILE__, __LINE__);
            exit(EXIT_FAILURE);
        }
    
        //check the source file is a file or a directory
        struct stat src_stat;
    
        //destination file check
        struct stat dest_stat;
    
        //If source is a file
        if( FILES == check_file(argv[1], &src_stat) )
        {
            FILE_ENUM dest_enum;
            dest_enum = check_file( argv[2], &dest_stat );
        
            char cmd[100] = "";
            char ch;
    
            switch(dest_enum)
            {
                case NOT_EXIST:
                    sprintf(cmd, "touch %s", argv[2]);
                    system( cmd );
                
                    check_file( argv[2], &dest_stat );
                    cp_file(argv[1], argv[2], &dest_stat);
                
                    break;
    
                case DIRECTORY:
                    cp_file(argv[1], argv[2], &dest_stat);
                    break;
    
                case FILES:
                    fprintf(stdout, "Overwrite the dest file %s, [y/n]\n", argv[2]);
                    ch = getchar();
                
                    if( ch == 'Y' || ch == 'y' )
                    {
                        cp_file(argv[1], argv[2], &dest_stat);
                    }
                    else
                        exit(0);
                
                    break;
                default:
                    fprintf(stderr, "%s(%d): file type error\n", __FILE__, __LINE__);
            }
        }
    
        //If source file is a directory
        else if( DIRECTORY == check_file(argv[1], &dest_stat) )
        {
            FILE_ENUM dest_enum;
            dest_enum = check_file( argv[2], &dest_stat );
        
            char cmd[100] = "";
    
            switch(dest_enum)
            {
                case NOT_EXIST:
                    sprintf(cmd, "mkdir -p %s", argv[2]);
                    system( cmd );
                    cp_dir(argv[1], argv[2] );
                    break;
    
                case DIRECTORY:
                    cp_dir(argv[1], argv[2]); 
                    break;
    
                case FILES:
                    fprintf(stderr, "Can't copy a directory to a file\n");
                    exit(EXIT_FAILURE);
                    break;
                
                default:
                    fprintf(stderr, "%s(%d): file type error\n", __FILE__, __LINE__);
                    break;
            }
        }
    
        return 0;
    }
    
  2. copy.c

    #include "copy_file.h"
    
    
    FILE_ENUM check_file(char *var, struct stat *st)
    {
        if( stat(var, st) )    //if stat function error(renturn nonzero)
        {
            if( ENOENT == errno)    //No such file or directory
            {
                return NOT_EXIST;
            }
            else
            {
                perror("stat");
                exit(EXIT_FAILURE);
            }
        }
    
        else    // stat() ok, no error
        {
            //check file attr(dir or file)
            if( S_ISDIR(st->st_mode ))  
                return DIRECTORY;
            else if( S_ISREG(st->st_mode) )
                return FILES;
            else
            {
                fprintf(stderr, "%s(%d):file type error", __FILE__ , __LINE__);
                exit(EXIT_FAILURE);
            }
        }
    }
    
    
    //-----------------------------------------------------
    
    int cp_file(char *src_var, char *dest_var, struct stat *st)
    {
        FILE *src = NULL;
        FILE *dest = NULL;
        
        if( S_ISREG(st->st_mode) )  //if dest is file
        {
            //1. open src and dest file
            if( NULL == (src = fopen(src_var, "r")) )
            {
                perror("fopen");
                exit(EXIT_FAILURE);
            }
    
            if( NULL == (dest = fopen(dest_var, "w+")) )
            {
                perror("fopen");
                exit(EXIT_FAILURE);
            }
            
            //2. copy the context from src to dest
            char buf[1024];
            int num;
        
            while(1)
            {
                // if at the end of file or an error occured
                if( 1024 != (num = fread(buf, 1,1024, src)))
                {
                    if( !feof(src))
                    {
                        perror("fread");
                        exit(EXIT_FAILURE);
                    }
                    
                    else
                    {
                        fwrite(buf, 1, num, dest);
                        fclose(dest);   //3. close dest file
                        break;
                    }
                }
                fwrite(buf, 1, 1024, dest);
                
            }
            
            //3. close src file
            fclose(src);
            return 0;
        }
    
        if( S_ISDIR(st->st_mode) )
        {
            char buf[100]="";
            
            //make the relative path to absolute path
            strncpy(buf, dest_var, sizeof(dest_var));
            strcat(buf, src_var);
            
            //if dest file doesn't exist, creat it first
            char cmd[100]="";
            sprintf(cmd, "touch %s",buf);
            system(cmd);
    
            struct stat new_dest_stat;
                    
            if( stat(buf, &new_dest_stat))
            {
                perror("stat");
                exit(EXIT_FAILURE);
            }
            
            cp_file(src_var, buf, &new_dest_stat);
        }
    
        return 0;
    }
    
    //----------------------------------------------
    //if src file is a dir
    int cp_dir(char *src, char *dest)
    {
        DIR *dirp = NULL;
        
        //1. open the dir
        if( NULL == (dirp = opendir(src)) )
        {
            perror("opendir");
            exit(EXIT_FAILURE);
        }
    
        struct dirent *entp = NULL;
        
        //2. read the dir
        while( NULL != (entp = readdir(dirp)))      //read the dir context
        {
            if( 0 == (strcmp(entp->d_name,"..")) || 0 == (strcmp(entp->d_name, ".")))
            {
                continue;
            }
    
            char src_buf[100] = "";
            char dest_buf[100] = "";
    
            sprintf(src_buf, "%s/%s\0", src, entp->d_name);
            sprintf(dest_buf, "%s/%s\0", dest, entp->d_name); 
       
    
            struct stat src_stat;
            
            if( stat(src_buf,&src_stat) )
            {
                perror("stat");
                exit(EXIT_FAILURE);
            }
    
            if( S_ISREG(src_stat.st_mode) )
            {
                cp_file(src_buf, dest_buf, &src_stat);
            }
    
            else if( S_ISDIR(src_stat.st_mode) )
            {
                if( -1 == mkdir(dest_buf, src_stat.st_mode) )
                {
                    perror("mkdir");
                    exit(EXIT_FAILURE);
                }
                
                cp_dir(src_buf, dest_buf);  //if subdir, recursive call itself
            }
        }
        
        return 0;
    }
    
  3. copy.h

    #ifndef _COPY_H_
    #define _COPY_H_
    
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <errno.h>
    #include <string.h>
    #include <dirent.h>
    #include <stdlib.h>
    
    
    typedef enum
    {
        NOT_EXIST=1,
        DIRECTORY,
        FILES
    }FILE_ENUM;
    
    extern FILE_ENUM check_file(char *var, struct stat *st);    //检查文件类型
    
    extern int cp_file(char *src_var, char *dest_var, struct stat *st);     //copy文件
    
    extern int cp_dir(char *src, char *dest);       //copy目录
    
    #endif
    
posted @ 2017-09-12 14:20  Jimmy_Nie  阅读(5108)  评论(0编辑  收藏  举报