Mercurial > hg > toybox
comparison toys/posix/cp.c @ 674:7e846e281e38
New build infrastructure to generate FLAG_ macros and TT alias, #define FOR_commandname before #including toys.h to trigger it. Rename DEFINE_GLOBALS() to just GLOBALS() (because I could never remember if it was DECLARE_GLOBALS). Convert existing commands to use new infrastructure, and replace optflag constants with FLAG_ macros where appropriate.
author | Rob Landley <rob@landley.net> |
---|---|
date | Mon, 08 Oct 2012 00:02:30 -0500 |
parents | 6df4ccc0acbe |
children | 786841fdb1e0 |
comparison
equal
deleted
inserted
replaced
673:c102f31a753e | 674:7e846e281e38 |
---|---|
4 * | 4 * |
5 * Copyright 2008 Rob Landley <rob@landley.net> | 5 * Copyright 2008 Rob Landley <rob@landley.net> |
6 * | 6 * |
7 * See http://opengroup.org/onlinepubs/9699919799/utilities/cp.html | 7 * See http://opengroup.org/onlinepubs/9699919799/utilities/cp.html |
8 * | 8 * |
9 * "R+ra+d+p+r" | 9 * TODO: "R+ra+d+p+r" sHLPR |
10 USE_CP(NEWTOY(cp, "<2vslrRdpaHLPif", TOYFLAG_BIN)) | 10 |
11 USE_CP(NEWTOY(cp, "<2"USE_CP_MORE("rdavsl")"RHLPfip", TOYFLAG_BIN)) | |
11 | 12 |
12 config CP | 13 config CP |
13 bool "cp (broken by dirtree changes)" | 14 bool "cp (broken by dirtree changes)" |
14 default n | 15 default n |
15 help | 16 help |
16 usage: cp -fiprdal SOURCE... DEST | 17 usage: cp [-fipRHLP] SOURCE... DEST |
17 | 18 |
18 Copy files from SOURCE to DEST. If more than one SOURCE, DEST must | 19 Copy files from SOURCE to DEST. If more than one SOURCE, DEST must |
19 be a directory. | 20 be a directory. |
20 | 21 |
21 -f force copy by deleting destination file | 22 -f force copy by deleting destination file |
22 -i interactive, prompt before overwriting existing DEST | 23 -i interactive, prompt before overwriting existing DEST |
23 -p preserve timestamps, ownership, and permissions | 24 -p preserve timestamps, ownership, and permissions |
24 -r recurse into subdirectories (DEST must be a directory) | 25 -R recurse into subdirectories (DEST must be a directory) |
26 -H Follow symlinks listed on command line | |
27 -L Follow all symlinks | |
28 -P Do not follow symlinks [default] | |
29 | |
30 config CP_MORE | |
31 bool "cp -rdavsl options" | |
32 default y | |
33 depends on CP | |
34 help | |
35 usage: cp [-rdavsl] | |
36 | |
37 -r synonym for -R | |
25 -d don't dereference symlinks | 38 -d don't dereference symlinks |
26 -a same as -dpr | 39 -a same as -dpr |
27 -l hard link instead of copying | 40 -l hard link instead of copy |
41 -s symlink instead of copy | |
28 -v verbose | 42 -v verbose |
29 */ | 43 */ |
30 | 44 |
45 #define FOR_cp | |
31 #include "toys.h" | 46 #include "toys.h" |
32 | 47 |
33 #define FLAG_f 1 | 48 // TODO: PLHlsd |
34 #define FLAG_i 2 | 49 |
35 #define FLAG_P 4 // todo | 50 GLOBALS( |
36 #define FLAG_L 8 // todo | |
37 #define FLAG_H 16 // todo | |
38 #define FLAG_a 32 | |
39 #define FLAG_p 64 | |
40 #define FLAG_d 128 // todo | |
41 #define FLAG_R 256 | |
42 #define FLAG_r 512 | |
43 #define FLAG_l 1024 // todo | |
44 #define FLAG_s 2048 // todo | |
45 #define FLAG_v 4098 | |
46 | |
47 DEFINE_GLOBALS( | |
48 char *destname; | 51 char *destname; |
49 int destisdir; | 52 int destisdir; |
50 int destisnew; | |
51 int keep_symlinks; | 53 int keep_symlinks; |
52 ) | 54 ) |
53 | |
54 #define TT this.cp | |
55 | 55 |
56 // Copy an individual file or directory to target. | 56 // Copy an individual file or directory to target. |
57 | 57 |
58 void cp_file(char *src, char *dst, struct stat *srcst) | 58 void cp_file(char *src, char *dst, struct stat *srcst) |
59 { | 59 { |
154 return 0; | 154 return 0; |
155 } | 155 } |
156 | 156 |
157 void cp_main(void) | 157 void cp_main(void) |
158 { | 158 { |
159 struct stat st; | 159 char *dpath = NULL; |
160 struct stat st, std; | |
160 int i; | 161 int i; |
161 | 162 |
162 // Grab target argument. (Guaranteed to be there due to "<2" above.) | 163 // Identify destination |
163 | 164 |
164 TT.destname = toys.optargs[--toys.optc]; | 165 if (!stat(TT.destname, &std) && S_ISDIR(std.st_mode)) TT.destisdir++; |
165 | 166 else if (toys.optc>1) error_exit("'%s' not directory", TT.destname); |
166 // If destination doesn't exist, are we ok with that? | 167 |
167 | 168 // TODO: This is too early: we haven't created it yet if we need to |
168 if (stat(TT.destname, &st)) { | 169 if (toys.optflags & (FLAG_R|FLAG_r|FLAG_a)) |
169 if (toys.optc>1) goto error_notdir; | 170 dpath = realpath(TT.destname = toys.optargs[--toys.optc], NULL); |
170 TT.destisnew++; | 171 |
171 | 172 // Loop through sources |
172 // If destination exists... | |
173 | |
174 } else { | |
175 if (S_ISDIR(st.st_mode)) TT.destisdir++; | |
176 else if (toys.optc > 1) goto error_notdir; | |
177 } | |
178 | |
179 // Handle sources | |
180 | 173 |
181 for (i=0; i<toys.optc; i++) { | 174 for (i=0; i<toys.optc; i++) { |
182 char *src = toys.optargs[i]; | 175 char *dst, *src = toys.optargs[i]; |
183 char *dst; | |
184 | 176 |
185 // Skip src==dest (TODO check inodes to catch "cp blah ./blah"). | 177 // Skip src==dest (TODO check inodes to catch "cp blah ./blah"). |
186 | 178 |
187 if (!strcmp(src, TT.destname)) continue; | 179 if (!strncmp(src, TT.destname)) continue; |
188 | 180 |
189 // Skip nonexistent sources. | 181 // Skip nonexistent sources. |
190 | 182 |
191 TT.keep_symlinks = toys.optflags & (FLAG_d|FLAG_a); | 183 TT.keep_symlinks = toys.optflags & (FLAG_d|FLAG_a); |
192 if (TT.keep_symlinks ? lstat(src, &st) : stat(src, &st)) | 184 if (TT.keep_symlinks ? lstat(src, &st) : stat(src, &st) |
185 || (st.st_dev = dst.st_dev && st.st_ino == dst.dst_ino)) | |
193 { | 186 { |
194 perror_msg("'%s'", src); | 187 objection: |
188 perror_msg("bad '%s'", src); | |
195 toys.exitval = 1; | 189 toys.exitval = 1; |
196 continue; | 190 continue; |
197 } | 191 } |
198 | 192 |
199 // Copy directory or file. | 193 // Copy directory or file. |
200 | 194 |
201 if (TT.destisdir) { | 195 if (TT.destisdir) { |
196 char *s; | |
197 | |
198 // Catch "cp -R .. ." and friends that would go on forever | |
199 if (dpath && (s = realpath(src, NULL)) { | |
200 int i = strlen(s); | |
201 i = (!strncmp(s, dst, i) && (!s[i] || s[i]=='/')); | |
202 free(s); | |
203 | |
204 if (i) goto objection; | |
205 } | |
206 | |
207 // Create destination filename within directory | |
202 dst = strrchr(src, '/'); | 208 dst = strrchr(src, '/'); |
203 if (dst) dst++; | 209 if (dst) dst++; |
204 else dst=src; | 210 else dst=src; |
205 dst = xmsprintf("%s/%s", TT.destname, dst); | 211 dst = xmsprintf("%s/%s", TT.destname, dst); |
206 } else dst = TT.destname; | 212 } else dst = TT.destname; |
213 | |
207 if (S_ISDIR(st.st_mode)) { | 214 if (S_ISDIR(st.st_mode)) { |
208 if (toys.optflags & (FLAG_r|FLAG_R|FLAG_a)) { | 215 if (toys.optflags & (FLAG_r|FLAG_R|FLAG_a)) { |
209 cp_file(src, dst, &st); | 216 cp_file(src, dst, &st); |
210 | 217 |
211 TT.keep_symlinks++; | 218 TT.keep_symlinks++; |
212 strncpy(toybuf, src, sizeof(toybuf)-1); | 219 dirtree_read(src, cp_node); |
213 toybuf[sizeof(toybuf)-1]=0; | |
214 dirtree_read(toybuf, cp_node); | |
215 } else error_msg("Skipped dir '%s'", src); | 220 } else error_msg("Skipped dir '%s'", src); |
216 } else cp_file(src, dst, &st); | 221 } else cp_file(src, dst, &st); |
217 if (TT.destisdir) free(dst); | 222 if (TT.destisdir) free(dst); |
218 } | 223 } |
219 | 224 |
225 if (CFG_TOYBOX_FREE) free(dpath); | |
220 return; | 226 return; |
221 | |
222 error_notdir: | |
223 error_exit("'%s' isn't a directory", TT.destname); | |
224 } | 227 } |