当前位置:   article > 正文

嵌入式 C语言实现进度条以及实现带进度条的CP命令等编程示例收集二_human_readable_time c语言

human_readable_time c语言

1、脚本shell

root@u12d32:/home/kongjun/work/hi_test/time_count_down# cat time_test.sh
#!/bin/bash
COUNTER=0
_R=0
_C=`tput cols`
_PROCEC=`tput cols`
tput cup $_C $_R
printf "["
while [ $COUNTER -lt 100 ]
do
COUNTER=`expr $COUNTER + 1`
sleep 0.1
printf "=>"
_R=`expr $_R + 1`
_C=`expr $_C + 1`
tput cup $_PROCEC 101
printf "]%d%%" $COUNTER
tput cup $_C $_R
done
printf "\n"

2、C编程

Linux下复制文件的命令cp非常强大,就是有一点不是很好:没有进度提示。尤其是在复制很多大文件时,控制台仅仅停在那里什么信息都没有让人非常不爽。

当然可以通过shell脚本实现进度提示,不过我喜欢直接一点的方式:用C程序解决。

1.遍历

想要知道进度首先要统计源文件的个数和大小,然后再复制,所以需要对源文件做两次遍历。当然也可以只遍历一遍,把遍历的结果保存在内存中这样后续操作就不用再遍历了,不过我认为这样做是没有必要的。

遍历函数 walk() 接受一个函数指针参数opp_func,walk()保证对源的每一项(可能是文件也可能是目录)调用一次opp_func. 这样可以通过设置不同的opp_func用同一个遍历函数完成不同的功能。后面的代码实现了3个opp_func分别是 统计函数 sum_up, 演示函数 demo, 和真正的执行函数  action.

2.进度信息显示

