一:背景

        看了unix/linux编程实践,跟着书上代码实现了普通文件的拷贝,看到课后习题后需要实现目录之间的拷贝,因此有了本文,我最初实现cp用了180多行代码,后来觉得很多地方可以封装,但是最后居然越封装越多达到了200多行,今晚果断再次封装,修剪了代码大概170多行,要比课后答案的要简便点。该cp可以实现普通文件的拷贝,拷贝到指定目录下,和目录直接拷贝等功能。


二:思路

    目录之间的拷贝我觉得最主要的功能就是path路径的拼装,处理好path路径问题,就很简单了。例如 /root/a.txt 拷贝到 /tmp下,那么只要传入/root/a.txt 和 /tmp/a.txt即可,那么关键就是/tmp和a.txt的拼装,在拷贝到/tmp/下你也可以先判断下目标是否有相同文件存在。若存在可以提示用户是否覆盖(这个功能我还没做)。这里主要还是字符串处理函数用的比较多,还有一个就是函数返回值得一个难点(下篇博文介绍下函数返回值)。


三:实现

    

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include 
#define BUFFERSIZE 4096
#define COPYMODE 0644

char *deal_path(char *,char *);
char *deal_path1(char *,struct dirent *);
int is_file(char *);
char *deal_with(char *,char *);
void oops(char *,char *);
int exists(char *);
void do_cp(char *,char *);
int main(int argc,char *argv[])
{
        char answer[10];
        char c;
        //设置buf
        struct stat filebuf1;
        struct stat filebuf2;
        struct stat tmpbuf;
        char *filename=NULL;
        char *filename2=NULL;
        struct dirent  *dirname;
        DIR *dir_ptr;
        //判断参数
                if(argc != 3){
                fprintf(stderr,"Usage: %s source destination\n",*argv);
                exit(1);
        }
        //判断源文件和目标文件是否相等
        if(strcmp(argv[1],argv[2]) == 0)
        {
                fprintf(stderr,"No Copy Source File equal Dest File\n");
                exit(1);
        }
        //测试文件是否可以访问

        if(access(argv[2],F_OK) == 0){
                //询问文件是否可以覆盖
                printf("Can you ovver the file (y/n):");
                scanf("%9s",&answer);
                while((c = getchar()) != EOF && c != '\n');
        }

        //判断用户的输入

        if(*answer != 'y' && *answer != 'Y'){
                fprintf(stderr,"the dst file exists,don't over\n");
                exit(1);
        }

        //判断目标是否是目录
        stat(argv[2],&filebuf2);
        stat(argv[1],&filebuf1);
        if(!S_ISDIR(filebuf1.st_mode)){
                if(S_ISDIR(filebuf2.st_mode))
                        filename = deal_path(argv[2],argv[1]);
                else
                        filename = argv[2];
        //      printf("%s\n",filename);
                do_cp(argv[1],filename);
                free(filename);
             }
             
        else{
                if(S_ISDIR(filebuf2.st_mode))
                if((dir_ptr = opendir(argv[1])) == NULL)
                        sprintf("stderr","Can't open dir %s\n",argv[1]);
                else
                        while((dirname=readdir(dir_ptr)) != NULL){
                                        filename = deal_path(argv[1],dirname->d_name);
                                        if(is_file(filename)){
                                                filename2 = deal_with(filename,argv[2]);
                                                do_cp(filename,filename2);
                                                free(filename);
                                                free(filename2);
                                        }else{
                                                free(filename);
                                                continue;
                                        }
                                }
                        }
                closedir(dir_ptr);
        }



void do_cp(char *path1,char *path2)
{
        int in_fd,out_fd,n_chars;
        char buf[BUFFERSIZE];
        if((in_fd = open(path1,O_RDONLY)) == -1)
                oops("Cannot open",path1);
        if((out_fd = open(path2,O_WRONLY|O_CREAT,COPYMODE)) == -1)
                oops("Cannot create",path2);
        //开始读取
        while((n_chars = read(in_fd,buf,BUFFERSIZE)) > 0)
                if(write(out_fd,buf,n_chars) != n_chars)
                oops("Write error to",path2);
        //判断最后是否写入
        if(n_chars == -1)
                oops("Read error from,path1","");
                        //判断最后是否写入
        if(n_chars == -1)
                oops("Read error from,path1","");
        //关闭文件
        if(close(in_fd) == -1||close(out_fd) == -1)
                oops("Error closing files","");
}


void oops(char *s1,char *s2)
{
        fprintf(stderr,"Error:%s ",s1);
        perror(s2);
        exit(1);
}


int exists(char *filename)
{
        return access(filename,F_OK);

}

int is_file(char *filename)
{
        struct stat filebuf;
        stat(filename,&filebuf);
        if(S_ISREG(filebuf.st_mode))
                return 1;
        else
                return 0;
}

char *deal_with(char *filename,char *filename2)
{
        char *file=NULL;;
        if((file = (char *)malloc(strlen(basename(filename))+strlen(filename2)+3)) == NULL)
                perror("malloc error");
                        else{
                if(filename2[strlen(filename2)-1] == '/'){
                        strcpy(file,filename2);
                        strcat(file,basename(filename));
                }else{
                        strcpy(file,filename2);
                        strcat(file,"/");
                        strcat(file,basename(filename));
                     }
        }
        return file;
}

char *deal_path(char *file,char *file2)
{
        char *filename=NULL;
        if((filename = (char *)malloc(strlen(file)+strlen(file2)+3)) == NULL)
                                perror("Malloc erro:");
        else{

        if(file[strlen(file)-1] == '/'){
                        strcpy(filename,file);
                        strcat(filename,file2);
        }else{
                        strcpy(filename,file);
                        strcat(filename,"/");
                        strcat(filename,file2);
                }
        }
        return filename;

}
用的最多的还是这个deal_path这个函数,这个函数会判断/tmp/ /tmp这个的区别,最终会将给出的
两个参数合并成一个文件路径创建并写入要拷贝的内容。

四:总结

    C语言基础很重要,最近在看C专家编程,C和指针,感触很深里面很多C语言的细节我都不知道。还有linux C编程实战中的关于结构体的字节对齐我也知之甚少。后期会用博客记录相关内容加深自己对这些内容的理解。