FileUtils.cpp 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. #include "FileUtils.h"
  2. #include <AK/FileSystemPath.h>
  3. #include <AK/StringBuilder.h>
  4. #include <LibCore/CDirIterator.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <sys/stat.h>
  8. #include <unistd.h>
  9. namespace FileUtils {
  10. bool copy_file_or_directory(const String& src_path, const String& dst_path)
  11. {
  12. int duplicate_count = 0;
  13. while (access(get_duplicate_name(dst_path, duplicate_count).characters(), F_OK) == 0) {
  14. ++duplicate_count;
  15. }
  16. if (duplicate_count != 0) {
  17. return copy_file_or_directory(src_path, get_duplicate_name(dst_path, duplicate_count));
  18. }
  19. int src_fd = open(src_path.characters(), O_RDONLY);
  20. if (src_fd < 0) {
  21. return false;
  22. }
  23. struct stat src_stat;
  24. int rc = fstat(src_fd, &src_stat);
  25. if (rc < 0) {
  26. return false;
  27. }
  28. if (S_ISDIR(src_stat.st_mode)) {
  29. return copy_directory(src_path, dst_path);
  30. }
  31. return copy_file(src_path, dst_path, src_stat, src_fd);
  32. }
  33. bool copy_directory(const String& src_path, const String& dst_path)
  34. {
  35. int rc = mkdir(dst_path.characters(), 0755);
  36. if (rc < 0) {
  37. return false;
  38. }
  39. CDirIterator di(src_path, CDirIterator::SkipDots);
  40. if (di.has_error()) {
  41. return false;
  42. }
  43. while (di.has_next()) {
  44. String filename = di.next_path();
  45. bool is_copied = copy_file_or_directory(
  46. String::format("%s/%s", src_path.characters(), filename.characters()),
  47. String::format("%s/%s", dst_path.characters(), filename.characters()));
  48. if (!is_copied) {
  49. return false;
  50. }
  51. }
  52. return true;
  53. }
  54. bool copy_file(const String& src_path, const String& dst_path, const struct stat& src_stat, int src_fd)
  55. {
  56. int dst_fd = creat(dst_path.characters(), 0666);
  57. if (dst_fd < 0) {
  58. if (errno != EISDIR) {
  59. return false;
  60. }
  61. StringBuilder builder;
  62. builder.appendf("%s/%s", dst_path, FileSystemPath(src_path).basename());
  63. String dst_path = builder.to_string();
  64. dst_fd = creat(dst_path.characters(), 0666);
  65. if (dst_fd < 0) {
  66. return false;
  67. }
  68. }
  69. if (src_stat.st_size > 0) {
  70. if (ftruncate(dst_fd, src_stat.st_size) < 0) {
  71. perror("cp: ftruncate");
  72. return false;
  73. }
  74. }
  75. for (;;) {
  76. char buffer[32768];
  77. ssize_t nread = read(src_fd, buffer, sizeof(buffer));
  78. if (nread < 0) {
  79. return false;
  80. }
  81. if (nread == 0)
  82. break;
  83. ssize_t remaining_to_write = nread;
  84. char* bufptr = buffer;
  85. while (remaining_to_write) {
  86. ssize_t nwritten = write(dst_fd, bufptr, remaining_to_write);
  87. if (nwritten < 0) {
  88. return false;
  89. }
  90. assert(nwritten > 0);
  91. remaining_to_write -= nwritten;
  92. bufptr += nwritten;
  93. }
  94. }
  95. auto my_umask = umask(0);
  96. umask(my_umask);
  97. int rc = fchmod(dst_fd, src_stat.st_mode & ~my_umask);
  98. if (rc < 0) {
  99. return false;
  100. }
  101. close(src_fd);
  102. close(dst_fd);
  103. return true;
  104. }
  105. String get_duplicate_name(const String& path, int duplicate_count)
  106. {
  107. if (duplicate_count == 0) {
  108. return path;
  109. }
  110. FileSystemPath fsp(path);
  111. StringBuilder duplicated_name;
  112. duplicated_name.append('/');
  113. for (int i = 0; i < fsp.parts().size() - 1; ++i) {
  114. duplicated_name.appendf("%s/", fsp.parts()[i].characters());
  115. }
  116. auto prev_duplicate_tag = String::format("(%d)", duplicate_count);
  117. auto title = fsp.title();
  118. if (title.ends_with(prev_duplicate_tag)) {
  119. // remove the previous duplicate tag "(n)" so we can add a new tag.
  120. title = title.substring(0, title.length() - prev_duplicate_tag.length());
  121. }
  122. duplicated_name.appendf("%s (%d)", fsp.title().characters(), duplicate_count);
  123. if (!fsp.extension().is_empty()) {
  124. duplicated_name.appendf(".%s", fsp.extension().characters());
  125. }
  126. return duplicated_name.build();
  127. }
  128. }