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 }