GRASS 8 Programmer's Manual  8.5.0dev(2025)-c070206eb1
copy_dir.c
Go to the documentation of this file.
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 
16 #include <stdio.h>
17 #include <errno.h>
18 #include <string.h>
19 
20 #include <grass/gis.h>
21 
22 #include <fcntl.h>
23 #include <unistd.h>
24 #include <dirent.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 
28 /*!
29  * \brief Copy recursively source directory to destination directory
30  *
31  * RULE:
32  * 1. If destination does not exist, copy source to destination as expected.
33  * 2. If destination already exists and it's a file, destination will be
34  * deleted first and apply RULE 1.
35  * 3. If destination already exists which is a directory and source is a file,
36  * try to copy source to destination directory.
37  * 4. If destination already exists which is a directory and source is also a
38  * directory, try to copy all contents in source to destination directory.
39  *
40  * This rule is designed according to general/manage/lib/copy.sh.
41  *
42  * POSSIBLE CASES:
43  * \verbatim
44  * if src is a file:
45  * if dst does not exist:
46  * copy src to dst RULE 1
47  * if dst is a file:
48  * delete dst and copy src to dst RULE 2
49  * if dst is a directory:
50  * try recursive_copy(src, dst/src) RULE 3
51  * if src is a directory:
52  * if dst does not exist:
53  * copy src to dst RULE 1
54  * if dst is a file:
55  * delete dst and copy src to dst RULE 2
56  * if dst is a directory:
57  * try RULE 4
58  * for i in `ls src`
59  * do
60  * recursive_copy(src/$i, dst/$i)
61  * done
62  * \endverbatim
63  *
64  * \param src source directory
65  * \param dst destination directory
66  *
67  * \return 0 if successful, otherwise 1
68  */
69 int G_recursive_copy(const char *src, const char *dst)
70 {
71  DIR *dirp;
72  struct stat sb;
73 
74  if (G_lstat(src, &sb) < 0)
75  return 1;
76 
77  /* src is a file */
78  if (!S_ISDIR(sb.st_mode)) {
79  char buf[4096];
80  int fd, fd2;
81  ssize_t len, len2;
82 
83  if (G_lstat(dst, &sb) == 0 && S_ISDIR(sb.st_mode)) {
84  char path[GPATH_MAX];
85  const char *p = strrchr(src, '/');
86 
87  /* src => dst/src */
88  snprintf(path, sizeof(path), "%s/%s", dst, (p ? p + 1 : src));
89  return G_recursive_copy(src, path);
90  }
91 
92  /* src => dst */
93  if ((fd = open(src, O_RDONLY)) < 0)
94  return 1;
95 
96  if ((fd2 = open(dst, O_CREAT | O_TRUNC | O_WRONLY, sb.st_mode & 0777)) <
97  0) {
98  close(fd);
99  return 1;
100  }
101 
102  while ((len = read(fd, buf, sizeof(buf))) > 0) {
103  while ((len > 0) && (len2 = write(fd2, buf, (size_t)len)) >= 0)
104  len -= len2;
105  }
106 
107  close(fd);
108  close(fd2);
109 
110  return 0;
111  }
112 
113  /* src is a directory */
114  if (G_lstat(dst, &sb) < 0) {
115  if (G_mkdir(dst))
116  return 1;
117  }
118  else
119  /* if dst already exists and it's a file, try to remove it */
120  if (!S_ISDIR(sb.st_mode)) {
121  if (remove(dst) < 0 || G_mkdir(dst) < 0)
122  return 1;
123  }
124 
125  dirp = opendir(src);
126  if (!dirp)
127  return 1;
128 
129  for (;;) {
130  char path[GPATH_MAX], path2[GPATH_MAX];
131  struct dirent *dp = readdir(dirp);
132 
133  if (!dp)
134  break;
135 
136  /* do not copy hidden files */
137  if (dp->d_name[0] == '.')
138  continue;
139 
140  snprintf(path, sizeof(path), "%s/%s", src, dp->d_name);
141  snprintf(path2, sizeof(path2), "%s/%s", dst, dp->d_name);
142 
143  if (G_recursive_copy(path, path2) != 0) {
144  closedir(dirp);
145  return 1;
146  }
147  }
148 
149  closedir(dirp);
150 
151  return 0;
152 }
int G_recursive_copy(const char *src, const char *dst)
Copy recursively source directory to destination directory.
Definition: copy_dir.c:69
int G_lstat(const char *, struct stat *)
Get file status.
Definition: paths.c:145
int G_mkdir(const char *)
Creates a new directory.
Definition: paths.c:27
struct DIR DIR
Definition: dirent.h:18
Header file for msvc/fcntl.c.
#define open
Definition: fcntl.h:33
#define GPATH_MAX
Definition: gis.h:199
DIR * opendir(const char *name)
Definition: msvc/dirent.c:15
int closedir(DIR *dir)
Definition: msvc/dirent.c:54
struct dirent * readdir(DIR *dir)
Definition: msvc/dirent.c:75
#define S_ISDIR(mode)
Definition: stat.h:6
SSIZE_T ssize_t
Definition: stdio.h:9
Definition: dirent.h:20
char * d_name
Definition: dirent.h:21
Definition: path.h:15
#define read
Definition: unistd.h:5
#define close
Definition: unistd.h:8
#define write
Definition: unistd.h:6