进度提示要在Linux的控制台的同一行覆盖刷新,否则就不美观了.这里需要用到一个小技巧:printf 输出控制台控制字符 \r\033[K 用来把光标移动到当前行行首(不换行)并清空当前行的内容。

进度提示的刷新时机也是一个问题:单独创建一个线程用来刷新进度信息未免小题大做,如果每复制一点数据就刷新一次又过于频繁,用定时器则刚刚好。不过定时器也有定时器的问题,在附录440行提示用户是否覆盖已经存在的文件时,需要等待用户输入,此时正在运行的定时器会导致中断重启使getchar函数出错返回,需要避免这种情况。

3.安装

把附录中的源代码保存成文件xcp.c

gcc xcp.c -o xcp

sudo xcp xcp /usr/bin

xcp

3.运行结果

在我的机器上(ubuntu 12.04LTS)是这样:

运行中:

ted@ted-ThinkPad-T410:~/movies$ xcp ./BBC.野性澳洲/ ./BBC.Australia -r
1 directories 19 files 3.43GB detected.
1 directories 9 files 494.49MB copied, 14%, 22.48MB/s -

完成:

ted@ted-ThinkPad-T410:~/movies$ xcp ./BBC.野性澳洲/ ./BBC.Australia -r
1 directories 19 files 3.43GB detected.
1 directories 19 files 3.43GB copied, 2m 30s, 23.42MB/s.

有目录个数,文件个数,字节数,用时,平均速度,复制过程中还有一个字符的小动画。嗯,差不多了。

=== 2013.11.1更新

1.支持多源文件,即可以 “xcp file1 dir1 file2 dir2... dest” 这样的方式拷贝多个文件/目录到同一个目录中。

2. 只有在递归复制目录(参数-r)时才判断是否循环复制。

=====================我是分割线=======================

PS,鄙视只贴代码不写说明的行为。

附录: xcp.c

=====================我也是=======================

  1. #define _FILE_OFFSET_BITS 64 
  2.  
  3. #include <unistd.h> 
  4. #include <stdio.h> 
  5. #include <stdlib.h> 
  6. #include <sys/stat.h> 
  7. #include <sys/types.h> 
  8. #include <dirent.h> 
  9. #include <string.h> 
  10. #include <time.h> 
  11. #include <signal.h> 
  12. #include <sys/time.h> 
  13. #include <stdarg.h> 
  14. #include <errno.h> 
  15.  
  16. /*
  17. * 复制文件可以显示进度
  18. *
  19. * 两个思路:遍历文件一次,把文件名记录在一个列表,后续操作直接从列表中得到文件名
  20. * 或者遍历两遍,第一次统计,第二次执行
  21. *
  22. * 关于进度条
  23. * 1. 用定时器每隔1秒刷新一次要注意函数重入的问题
  24. * 2. 两个线程工作线程统计/拷贝主线程刷新状态,似乎小题大做了
  25. * 3. 一个线程有变化时刷新,这样就无法现实动画
  26. *
  27. * TODO
  28. * 2013-10-22
  29. * 1. 添加命令行选项的处理。
  30. * 2. 添加文件无法访问/目录无法创建或者文件/目录已经存在的情况的处理。
  31. * 3. 如果没有任何文件成功复制时的提示信息BUG(在有文件detected的情况下)。
  32. * 4. 复制文件,目标是已经存在的目录名时自动添加文件名而不是直接复制。
  33. * 5. 结束时用human_time 来显示用去的时间。
  34. *
  35. * 2013-10-23
  36. * 1. 统计阶段也要显示动画
  37. *
  38. * 2013-10-24
  39. * 1. overwrite 提示后等待用户输入和定时器冲突的问题
  40. *
  41. * 2013-10-29
  42. * 1. 多源拷贝在主函数做个循环,都要补齐文件名,判断是否存在等.
  43. *
  44. */ 
  45.  
  46. #define BOOL int 
  47. #define TRUE 1 
  48. #define FALSE 0 
  49. #define MAX_FMTSTR_LENGTH 2048  /*传递给print_message函数的格式字符串最大长度*/ 
  50. #define COPY_BUF_SIZE 4096 /*复制文件时每次读取的长度*/ 
  51. #define MAX_PATH_LENGTH (PATH_MAX + 1)  /*路径的最大长度*/ 
  52. #define GBYTES (1024 * 1024 * 1024) 
  53. #define MBYTES (1024 * 1024) 
  54. #define KBYTES 1024 
  55. #define HOUR (60 * 60) 
  56. #define MINUTE 60 
  57. #define OPP_CONTINUE 0 
  58. #define OPP_SKIP 1 
  59. #define OPP_CANCEL 2 /*walk 函数终止遍历退出*/ 
  60.  
  61. #define MSGT_PROMPT 0 
  62. #define MSGT_WARNING 1 
  63. #define MSGT_ERROR 2 
  64. #define MSGT_VERBOSE 3 
  65. #define MSGT_DEMO 4 
  66.  
  67. /*启用大文件支持*/ 
  68. //#define _LARGEFILE64_SOURCE 
  69. //#define _FILE_OFFSET_BITS 64 
  70.  
  71. //#ifdef _LARGEFILE64_SOURCE 
  72. //#define stat stat64 
  73. //#define fopen fopen64 
  74. //#define fread fread64 
  75. //#define fwrite fwrite64 
  76. //#endif 
  77.  
  78. typedefint (*each_opp_t)(constchar*,constchar*,constchar*,conststruct stat* st); 
  79. typedefvoid (*sig_handler_t)(int); 
  80.  
  81. /* 全局变量 */ 
  82. int sum_file = 0; 
  83. int sum_dir = 0; 
  84. longlong sum_size = 0; 
  85. int copied_file = 0; 
  86. int copied_dir = 0; 
  87. longlong copied_size = 0; 
  88. time_t copy_start_time = 0; 
  89. BOOL status_pause = FALSE; 
  90. BOOL opt_d = FALSE; 
  91. BOOL opt_f = FALSE; 
  92. BOOL opt_q = FALSE; 
  93. BOOL opt_r = FALSE; 
  94. BOOL opt_v = FALSE; 
  95.  
  96. /*显示为可读数字*/ 
  97. char* human_size(longlong s,char *hs) 
  98.    if(s >= GBYTES) 
  99.     { 
  100.         sprintf(hs,"%.2fGB", (s * 1.0) / GBYTES); 
  101.     } 
  102.    elseif(s >= 1024 * 1024) 
  103.     { 
  104.         sprintf(hs,"%.2fMB", (s * 1.0) / MBYTES); 
  105.     } 
  106.    elseif(s > 1024) 
  107.     { 
  108.         sprintf(hs,"%.2fKB", (s * 1.0) / KBYTES); 
  109.     } 
  110.    else 
  111.     { 
  112.         sprintf(hs,"%lldB", s); 
  113.     } 
  114.  
  115.     return hs; 
  116.  
  117. /* human readable time */ 
  118. char* human_time(time_t t, char *text) 
  119.     int h,m,s; 
  120.     h = (int)(t / HOUR); 
  121.     m = (int)((t % HOUR) / MINUTE); 
  122.     s = (int)(t % HOUR % MINUTE); 
  123.  
  124.    if(h > 0) 
  125.     { 
  126.         sprintf(text,"%dh %dm %ds", h, m, s); 
  127.     } 
  128.    elseif(m > 0) 
  129.     { 
  130.         sprintf(text,"%dm %ds", m, s); 
  131.     } 
  132.    else 
  133.     { 
  134.         sprintf(text,"%ds", s); 
  135.     } 
  136.    return text; 
  137.  
  138. /*
  139. * 先清除状态文字然后在输出信息
  140. * 1. 状态文字总是在当前行输出不换行
  141. * 2. printerror只能在状态文字被显示之后输出,即定时器被安装之后使用。
  142. */ 
  143. void print_message(int t, constchar* fmt, ...) 
  144.    char real_fmt[MAX_FMTSTR_LENGTH]; 
  145.     va_list args; 
  146.     va_start(args, fmt); 
  147.  
  148.    if(opt_q && (t == MSGT_WARNING || t == MSGT_ERROR)) 
  149.     { 
  150.        /*quiet, don't output warning nor error message*/ 
  151.     } 
  152.    else 
  153.     { 
  154.         sprintf(real_fmt,"\r\033[K%s", fmt); 
  155.         vprintf(real_fmt, args); 
  156.     } 
  157.  
  158. /*连接目录字符串,主要处理末尾/的问题,frt snd 两个参数不能同时为空那样没有意义*/ 
  159. char* make_path(char *dest, constchar *frt,constchar *snd) 
  160.    if(NULL == frt || strlen(frt) == 0) 
  161.     { 
  162.         sprintf(dest,"%s", snd); 
  163.     } 
  164.    elseif(NULL == snd || strlen(snd) == 0) 
  165.     { 
  166.         sprintf(dest,"%s", frt); 
  167.     } 
  168.    else 
  169.     { 
  170.        if(frt[strlen(frt) - 1] =='/'
  171.         { 
  172.             sprintf(dest,"%s%s", frt, snd); 
  173.         } 
  174.        else 
  175.         { 
  176.             sprintf(dest,"%s/%s", frt, snd); 
  177.         } 
  178.     } 
  179.     return dest; 
  180.  
  181. /*显示进度条*/ 
  182. void show_status(BOOL finish) 
  183.     int percent,i; 
  184.    char animate[4]; 
  185.     static int animate_pos = -1; 
  186.    time_t cur_time; 
  187.     char speed[512]; 
  188.    char hs[512]; 
  189.     long long sp = 0; 
  190.    char ht[512]; 
  191.  
  192.     animate[0] ='-'
  193.     animate[1] ='/'
  194.     animate[2] ='|'
  195.     animate[3] ='\\'
  196.  
  197.     time(&cur_time); 
  198.    if(sum_size == 0) 
  199.     { 
  200.         percent = 0; 
  201.     } 
  202.    else 
  203.     { 
  204.         percent = (copied_size * 1.0 / sum_size) * 100; 
  205.     } 
  206.  
  207.     if(cur_time > copy_start_time) 
  208.     { 
  209.         sp = copied_size / (cur_time - copy_start_time); 
  210.         sprintf(speed,"%s/s", human_size(sp, hs)); 
  211.     } 
  212.    else 
  213.     { 
  214.         sprintf(speed,"-"); 
  215.     } 
  216.  
  217.     human_size(copied_size, hs); 
  218.    if(finish) 
  219.     { 
  220.         printf("\r\033[K%d directories %d files %s copied, %s, %s.\n",\ 
  221.             copied_dir, copied_file, hs, human_time(cur_time - copy_start_time, ht), speed); 
  222.     } 
  223.     else 
  224.     { 
  225.         printf("\r\033[K%d directories %d files %s copied, %d%%, %s %c ", \ 
  226.             copied_dir, copied_file, hs, percent, speed, animate[animate_pos = (animate_pos + 1) % 4]); 
  227.     } 
  228.     fflush(stdout); 
  229.  
  230. /*定时器处理函数*/ 
  231. void timer_handler(int signum) 
  232.    if(!status_pause) 
  233.     { 
  234.         show_status(FALSE); 
  235.     }    
  236.  
  237. /*安装/删除定时器*/ 
  238. void install_timer(size_t sec, sig_handler_t  handler_func) 
  239.     struct sigaction act; 
  240.    struct itimerval tick; 
  241.  
  242.    if(sec > 0) 
  243.     { 
  244.         act.sa_handler = handler_func; 
  245.     } 
  246.    else 
  247.     { 
  248.         act.sa_handler = SIG_DFL; 
  249.     } 
  250.     sigemptyset(&act.sa_mask); 
  251.     act.sa_flags = 0; 
  252.     sigaction(SIGALRM, &act, 0); 
  253.      
  254.     memset(&tick, 0,sizeof(tick)); 
  255.     tick.it_value.tv_sec = sec; 
  256.     tick.it_value.tv_usec = 0; 
  257.     tick.it_interval.tv_sec = sec; 
  258.     tick.it_interval.tv_usec = 0; 
  259.  
  260.     setitimer(ITIMER_REAL, &tick, 0); 
  261.  
  262. /*
  263. * 遍历函数
  264. * 遍历函数只保证源文件/文件夹的每一项都调用一次opp函数
  265. * 由opp函数的返回值决定是否继续扫描
  266. * 采用“串烧”式程序风格
  267. * 只有一种情况下返回值为FALSE:opp 函数返回OPP_CANCEL
  268. */ 
  269. int walk(constchar* path_from,constchar* path_to,constchar* path_tree, each_opp_t opp) 
  270.    struct stat st; 
  271.     DIR* dir = NULL; 
  272.    struct dirent *entry = NULL; 
  273.     char path_tree_new[MAX_PATH_LENGTH]; 
  274.    char path_from_full[MAX_PATH_LENGTH]; 
  275.     int ret_val = OPP_CONTINUE; 
  276.  
  277.     /*获得源的属性*/ 
  278.     make_path(path_from_full, path_from, path_tree); 
  279.     if(-1 == stat(path_from_full, &st)) 
  280.     { 
  281.         print_message(MSGT_ERROR,"can't access \"%s\".\n", path_from_full); 
  282.        return OPP_SKIP; 
  283.     } 
  284.  
  285.     /*调用一次处理函数,处理当前项*/ 
  286.    if((ret_val = opp(path_from, path_to, path_tree, &st)) != OPP_CONTINUE) 
  287.     { 
  288.        return ret_val; 
  289.     } 
  290.              
  291.     /*如果是目录,则浏览目录,否则结束*/ 
  292.    if(!S_ISDIR(st.st_mode)) 
  293.     { 
  294.        return OPP_CONTINUE; 
  295.     } 
  296.  
  297.     /*打开目录*/ 
  298.    if(!(dir = opendir(path_from_full))) 
  299.     { 
  300.         print_message(MSGT_ERROR,"can't open directory \"%s\".\n", path_from_full); 
  301.        return OPP_SKIP; 
  302.     } 
  303.  
  304.    /*浏览目录*/ 
  305.     while((entry = readdir(dir)) != NULL) 
  306.     { 
  307.        /*构建path_tree_new*/ 
  308.         make_path(path_tree_new, path_tree, entry->d_name); 
  309.         make_path(path_from_full, path_from, path_tree_new); 
  310.      
  311.        /*无法访问 skip*/ 
  312.        if(-1 == stat(path_from_full, &st)) 
  313.         { 
  314.             print_message(MSGT_ERROR,"skip, can't access \"\".\n", path_from_full); 
  315.            continue
  316.         } 
  317.  
  318.        /* 忽略 . 和 .. */ 
  319.        if(S_ISDIR(st.st_mode) && (strcmp(".", entry->d_name) == 0 || strcmp("..", entry->d_name) == 0)) 
  320.         { 
  321.            continue
  322.         } 
  323.  
  324.        if(S_ISDIR(st.st_mode) && opt_r) 
  325.         { 
  326.          /*递归处理子目录*/ 
  327.            if(walk(path_from, path_to, path_tree_new, opp) == OPP_CANCEL) 
  328.             { 
  329.                 ret_val = OPP_CANCEL; 
  330.                break
  331.             } 
  332.         } 
  333.        else 
  334.         { 
  335.            /*处理函数处理一个子项*/ 
  336.            if(opp(path_from, path_to, path_tree_new, &st) == OPP_CANCEL) 
  337.             { 
  338.                 ret_val = OPP_CANCEL; 
  339.                break
  340.             } 
  341.         } 
  342.     } 
  343.     closedir(dir); 
  344.    return ret_val; 
  345.  
  346. /* 统计函数 */ 
  347. int sum_up(constchar* path_from,constchar* path_to,constchar* path_tree,conststruct stat* st) 
  348.    if(S_ISREG(st->st_mode)) 
  349.     { 
  350.         sum_file++; 
  351.         sum_size += st->st_size; 
  352.     } 
  353.     else if(S_ISDIR(st->st_mode)) 
  354.     { 
  355.         sum_dir++; 
  356.     } 
  357.     else 
  358.     { 
  359.         print_message(MSGT_WARNING,"skip:%s\n", path_tree); 
  360.     } 
  361.     return OPP_CONTINUE; 
  362.  
  363. /*demo*/ 
  364. int demo(constchar* path_from,constchar* path_to,constchar* path_tree,conststruct stat* st) 
  365.     char path_from_full[MAX_PATH_LENGTH]; 
  366.    char path_to_full[MAX_PATH_LENGTH]; 
  367.      
  368.     make_path(path_from_full, path_from, path_tree); 
  369.     make_path(path_to_full, path_to, path_tree); 
  370.  
  371.     if(S_ISREG(st->st_mode)) 
  372.     { 
  373.         print_message(MSGT_DEMO,"cp \"%s\" -> \"%s\".\n", path_from_full, path_to_full); 
  374.     } 
  375.     else if(S_ISDIR(st->st_mode)) 
  376.     { 
  377.         print_message(MSGT_DEMO,"mkdir \"%s\".\n", path_to_full); 
  378.     } 
  379.     else 
  380.     { 
  381.         print_message(MSGT_WARNING,"skip \"%s\"\n", path_tree); 
  382.     } 
  383.     return OPP_CONTINUE; 
  384.  
  385.  
  386. /* 操作 */ 
  387. int action(constchar* path_from,constchar* path_to,constchar* path_tree,conststruct stat* st) 
  388.    int ret_val = OPP_CONTINUE; 
  389.     char path_from_full[MAX_PATH_LENGTH]; 
  390.    char path_to_full[MAX_PATH_LENGTH]; 
  391.     size_t rd, wr, swr; 
  392.    char buf[COPY_BUF_SIZE]; 
  393.     FILE *src_file, *dest_file;  
  394.    BOOL over_write = FALSE; 
  395.     int cmd; 
  396.    BOOL skip = FALSE; 
  397.     struct stat st_dest; 
  398.      
  399.     make_path(path_from_full, path_from, path_tree); 
  400.     make_path(path_to_full, path_to, path_tree); 
  401.  
  402.    if(S_ISREG(st->st_mode)) 
  403.     { 
  404.        /* regular file */ 
  405.        if(opt_v) 
  406.         { 
  407.             print_message(MSGT_VERBOSE,"cp \"%s\" -> \"%s\".\n", path_from_full, path_to_full); 
  408.         } 
  409.  
  410.        if(strcmp(path_from_full, path_to_full) == 0) 
  411.         { 
  412.             ret_val = OPP_SKIP; 
  413.             print_message(MSGT_ERROR,"skip, \"%s\" and \"%s\" are the same.\n", path_from_full, path_to_full); 
  414.         } 
  415.        elseif(src_file = fopen(path_from_full,"rb")) 
  416.         { 
  417.            do 
  418.             { 
  419.                /*询问是否可以覆盖*/ 
  420.                if(!opt_f && 0 == access(path_to_full, F_OK))  
  421.                 { 
  422.                    /* 应该先停止计时器,否则在等待用户输入时如果有定时器被触发,会导致 getchar()停止等待并返回 EOF*/ 
  423.                     status_pause = TRUE; 
  424.                     print_message(MSGT_PROMPT,"overwrite \"%s\"? ([y] yes,[n] no, [a] all, [c] cancel)", path_to_full); 
  425.                    while(1) 
  426.                     { 
  427.                         cmd = getchar(); 
  428.  
  429.                        /*中断重启 由于有一个定时器正在运行,在等待用户输入的过程中getchar会被中断返回*/ 
  430.                        if(-1 == cmd)continue
  431.  
  432.                        /*skip useless chars of inputed line*/ 
  433.                        if(cmd !='\n'
  434.                         { 
  435.                            while(getchar() !='\n'); 
  436.                         } 
  437.  
  438.                        if('y' == cmd) 
  439.                         { 
  440.                            break
  441.                         } 
  442.                        elseif('n' == cmd) 
  443.                         { 
  444.                             skip = TRUE; 
  445.                             ret_val = OPP_SKIP; 
  446.                            break
  447.                         } 
  448.                        elseif('a' == cmd) 
  449.                         { 
  450.                             opt_f = TRUE; 
  451.                            break
  452.                         } 
  453.                        elseif('c' == cmd) 
  454.                         { 
  455.                            /* skip current file and cancel walk progress */ 
  456.                             skip = TRUE; 
  457.                             ret_val = OPP_CANCEL; 
  458.                            break
  459.                         } 
  460.                        else 
  461.                         { 
  462.                            /* unknown command */ 
  463.                         } 
  464.                     } 
  465.                     status_pause = FALSE; 
  466.                  
  467.                    /* ship current file */ 
  468.                    if(skip)break
  469.                 } 
  470.                  
  471.                /* open target file for write */ 
  472.                if(dest_file = fopen(path_to_full,"wb")) 
  473.                 { 
  474.                    while((rd = fread(buf, 1, COPY_BUF_SIZE, src_file)) > 0) 
  475.                     { 
  476.                         wr = 0; 
  477.                        do 
  478.                         { 
  479.                             swr = fwrite(buf + wr, 1, rd - wr, dest_file); 
  480.                             wr += swr; 
  481.                         } 
  482.                        while(swr > 0 && wr < rd); 
  483.                         copied_size += rd; 
  484.                      
  485.                        if(wr != rd) 
  486.                         { 
  487.                            /*只有部分文件被复制也视为成功因为文件系统中已经有这个文件的记录了*/ 
  488.                             print_message(MSGT_ERROR,"write file error %s.\n", path_to_full); 
  489.                            break
  490.                         } 
  491.                     } 
  492.                     fclose(dest_file); 
  493.                     chmod(path_to_full, st->st_mode); 
  494.                     copied_file++; 
  495.                 } 
  496.                else 
  497.                 { 
  498.                     ret_val = OPP_SKIP; 
  499.                     print_message(MSGT_ERROR,"skip, can't open target file \"%s\"\n", path_to_full); 
  500.                 } 
  501.             }while(0); 
  502.  
  503.             fclose(src_file); 
  504.         } 
  505.        else 
  506.         { 
  507.             ret_val = OPP_SKIP; 
  508.             print_message(MSGT_ERROR,"skip, can't open source file \"%s\"\n", path_from_full); 
  509.         } 
  510.     } 
  511.     else if(S_ISDIR(st->st_mode)) 
  512.     { 
  513.        /* directories */ 
  514.        if(opt_v) 
  515.         { 
  516.             print_message(MSGT_VERBOSE,"mkdir \"%s\"\n", path_to_full); 
  517.         } 
  518.  
  519.        if(0 == stat(path_to_full, &st_dest)) 
  520.         { 
  521.            /*path_to_full already exist*/ 
  522.            if(S_ISDIR(st_dest.st_mode)) 
  523.             { 
  524.                 copied_dir++; 
  525.             } 
  526.            else 
  527.             { 
  528.                 ret_val = OPP_SKIP; 
  529.                 print_message(MSGT_WARNING,"skip, \"%s\" exists and it's not a directory.\n", path_to_full); 
  530.             } 
  531.         } 
  532.        else 
  533.         { 
  534.            /*try to make a new directory*/ 
  535.            if(0 == mkdir(path_to_full, st->st_mode)) 
  536.             { 
  537.                 chmod(path_to_full, st->st_mode); 
  538.                 copied_dir++; 
  539.             } 
  540.            else 
  541.             { 
  542.                 ret_val = OPP_SKIP; 
  543.                 print_message(MSGT_ERROR,"skip, \"%s\" mkdir failed.\n", path_to_full); 
  544.             } 
  545.         } 
  546.     } 
  547.     else 
  548.     { 
  549.         ret_val = OPP_SKIP; 
  550.         print_message(MSGT_WARNING,"skip, \"%s\" is not a file nor directory.\n", path_to_full); 
  551.     } 
  552.  
  553.     return ret_val; 
  554.  
  555. /*使用说明*/ 
  556. void usage() 
  557.     printf("xcp - by Q++ Studio 2013-11-1\n"); 
  558.     printf("description:cp with progress\n"); 
  559.     printf("synopsis: xcp [OPTIONS] src1 [src2 ... srcn] dest\n\n"); 
  560.     printf("[OPTIONS]\n"); 
  561.     printf("-r:recusive copy sub directories.\n"); 
  562.     printf("-f:force overwrite without prompt.\n"); 
  563.     printf("-q:quiet no warning/error message.\n"); 
  564.     printf("-d:demo,do not copy,output message only.\n"); 
  565.     printf("-v:verbos output.\n"); 
  566.     printf("-h:show usage message.\n"); 
  567.  
  568. /*禁止循环复制,即目标文件/文件夹不能包含在源文件/文件夹中*/ 
  569. BOOL is_self_copy(constchar* src,constchar* dest) 
  570.     /*严格的做法应该先把src和dest都转化为绝对路径然后在比较,不过
  571.      *Linux下的相对路径比较麻烦有 ~ ./ ../ ../../ 等...
  572.     */ 
  573.     char c; 
  574.    char* sub = strstr(dest, src); 
  575.  
  576.    if(sub) 
  577.     { 
  578.         c = sub[strlen(src)]; 
  579.        return c =='\0' || c =='/' || src[strlen(src) - 1] =='/'
  580.     } 
  581.     else 
  582.     { 
  583.        return FALSE; 
  584.     } 
  585.  
  586. /*主函数,做两次遍历*/ 
  587. int main(int argc, char* args[]) 
  588.    int i = 0; 
  589.     char *path_from = NULL, *path_to = NULL, *file_name = NULL; 
  590.    char path_to_fixed[MAX_PATH_LENGTH]; 
  591.     struct stat st_src, st_dest; 
  592.    char human_readable_size[200]; 
  593.     int opt; 
  594.    BOOL help = FALSE; 
  595.      
  596.    while((opt = getopt(argc, args,"rfqdhv")) != -1) 
  597.     { 
  598.        switch(opt) 
  599.         { 
  600.            case'r'
  601.                 opt_r = TRUE; 
  602.                break
  603.            case'f'
  604.                 opt_f = TRUE; 
  605.                break
  606.            case'q'
  607.                 opt_q = TRUE; 
  608.                break
  609.            case'd'
  610.                 opt_d = TRUE; 
  611.                break
  612.            case'h'
  613.                 help = TRUE; 
  614.                break
  615.            case'v'
  616.                 opt_v = TRUE; 
  617.                break
  618.            case'?'
  619.                 printf("unknown option: %c\n", optopt); 
  620.                 help = TRUE; 
  621.                break
  622.            default
  623.                break
  624.         } 
  625.     } 
  626.      
  627.     if(help || optind + 2 > argc) 
  628.     { 
  629.         usage(); 
  630.        return 1; 
  631.     } 
  632.  
  633.     /* 第一次遍历:统计 */ 
  634.     sum_file = 0; 
  635.     sum_dir = 0; 
  636.     sum_size = 0; 
  637.  
  638.     path_to = args[argc - 1]; 
  639.     for(i = optind; i < argc -1; ++i) 
  640.     { 
  641.         path_from = args[i]; 
  642.         walk(path_from, path_to, NULL, sum_up); 
  643.     } 
  644.  
  645.     if(sum_file == 0 && sum_dir == 0) 
  646.     { 
  647.         printf("nothing found.\n"); 
  648.     } 
  649.     else 
  650.     { 
  651.         human_size(sum_size, human_readable_size); 
  652.         printf("%d directories %d files %s detected.\n", sum_dir, sum_file, human_readable_size); 
  653.          
  654.        /* 第二次遍历:执行*/ 
  655.         copied_file = 0; 
  656.         copied_dir = 0; 
  657.         copied_size = 0; 
  658.  
  659.        // 设置一个定时器,每隔1秒显示一下进度  
  660.         time(©_start_time); 
  661.         show_status(FALSE); 
  662.         install_timer(1, timer_handler); 
  663.  
  664.        for(i = optind; i < argc - 1; ++i) 
  665.         { 
  666.             path_from = args[i]; 
  667.             path_to = args[argc - 1]; 
  668.  
  669.            /*源是否存在*/ 
  670.            if(-1 == stat(path_from, &st_src)) 
  671.             { 
  672.                     print_message(MSGT_ERROR,"\"%s\" doesn't exist.\n", path_from); 
  673.                    continue
  674.             } 
  675.          
  676.            /*
  677.             * 如果源是文件而且目标是已经存在的目录,则自动补齐文件名
  678.             * 如果目标是已经存在的文件,先判断是否指向同一个文件 inode number
  679.             */ 
  680.            if(S_ISREG(st_src.st_mode)) 
  681.             { 
  682.                if((0 == stat(path_to, &st_dest)) && S_ISDIR(st_dest.st_mode)) 
  683.                 { 
  684.                     file_name = strrchr(path_from,'/'); 
  685.                     path_to = make_path(path_to_fixed, path_to, file_name ? file_name + 1 : path_from); 
  686.                 } 
  687.             } 
  688.            elseif(S_ISDIR(st_src.st_mode)) 
  689.             { 
  690.                if(opt_r && is_self_copy(path_from, path_to)) 
  691.                 { 
  692.                    /*源是目录时要防止循环复制*/ 
  693.                     print_message(MSGT_ERROR,"can't xcp \"%s\" -> \"%s\"\n", path_from, path_to); 
  694.                    continue
  695.                 } 
  696.             } 
  697.            else 
  698.             { 
  699.                 print_message(MSGT_WARNING,"skip \"%s\" not a file nor a directory.\n", path_from); 
  700.                continue
  701.             } 
  702.  
  703.            if(opt_d) 
  704.             { 
  705.                 walk(path_from, path_to, NULL, demo);  
  706.             } 
  707.            else 
  708.             { 
  709.                 walk(path_from, path_to, NULL, action); 
  710.             } 
  711.         } 
  712.         install_timer(0, NULL); 
  713.         show_status(TRUE); 
  714.     } 
  715.  
  716.    return 0; 
  1. #define _FILE_OFFSET_BITS 64
  2. #include <unistd.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <sys/stat.h>
  6. #include <sys/types.h>
  7. #include <dirent.h>
  8. #include <string.h>
  9. #include <time.h>
  10. #include <signal.h>
  11. #include <sys/time.h>
  12. #include <stdarg.h>
  13. #include <errno.h>
  14. /*
  15. * 复制文件可以显示进度
  16. *
  17. * 两个思路:遍历文件一次,把文件名记录在一个列表,后续操作直接从列表中得到文件名
  18. * 或者遍历两遍,第一次统计,第二次执行
  19. *
  20. * 关于进度条
  21. * 1. 用定时器每隔1秒刷新一次要注意函数重入的问题
  22. * 2. 两个线程工作线程统计/拷贝主线程刷新状态,似乎小题大做了
  23. * 3. 一个线程有变化时刷新,这样就无法现实动画
  24. *
  25. * TODO
  26. * 2013-10-22
  27. * 1. 添加命令行选项的处理。
  28. * 2. 添加文件无法访问/目录无法创建或者文件/目录已经存在的情况的处理。
  29. * 3. 如果没有任何文件成功复制时的提示信息BUG(在有文件detected的情况下)。
  30. * 4. 复制文件,目标是已经存在的目录名时自动添加文件名而不是直接复制。
  31. * 5. 结束时用human_time 来显示用去的时间。
  32. *
  33. * 2013-10-23
  34. * 1. 统计阶段也要显示动画
  35. *
  36. * 2013-10-24
  37. * 1. overwrite 提示后等待用户输入和定时器冲突的问题
  38. *
  39. * 2013-10-29
  40. * 1. 多源拷贝在主函数做个循环,都要补齐文件名,判断是否存在等.
  41. *
  42. */
  43. #define BOOL int
  44. #define TRUE 1
  45. #define FALSE 0
  46. #define MAX_FMTSTR_LENGTH 2048 /*传递给print_message函数的格式字符串最大长度*/
  47. #define COPY_BUF_SIZE 4096 /*复制文件时每次读取的长度*/
  48. #define MAX_PATH_LENGTH (PATH_MAX + 1) /*路径的最大长度*/
  49. #define GBYTES (1024 * 1024 * 1024)
  50. #define MBYTES (1024 * 1024)
  51. #define KBYTES 1024
  52. #define HOUR (60 * 60)
  53. #define MINUTE 60
  54. #define OPP_CONTINUE 0
  55. #define OPP_SKIP 1
  56. #define OPP_CANCEL 2 /*walk 函数终止遍历退出*/
  57. #define MSGT_PROMPT 0
  58. #define MSGT_WARNING 1
  59. #define MSGT_ERROR 2
  60. #define MSGT_VERBOSE 3
  61. #define MSGT_DEMO 4
  62. /*启用大文件支持*/
  63. //#define _LARGEFILE64_SOURCE
  64. //#define _FILE_OFFSET_BITS 64
  65. //#ifdef _LARGEFILE64_SOURCE
  66. //#define stat stat64
  67. //#define fopen fopen64
  68. //#define fread fread64
  69. //#define fwrite fwrite64
  70. //#endif
  71. typedef int (*each_opp_t)(const char*, const char*, const char*, const struct stat* st);
  72. typedef void (*sig_handler_t)(int);
  73. /* 全局变量 */
  74. int sum_file = 0;
  75. int sum_dir = 0;
  76. long long sum_size = 0;
  77. int copied_file = 0;
  78. int copied_dir = 0;
  79. long long copied_size = 0;
  80. time_t copy_start_time = 0;
  81. BOOL status_pause = FALSE;
  82. BOOL opt_d = FALSE;
  83. BOOL opt_f = FALSE;
  84. BOOL opt_q = FALSE;
  85. BOOL opt_r = FALSE;
  86. BOOL opt_v = FALSE;
  87. /*显示为可读数字*/
  88. char* human_size(long long s, char *hs)
  89. {
  90. if(s >= GBYTES)
  91. {
  92. sprintf(hs, "%.2fGB", (s * 1.0) / GBYTES);
  93. }
  94. else if(s >= 1024 * 1024)
  95. {
  96. sprintf(hs, "%.2fMB", (s * 1.0) / MBYTES);
  97. }
  98. else if(s > 1024)
  99. {
  100. sprintf(hs, "%.2fKB", (s * 1.0) / KBYTES);
  101. }
  102. else
  103. {
  104. sprintf(hs, "%lldB", s);
  105. }
  106. return hs;
  107. }
  108. /* human readable time */
  109. char* human_time(time_t t, char *text)
  110. {
  111. int h,m,s;
  112. h = (int)(t / HOUR);
  113. m = (int)((t % HOUR) / MINUTE);
  114. s = (int)(t % HOUR % MINUTE);
  115. if(h > 0)
  116. {
  117. sprintf(text, "%dh %dm %ds", h, m, s);
  118. }
  119. else if(m > 0)
  120. {
  121. sprintf(text, "%dm %ds", m, s);
  122. }
  123. else
  124. {
  125. sprintf(text, "%ds", s);
  126. }
  127. return text;
  128. }
  129. /*
  130. * 先清除状态文字然后在输出信息
  131. * 1. 状态文字总是在当前行输出不换行
  132. * 2. printerror只能在状态文字被显示之后输出,即定时器被安装之后使用。
  133. */
  134. void print_message(int t, const char* fmt, ...)
  135. {
  136. char real_fmt[MAX_FMTSTR_LENGTH];
  137. va_list args;
  138. va_start(args, fmt);
  139. if(opt_q && (t == MSGT_WARNING || t == MSGT_ERROR))
  140. {
  141. /*quiet, don't output warning nor error message*/
  142. }
  143. else
  144. {
  145. sprintf(real_fmt, "\r\033[K%s", fmt);
  146. vprintf(real_fmt, args);
  147. }
  148. }
  149. /*连接目录字符串,主要处理末尾/的问题,frt snd 两个参数不能同时为空那样没有意义*/
  150. char* make_path(char *dest, const char *frt, const char *snd)
  151. {
  152. if(NULL == frt || strlen(frt) == 0)
  153. {
  154. sprintf(dest, "%s", snd);
  155. }
  156. else if(NULL == snd || strlen(snd) == 0)
  157. {
  158. sprintf(dest, "%s", frt);
  159. }
  160. else
  161. {
  162. if(frt[strlen(frt) - 1] == '/')
  163. {
  164. sprintf(dest, "%s%s", frt, snd);
  165. }
  166. else
  167. {
  168. sprintf(dest, "%s/%s", frt, snd);
  169. }
  170. }
  171. return dest;
  172. }
  173. /*显示进度条*/
  174. void show_status(BOOL finish)
  175. {
  176. int percent,i;
  177. char animate[4];
  178. static int animate_pos = -1;
  179. time_t cur_time;
  180. char speed[512];
  181. char hs[512];
  182. long long sp = 0;
  183. char ht[512];
  184. animate[0] = '-';
  185. animate[1] = '/';
  186. animate[2] = '|';
  187. animate[3] = '\\';
  188. time(&cur_time);
  189. if(sum_size == 0)
  190. {
  191. percent = 0;
  192. }
  193. else
  194. {
  195. percent = (copied_size * 1.0 / sum_size) * 100;
  196. }
  197. if(cur_time > copy_start_time)
  198. {
  199. sp = copied_size / (cur_time - copy_start_time);
  200. sprintf(speed, "%s/s", human_size(sp, hs));
  201. }
  202. else
  203. {
  204. sprintf(speed, "-");
  205. }
  206. human_size(copied_size, hs);
  207. if(finish)
  208. {
  209. printf("\r\033[K%d directories %d files %s copied, %s, %s.\n",\
  210. copied_dir, copied_file, hs, human_time(cur_time - copy_start_time, ht), speed);
  211. }
  212. else
  213. {
  214. printf("\r\033[K%d directories %d files %s copied, %d%%, %s %c ", \
  215. copied_dir, copied_file, hs, percent, speed, animate[animate_pos = (animate_pos + 1) % 4]);
  216. }
  217. fflush(stdout);
  218. }
  219. /*定时器处理函数*/
  220. void timer_handler(int signum)
  221. {
  222. if(!status_pause)
  223. {
  224. show_status(FALSE);
  225. }
  226. }
  227. /*安装/删除定时器*/
  228. void install_timer(size_t sec, sig_handler_t handler_func)
  229. {
  230. struct sigaction act;
  231. struct itimerval tick;
  232. if(sec > 0)
  233. {
  234. act.sa_handler = handler_func;
  235. }
  236. else
  237. {
  238. act.sa_handler = SIG_DFL;
  239. }
  240. sigemptyset(&act.sa_mask);
  241. act.sa_flags = 0;
  242. sigaction(SIGALRM, &act, 0);
  243. memset(&tick, 0, sizeof(tick));
  244. tick.it_value.tv_sec = sec;
  245. tick.it_value.tv_usec = 0;
  246. tick.it_interval.tv_sec = sec;
  247. tick.it_interval.tv_usec = 0;
  248. setitimer(ITIMER_REAL, &tick, 0);
  249. }
  250. /*
  251. * 遍历函数
  252. * 遍历函数只保证源文件/文件夹的每一项都调用一次opp函数
  253. * 由opp函数的返回值决定是否继续扫描
  254. * 采用“串烧”式程序风格
  255. * 只有一种情况下返回值为FALSE:opp 函数返回OPP_CANCEL
  256. */
  257. int walk(const char* path_from, const char* path_to, const char* path_tree, each_opp_t opp)
  258. {
  259. struct stat st;
  260. DIR* dir = NULL;
  261. struct dirent *entry = NULL;
  262. char path_tree_new[MAX_PATH_LENGTH];
  263. char path_from_full[MAX_PATH_LENGTH];
  264. int ret_val = OPP_CONTINUE;
  265. /*获得源的属性*/
  266. make_path(path_from_full, path_from, path_tree);
  267. if(-1 == stat(path_from_full, &st))
  268. {
  269. print_message(MSGT_ERROR, "can't access \"%s\".\n", path_from_full);
  270. return OPP_SKIP;
  271. }
  272. /*调用一次处理函数,处理当前项*/
  273. if((ret_val = opp(path_from, path_to, path_tree, &st)) != OPP_CONTINUE)
  274. {
  275. return ret_val;
  276. }
  277. /*如果是目录,则浏览目录,否则结束*/
  278. if(!S_ISDIR(st.st_mode))
  279. {
  280. return OPP_CONTINUE;
  281. }
  282. /*打开目录*/
  283. if(!(dir = opendir(path_from_full)))
  284. {
  285. print_message(MSGT_ERROR, "can't open directory \"%s\".\n", path_from_full);
  286. return OPP_SKIP;
  287. }
  288. /*浏览目录*/
  289. while((entry = readdir(dir)) != NULL)
  290. {
  291. /*构建path_tree_new*/
  292. make_path(path_tree_new, path_tree, entry->d_name);
  293. make_path(path_from_full, path_from, path_tree_new);
  294. /*无法访问 skip*/
  295. if(-1 == stat(path_from_full, &st))
  296. {
  297. print_message(MSGT_ERROR, "skip, can't access \"\".\n", path_from_full);
  298. continue;
  299. }
  300. /* 忽略 . 和 .. */
  301. if(S_ISDIR(st.st_mode) && (strcmp(".", entry->d_name) == 0 || strcmp("..", entry->d_name) == 0))
  302. {
  303. continue;
  304. }
  305. if(S_ISDIR(st.st_mode) && opt_r)
  306. {
  307. /*递归处理子目录*/
  308. if(walk(path_from, path_to, path_tree_new, opp) == OPP_CANCEL)
  309. {
  310. ret_val = OPP_CANCEL;
  311. break;
  312. }
  313. }
  314. else
  315. {
  316. /*处理函数处理一个子项*/
  317. if(opp(path_from, path_to, path_tree_new, &st) == OPP_CANCEL)
  318. {
  319. ret_val = OPP_CANCEL;
  320. break;
  321. }
  322. }
  323. }
  324. closedir(dir);
  325. return ret_val;
  326. }
  327. /* 统计函数 */
  328. int sum_up(const char* path_from, const char* path_to, const char* path_tree, const struct stat* st)
  329. {
  330. if(S_ISREG(st->st_mode))
  331. {
  332. sum_file++;
  333. sum_size += st->st_size;
  334. }
  335. else if(S_ISDIR(st->st_mode))
  336. {
  337. sum_dir++;
  338. }
  339. else
  340. {
  341. print_message(MSGT_WARNING, "skip:%s\n", path_tree);
  342. }
  343. return OPP_CONTINUE;
  344. }
  345. /*demo*/
  346. int demo(const char* path_from, const char* path_to, const char* path_tree, const struct stat* st)
  347. {
  348. char path_from_full[MAX_PATH_LENGTH];
  349. char path_to_full[MAX_PATH_LENGTH];
  350. make_path(path_from_full, path_from, path_tree);
  351. make_path(path_to_full, path_to, path_tree);
  352. if(S_ISREG(st->st_mode))
  353. {
  354. print_message(MSGT_DEMO, "cp \"%s\" -> \"%s\".\n", path_from_full, path_to_full);
  355. }
  356. else if(S_ISDIR(st->st_mode))
  357. {
  358. print_message(MSGT_DEMO, "mkdir \"%s\".\n", path_to_full);
  359. }
  360. else
  361. {
  362. print_message(MSGT_WARNING, "skip \"%s\"\n", path_tree);
  363. }
  364. return OPP_CONTINUE;
  365. }
  366. /* 操作 */
  367. int action(const char* path_from, const char* path_to, const char* path_tree, const struct stat* st)
  368. {
  369. int ret_val = OPP_CONTINUE;
  370. char path_from_full[MAX_PATH_LENGTH];
  371. char path_to_full[MAX_PATH_LENGTH];
  372. size_t rd, wr, swr;
  373. char buf[COPY_BUF_SIZE];
  374. FILE *src_file, *dest_file;
  375. BOOL over_write = FALSE;
  376. int cmd;
  377. BOOL skip = FALSE;
  378. struct stat st_dest;
  379. make_path(path_from_full, path_from, path_tree);
  380. make_path(path_to_full, path_to, path_tree);
  381. if(S_ISREG(st->st_mode))
  382. {
  383. /* regular file */
  384. if(opt_v)
  385. {
  386. print_message(MSGT_VERBOSE, "cp \"%s\" -> \"%s\".\n", path_from_full, path_to_full);
  387. }
  388. if(strcmp(path_from_full, path_to_full) == 0)
  389. {
  390. ret_val = OPP_SKIP;
  391. print_message(MSGT_ERROR, "skip, \"%s\" and \"%s\" are the same.\n", path_from_full, path_to_full);
  392. }
  393. else if(src_file = fopen(path_from_full, "rb"))
  394. {
  395. do
  396. {
  397. /*询问是否可以覆盖*/
  398. if(!opt_f && 0 == access(path_to_full, F_OK))
  399. {
  400. /* 应该先停止计时器,否则在等待用户输入时如果有定时器被触发,会导致 getchar()停止等待并返回 EOF*/
  401. status_pause = TRUE;
  402. print_message(MSGT_PROMPT, "overwrite \"%s\"? ([y] yes,[n] no, [a] all, [c] cancel)", path_to_full);
  403. while(1)
  404. {
  405. cmd = getchar();
  406. /*中断重启 由于有一个定时器正在运行,在等待用户输入的过程中getchar会被中断返回*/
  407. if(-1 == cmd) continue;
  408. /*skip useless chars of inputed line*/
  409. if(cmd != '\n')
  410. {
  411. while(getchar() != '\n');
  412. }
  413. if('y' == cmd)
  414. {
  415. break;
  416. }
  417. else if('n' == cmd)
  418. {
  419. skip = TRUE;
  420. ret_val = OPP_SKIP;
  421. break;
  422. }
  423. else if('a' == cmd)
  424. {
  425. opt_f = TRUE;
  426. break;
  427. }
  428. else if('c' == cmd)
  429. {
  430. /* skip current file and cancel walk progress */
  431. skip = TRUE;
  432. ret_val = OPP_CANCEL;
  433. break;
  434. }
  435. else
  436. {
  437. /* unknown command */
  438. }
  439. }
  440. status_pause = FALSE;
  441. /* ship current file */
  442. if(skip) break;
  443. }
  444. /* open target file for write */
  445. if(dest_file = fopen(path_to_full, "wb"))
  446. {
  447. while((rd = fread(buf, 1, COPY_BUF_SIZE, src_file)) > 0)
  448. {
  449. wr = 0;
  450. do
  451. {
  452. swr = fwrite(buf + wr, 1, rd - wr, dest_file);
  453. wr += swr;
  454. }
  455. while(swr > 0 && wr < rd);
  456. copied_size += rd;
  457. if(wr != rd)
  458. {
  459. /*只有部分文件被复制也视为成功因为文件系统中已经有这个文件的记录了*/
  460. print_message(MSGT_ERROR, "write file error %s.\n", path_to_full);
  461. break;
  462. }
  463. }
  464. fclose(dest_file);
  465. chmod(path_to_full, st->st_mode);
  466. copied_file++;
  467. }
  468. else
  469. {
  470. ret_val = OPP_SKIP;
  471. print_message(MSGT_ERROR, "skip, can't open target file \"%s\"\n", path_to_full);
  472. }
  473. }while(0);
  474. fclose(src_file);
  475. }
  476. else
  477. {
  478. ret_val = OPP_SKIP;
  479. print_message(MSGT_ERROR, "skip, can't open source file \"%s\"\n", path_from_full);
  480. }
  481. }
  482. else if(S_ISDIR(st->st_mode))
  483. {
  484. /* directories */
  485. if(opt_v)
  486. {
  487. print_message(MSGT_VERBOSE, "mkdir \"%s\"\n", path_to_full);
  488. }
  489. if(0 == stat(path_to_full, &st_dest))
  490. {
  491. /*path_to_full already exist*/
  492. if(S_ISDIR(st_dest.st_mode))
  493. {
  494. copied_dir++;
  495. }
  496. else
  497. {
  498. ret_val = OPP_SKIP;
  499. print_message(MSGT_WARNING, "skip, \"%s\" exists and it's not a directory.\n", path_to_full);
  500. }
  501. }
  502. else
  503. {
  504. /*try to make a new directory*/
  505. if(0 == mkdir(path_to_full, st->st_mode))
  506. {
  507. chmod(path_to_full, st->st_mode);
  508. copied_dir++;
  509. }
  510. else
  511. {
  512. ret_val = OPP_SKIP;
  513. print_message(MSGT_ERROR, "skip, \"%s\" mkdir failed.\n", path_to_full);
  514. }
  515. }
  516. }
  517. else
  518. {
  519. ret_val = OPP_SKIP;
  520. print_message(MSGT_WARNING, "skip, \"%s\" is not a file nor directory.\n", path_to_full);
  521. }
  522. return ret_val;
  523. }
  524. /*使用说明*/
  525. void usage()
  526. {
  527. printf("xcp - by Q++ Studio 2013-11-1\n");
  528. printf("description:cp with progress\n");
  529. printf("synopsis: xcp [OPTIONS] src1 [src2 ... srcn] dest\n\n");
  530. printf("[OPTIONS]\n");
  531. printf("-r:recusive copy sub directories.\n");
  532. printf("-f:force overwrite without prompt.\n");
  533. printf("-q:quiet no warning/error message.\n");
  534. printf("-d:demo,do not copy,output message only.\n");
  535. printf("-v:verbos output.\n");
  536. printf("-h:show usage message.\n");
  537. }
  538. /*禁止循环复制,即目标文件/文件夹不能包含在源文件/文件夹中*/
  539. BOOL is_self_copy(const char* src, const char* dest)
  540. {
  541. /*严格的做法应该先把src和dest都转化为绝对路径然后在比较,不过
  542. *Linux下的相对路径比较麻烦有 ~ ./ ../ ../../ 等...
  543. */
  544. char c;
  545. char* sub = strstr(dest, src);
  546. if(sub)
  547. {
  548. c = sub[strlen(src)];
  549. return c == '\0' || c == '/' || src[strlen(src) - 1] == '/';
  550. }
  551. else
  552. {
  553. return FALSE;
  554. }
  555. }
  556. /*主函数,做两次遍历*/
  557. int main(int argc, char* args[])
  558. {
  559. int i = 0;
  560. char *path_from = NULL, *path_to = NULL, *file_name = NULL;
  561. char path_to_fixed[MAX_PATH_LENGTH];
  562. struct stat st_src, st_dest;
  563. char human_readable_size[200];
  564. int opt;
  565. BOOL help = FALSE;
  566. while((opt = getopt(argc, args, "rfqdhv")) != -1)
  567. {
  568. switch(opt)
  569. {
  570. case 'r':
  571. opt_r = TRUE;
  572. break;
  573. case 'f':
  574. opt_f = TRUE;
  575. break;
  576. case 'q':
  577. opt_q = TRUE;
  578. break;
  579. case 'd':
  580. opt_d = TRUE;
  581. break;
  582. case 'h':
  583. help = TRUE;
  584. break;
  585. case 'v':
  586. opt_v = TRUE;
  587. break;
  588. case '?':
  589. printf("unknown option: %c\n", optopt);
  590. help = TRUE;
  591. break;
  592. default:
  593. break;
  594. }
  595. }
  596. if(help || optind + 2 > argc)
  597. {
  598. usage();
  599. return 1;
  600. }
  601. /* 第一次遍历:统计 */
  602. sum_file = 0;
  603. sum_dir = 0;
  604. sum_size = 0;
  605. path_to = args[argc - 1];
  606. for(i = optind; i < argc -1; ++i)
  607. {
  608. path_from = args[i];
  609. walk(path_from, path_to, NULL, sum_up);
  610. }
  611. if(sum_file == 0 && sum_dir == 0)
  612. {
  613. printf("nothing found.\n");
  614. }
  615. else
  616. {
  617. human_size(sum_size, human_readable_size);
  618. printf("%d directories %d files %s detected.\n", sum_dir, sum_file, human_readable_size);
  619. /* 第二次遍历:执行*/
  620. copied_file = 0;
  621. copied_dir = 0;
  622. copied_size = 0;
  623. // 设置一个定时器,每隔1秒显示一下进度
  624. time(©_start_time);
  625. show_status(FALSE);
  626. install_timer(1, timer_handler);
  627. for(i = optind; i < argc - 1; ++i)
  628. {
  629. path_from = args[i];
  630. path_to = args[argc - 1];
  631. /*源是否存在*/
  632. if(-1 == stat(path_from, &st_src))
  633. {
  634. print_message(MSGT_ERROR, "\"%s\" doesn't exist.\n", path_from);
  635. continue;
  636. }
  637. /*
  638. * 如果源是文件而且目标是已经存在的目录,则自动补齐文件名
  639. * 如果目标是已经存在的文件,先判断是否指向同一个文件 inode number
  640. */
  641. if(S_ISREG(st_src.st_mode))
  642. {
  643. if((0 == stat(path_to, &st_dest)) && S_ISDIR(st_dest.st_mode))
  644. {
  645. file_name = strrchr(path_from, '/');
  646. path_to = make_path(path_to_fixed, path_to, file_name ? file_name + 1 : path_from);
  647. }
  648. }
  649. else if(S_ISDIR(st_src.st_mode))
  650. {
  651. if(opt_r && is_self_copy(path_from, path_to))
  652. {
  653. /*源是目录时要防止循环复制*/
  654. print_message(MSGT_ERROR, "can't xcp \"%s\" -> \"%s\"\n", path_from, path_to);
  655. continue;
  656. }
  657. }
  658. else
  659. {
  660. print_message(MSGT_WARNING, "skip \"%s\" not a file nor a directory.\n", path_from);
  661. continue;
  662. }
  663. if(opt_d)
  664. {
  665. walk(path_from, path_to, NULL, demo);
  666. }
  667. else
  668. {
  669. walk(path_from, path_to, NULL, action);
  670. }
  671. }
  672. install_timer(0, NULL);
  673. show_status(TRUE);
  674. }
  675. return 0;
  676. }



 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/264227
推荐阅读
相关标签
  

闽ICP备14008679号