copy_dir.c 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /*!
  2. * \file lib/gis/copy_dir.c
  3. *
  4. * \brief GIS Library - function to recursively copy a directory
  5. *
  6. * Extracted from general/manage/lib/do_copy.c
  7. *
  8. * (C) 2008-2015 by the GRASS Development Team
  9. *
  10. * This program is free software under the GNU General Public License
  11. * (>=v2). Read the file COPYING that comes with GRASS for details.
  12. *
  13. * \author Huidae Cho
  14. */
  15. #include <stdio.h>
  16. #include <errno.h>
  17. #include <string.h>
  18. #include <grass/gis.h>
  19. #include <fcntl.h>
  20. #include <unistd.h>
  21. #include <dirent.h>
  22. #include <sys/types.h>
  23. #include <sys/stat.h>
  24. /*!
  25. * \brief Copy recursively source directory to destination directory
  26. *
  27. * RULE:
  28. * 1. If destination does not exist, copy source to destination as expected.
  29. * 2. If destination already exists and it's a file, destination will be
  30. * deleted first and apply RULE 1.
  31. * 3. If destination already exists which is a directory and source is a file,
  32. * try to copy source to destination directory.
  33. * 4. If destination already exists which is a directory and source is also a
  34. * directory, try to copy all contents in source to destination directory.
  35. *
  36. * This rule is designed according to general/manage/lib/copy.sh.
  37. *
  38. * POSSIBLE CASES:
  39. * \verbatim
  40. * if src is a file:
  41. * if dst does not exist:
  42. * copy src to dst RULE 1
  43. * if dst is a file:
  44. * delete dst and copy src to dst RULE 2
  45. * if dst is a directory:
  46. * try recursive_copy(src, dst/src) RULE 3
  47. * if src is a directory:
  48. * if dst does not exist:
  49. * copy src to dst RULE 1
  50. * if dst is a file:
  51. * delete dst and copy src to dst RULE 2
  52. * if dst is a directory:
  53. * try RULE 4
  54. * for i in `ls src`
  55. * do
  56. * recursive_copy(src/$i, dst/$i)
  57. * done
  58. * \endverbatim
  59. *
  60. * \param src source directory
  61. * \param dst destination directory
  62. *
  63. * \return 0 if successful, otherwise 1
  64. */
  65. int G_recursive_copy(const char *src, const char *dst)
  66. {
  67. DIR *dirp;
  68. struct stat sb;
  69. if (G_lstat(src, &sb) < 0)
  70. return 1;
  71. /* src is a file */
  72. if (!S_ISDIR(sb.st_mode)) {
  73. char buf[4096];
  74. int fd, fd2;
  75. size_t len, len2;
  76. if (G_lstat(dst, &sb) == 0 && S_ISDIR(sb.st_mode)) {
  77. char path[GPATH_MAX];
  78. const char *p = strrchr(src, '/');
  79. /* src => dst/src */
  80. sprintf(path, "%s/%s", dst, (p ? p + 1 : src));
  81. return G_recursive_copy(src, path);
  82. }
  83. /* src => dst */
  84. if ((fd = open(src, O_RDONLY)) < 0)
  85. return 1;
  86. if ((fd2 =
  87. open(dst, O_CREAT | O_TRUNC | O_WRONLY,
  88. sb.st_mode & 0777)) < 0) {
  89. close(fd);
  90. return 1;
  91. }
  92. while ((len = read(fd, buf, sizeof(buf))) > 0) {
  93. while (len && (len2 = write(fd2, buf, len)) >= 0)
  94. len -= len2;
  95. }
  96. close(fd);
  97. close(fd2);
  98. return 0;
  99. }
  100. /* src is a directory */
  101. if (G_lstat(dst, &sb) < 0) {
  102. if (G_mkdir(dst))
  103. return 1;
  104. }
  105. else
  106. /* if dst already exists and it's a file, try to remove it */
  107. if (!S_ISDIR(sb.st_mode)) {
  108. if (remove(dst) < 0 || G_mkdir(dst) < 0)
  109. return 1;
  110. }
  111. dirp = opendir(src);
  112. if (!dirp)
  113. return 1;
  114. for (;;) {
  115. char path[GPATH_MAX], path2[GPATH_MAX];
  116. struct dirent *dp = readdir(dirp);
  117. if (!dp)
  118. break;
  119. /* do not copy hidden files */
  120. if (dp->d_name[0] == '.')
  121. continue;
  122. sprintf(path, "%s/%s", src, dp->d_name);
  123. sprintf(path2, "%s/%s", dst, dp->d_name);
  124. if (G_recursive_copy(path, path2) != 0)
  125. return 1;
  126. }
  127. closedir(dirp);
  128. return 0;
  129. }