new version from Dave Hitz of Auspex
[unix-history] / usr / src / bin / cp / cp.c
CommitLineData
1f978f4c 1/*
ae5e5236
KB
2 * Cp copies source files to target files.
3 *
4 * The global path_t structures "to" and "from" always contain paths to the
5 * current source and target files, respectively. Since cp does not
6 * change directories, these paths can be either absolute or
7 * dot-realative.
8 *
9 * The basic algorithm is to initialize "to" and "from", and then call the
10 * recursive copy() function to do the actual work. If "from" is a file,
11 * copy copies the data. If "from" is a directory, copy creates the
12 * corresponding "to" directory, and calls itself recursively on all of
13 * the entries in the "from" directory.
14 *
15 * Instead of handling directory entires in the order they appear one disk,
16 * copy() does non-directory files before directory files.
17 *
18 * There are two reasons to do directories last. The first is efficiency.
19 * Files tend to be in the same cylinder group as their parent, whereas
20 * directories tend not to be. Copying files all at once reduces seeking.
21 *
22 * Second, deeply nested tree's could use up all the file descriptors if we
23 * didn't close one directory before recursivly starting on the next.
1f978f4c
KM
24 */
25
84592a94 26#include <stdio.h>
ae5e5236
KB
27#include <errno.h>
28#include <strings.h>
29
30#include <sys/types.h>
84592a94 31#include <sys/stat.h>
ae5e5236 32#include <sys/file.h>
7afd0a98 33#include <sys/dir.h>
0908a03a 34#include <sys/time.h>
5f89032b 35
ae5e5236
KB
36#include <sys/param.h>
37
38
39#define TRUE (1)
40#define FALSE (0)
41
42typedef struct {
43 char *p_path; /* Pointer to the start of a path. */
44 char *p_end; /* Pointer to NULL at end of path. */
45} path_t;
46
47int path_set();
48char *path_append();
49char *path_basename();
50void path_restore();
51
52
53int exit_val;
54int my_umask;
55int interactive_flag,
56 preserve_flag,
57 recursive_flag;
58
59char *buf; /* I/O buffer -- malloc for best alignment. */
60
61char from_buf[MAXPATHLEN + 1], /* Source path buffer. */
62 to_buf[MAXPATHLEN + 1]; /* Target path buffer. */
63
64path_t from = {from_buf, from_buf};
65path_t to = {to_buf, to_buf};
66
67
68
69usage()
70{
71 (void) fprintf(stderr,
72 "usage: cp [-irp] f1 f2; or: cp [-irp] f1 ... fn d\n");
73 exit(1);
74}
75
76
84592a94
BJ
77
78main(argc, argv)
ae5e5236
KB
79 int argc;
80 char **argv;
84592a94 81{
ae5e5236
KB
82 extern int optind;
83 extern int opterr;
84 extern char *optarg;
85 struct stat to_stat;
86 char *old_to;
87 int c;
88 int r;
89
90 opterr = 0;
91 while ((c = getopt(argc, argv, "Ripr")) != EOF) {
92 switch ((char) c) {
93 case 'i':
94 interactive_flag = isatty(fileno(stdin));
95 break;
96 case 'p':
97 preserve_flag = 1;
98 (void) umask(0);
99 break;
100 case 'R':
101 case 'r':
102 recursive_flag = 1;
103 break;
104 case '?':
105 default:
106 usage();
107 break;
84592a94 108 }
ae5e5236
KB
109 }
110
111 argc -= optind; /* argc is count of remaining arguments. */
112 argv += optind; /* argv[0] is first remaining argument. */
113
114 if (argc < 2) {
115 usage();
116 }
117
118 my_umask = umask(0);
119 (void) umask(my_umask);
120
121 buf = (char *) malloc(MAXBSIZE);
122 if (!buf) {
123 fprintf(stderr, "cp: Can't allocate memory for I/O buffer.\n");
124 exit(1);
125 }
126
127 /*
128 * Consume last argument first.
129 */
130 if ( !path_set(&to, argv[--argc]) )
131 exit(exit_val);
132
133 /*
134 * Cp has two distinct cases:
135 *
136 * Case (1) $ cp [-rip] source target
137 *
138 * Case (2) $ cp [-rip] source1 ... directory
139 *
140 * In both cases, source can be either a file or a directory.
141 *
142 * In (1), the target becomes a copy of the source. That is, if the
143 * source is a file, the target will be a file, and likewise for
144 * directories.
145 *
146 * In (2), the real target is not directory, but "directory/source".
147 */
148 r = stat(to.p_path, &to_stat);
149 if (r == -1 && errno != ENOENT) {
150 error(to.p_path);
84592a94 151 exit(1);
ae5e5236
KB
152 }
153
154 if (r == -1 || (to_stat.st_mode & S_IFMT) != S_IFDIR) {
155 /*
156 * Case (1). Target is not a directory.
157 */
158 if (argc > 1) {
159 usage();
160 exit(1);
161 }
162
163 if ( !path_set(&from, *argv))
164 exit(exit_val);
165
166 copy();
167 }
168 else {
169 /*
170 * Case (2). Target is a directory.
171 */
172 for (; argc; --argc, ++argv) {
173 if ( !path_set(&from, *argv) )
174 continue;
175
176 old_to = path_append(&to, path_basename(&from), -1);
177 if ( !old_to )
178 continue;
179
180 copy();
181 path_restore(&to, old_to);
182 }
183 }
184 exit(exit_val);
84592a94
BJ
185}
186
ded1b933 187
ae5e5236
KB
188
189/*
190 * Copy file or directory at "from" to "to".
191 */
192copy()
84592a94 193{
ae5e5236
KB
194 struct stat from_stat;
195 struct stat to_stat;
196 int new_target_dir = 0;
197
198 if (stat(from.p_path, &from_stat) == -1) {
199 error(from.p_path);
200 return;
201 }
202
203 if (stat(to.p_path, &to_stat) == -1) {
204 /*
205 * This is not an error, but we need to remember that it happened.
206 */
207 to_stat.st_ino = -1;
208 }
209 else {
210 if (to_stat.st_dev == from_stat.st_dev &&
211 to_stat.st_ino == from_stat.st_ino) {
212 fprintf(stderr,
213 "cp: \"%s\" and \"%s\" are identical (not copied).\n",
214 to.p_path, from.p_path);
215 exit_val = 1;
216 return;
84592a94 217 }
ae5e5236
KB
218 }
219
220 if ((from_stat.st_mode & S_IFMT) != S_IFDIR) {
221 if (!copy_file(from_stat.st_mode))
222 return;
223 }
224 else {
225 if (!recursive_flag) {
226 (void) fprintf(stderr,
227 "cp: \"%s\" is a directory (not copied).\n",
228 from.p_path);
229 exit_val = 1;
230 return;
84592a94 231 }
ae5e5236
KB
232
233 if (to_stat.st_ino == -1) {
234 if (mkdir(to.p_path, 0777) < 0) {
235 error(to.p_path);
236 return;
237 }
238 new_target_dir = TRUE;
5f89032b 239 }
ae5e5236
KB
240 else if ((to_stat.st_mode & S_IFMT) != S_IFDIR) {
241 (void) fprintf(stderr,
242 "cp: \"%s\": not a directory.\n",
243 to.p_path);
244 return;
5f89032b 245 }
685d778a 246
ae5e5236
KB
247 copy_dir();
248 }
249
250
251 /*
252 * Preserve old times/modes if necessary.
253 */
254 if (preserve_flag)
255 (void) chmod(to.p_path, (int) from_stat.st_mode);
256 else if (new_target_dir)
257 (void) chmod(to.p_path, (int) from_stat.st_mode & ~my_umask);
258
259 if (preserve_flag || new_target_dir) {
260 static struct timeval tv[2];
261
262 tv[0].tv_sec = from_stat.st_atime;
263 tv[1].tv_sec = from_stat.st_mtime;
264 if (utimes(to.p_path, tv)) {
265 error(to.p_path);
84592a94 266 }
ae5e5236
KB
267 }
268}
269
270
271
272copy_file(mode)
273 u_short mode; /* Permissions for new file. */
274{
275 int from_fd;
276 int to_fd;
277 int rcount;
278 int wcount;
279 int r;
280 char c;
281
282 from_fd = open(from.p_path, O_RDONLY, 0);
283 if (from_fd == -1) {
284 error(from.p_path);
285 (void) close(from_fd);
286 return 0;
287 }
288
289 /*
290 * In the interactive case, use O_EXCL to notice existing files. If
291 * the file exists, verify with the user.
292 */
293 to_fd = open(to.p_path,
294 (interactive_flag ? O_EXCL : 0) | O_WRONLY | O_CREAT | O_TRUNC,
295 mode);
296
297 if (to_fd == -1 && errno == EEXIST && interactive_flag) {
298 (void) fprintf(stderr, "overwrite \"%s\"? ", to.p_path);
299 r = scanf("%1s", &c);
300 if (r != 1 || c != 'y')
301 return FALSE;
302
303 /* Try again. */
304 to_fd = open(to.p_path, O_WRONLY | O_CREAT | O_TRUNC, mode);
305 }
306
307 if (to_fd == -1) {
308 error(to.p_path);
309 (void) close(from_fd);
310 return FALSE;
311 }
312
313 while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
314 wcount = write(to_fd, buf, rcount);
315 if (rcount != wcount || wcount == -1) {
316 error(from.p_path);
317 break;
5f89032b 318 }
ae5e5236
KB
319 }
320
321 (void) close(from_fd);
322 (void) close(to_fd);
323 return TRUE;
5f89032b
BJ
324}
325
ae5e5236
KB
326
327
328copy_dir()
5f89032b 329{
ae5e5236
KB
330 struct stat from_stat;
331 char *old_from;
332 char *old_to;
333 struct direct *dp;
334 struct direct **dir_list;
335 int dir_cnt;
336 int i;
337
338 dir_cnt = scandir(from.p_path, &dir_list, NULL, NULL);
339 if (dir_cnt == -1) {
340 (void) fprintf(stderr, "cp: Can't read directory \"%s\".\n",
341 from.p_path);
342 exit_val = 1;
343 }
344
345 /*
346 * Copy files first.
347 */
348 for (i = 0; i < dir_cnt; ++i) {
349 dp = dir_list[i];
350
351 if (dp->d_namlen <= 2 && dp->d_name[0] == '.'
352 && (dp->d_name[1] == NULL || dp->d_name[1] == '.')) {
353 free((char *) dp);
354 dir_list[i] = NULL;
355 continue;
356 }
357
358 old_from = path_append(&from, dp->d_name, (int) dp->d_namlen);
359 if ( !old_from ) {
360 dir_list[i] = NULL;
361 free((char *) dp);
362 continue;
363 }
364
365 if (stat(from.p_path, &from_stat) < 0) {
366 error(dp->d_name);
367 path_restore(&from, old_from);
368 continue;
369 }
370
371 if ((from_stat.st_mode & S_IFMT) != S_IFDIR) {
372 old_to = path_append(&to, dp->d_name, (int) dp->d_namlen);
373 if ( !old_to ) {
374 dir_list[i] = NULL;
375 free((char *) dp);
376 continue;
377 }
378
379 copy();
380
381 path_restore(&to, old_to);
382
383 dir_list[i] = NULL;
384 free((char *) dp);
385 }
386 path_restore(&from, old_from);
387 }
388
389 /*
390 * Then copy directories.
391 */
392 for (i = 0; i < dir_cnt; ++i) {
393 dp = dir_list[i];
394 if (!dp)
395 continue;
396
397 old_from = path_append(&from, dp->d_name, (int) dp->d_namlen);
398 if ( !old_from ) {
399 free((char *) dp);
400 continue;
5f89032b 401 }
ae5e5236
KB
402
403 old_to = path_append(&to, dp->d_name, (int) dp->d_namlen);
404 if ( !old_to ) {
405 free((char *) dp);
406 path_restore(&from, old_from);
407 continue;
5f89032b 408 }
ae5e5236
KB
409
410 copy();
411 free((char *) dp);
412
413 path_restore(&from, old_from);
414 path_restore(&to, old_to);
415 }
416
417 free((char *) dir_list);
5f89032b 418}
1a4b831f 419
ae5e5236
KB
420
421
422error(s)
423 char *s;
0908a03a 424{
ae5e5236
KB
425 exit_val = 1;
426 (void) fprintf(stderr, "cp: ");
427 perror(s);
0908a03a
JL
428}
429
ae5e5236
KB
430
431
432/********************************************************************
433 * Path Manipulation Routines.
434 ********************************************************************/
435
436/*
437 * These functions manipulate paths in "path_t" structures.
438 *
439 * They eliminate multiple slashes in paths when they notice them, and keep
440 * the path non-slash terminated.
441 *
442 * Both path_set() and path_append() return FALSE if the requested name
443 * would be too long.
444 */
445
446#define STRIP_TRAILING_SLASH(p) \
447 while ((p)->p_end > (p)->p_path && (p)->p_end[-1] == '/') \
448 { *--(p)->p_end = 0; };
449
450/*
451 * Move specified string into path. Convert "" to "." to handle BSD
452 * semantics for a null path. Strip trailing slashes.
453 */
454path_set(p, string)
455 path_t *p;
456 char *string;
457{
458 int len;
459
460 if (strlen(string) > MAXPATHLEN) {
461 fprintf(stderr, "cp: \"%s\": Name too long.\n", string);
462 exit_val = 1;
463 return FALSE;
464 }
465
466 (void) strcpy(p->p_path, string);
467 p->p_end = p->p_path + strlen(p->p_path);
468
469 if (p->p_path == p->p_end) {
470 *p->p_end++ = '.';
471 *p->p_end = 0;
472 }
473
474 STRIP_TRAILING_SLASH(p);
475
476 return TRUE;
477}
478
479/*
480 * Append specified string to path, inserting '/' if necessary. Return a
481 * pointer to the old end of path for restoration.
482 */
483char *
484path_append(p, name, len)
485 path_t *p;
486 char *name;
487 int len;
1a4b831f 488{
ae5e5236
KB
489 char *old;
490
491 old = p->p_end;
492
493 if (len == -1)
494 len = strlen(name);
495
496 /*
497 * The final "+ 1" accounts for the '/' between old path and name.
498 */
499 if ( (len + p->p_end - p->p_path + 1) > MAXPATHLEN ) {
500 fprintf(stderr, "cp: \"%s/%s\": Name too long.\n", p->p_path, name);
501 exit_val = 1;
502 return FALSE;
503 }
504
505 /*
506 * This code should always be executed, since paths shouldn't
507 * end in '/'.
508 */
509 if (p->p_end[-1] != '/') {
510 *p->p_end++ = '/';
511 *p->p_end = 0;
512 }
513
514 (void) strncat(p->p_end, name, len);
515 p->p_end += len;
516 *p->p_end = 0;
517
518 STRIP_TRAILING_SLASH(p);
519
520 return old;
521}
522
523
524/*
525 * Restore path to previous value. (As returned by path_append.)
526 */
527void
528path_restore(p, old)
529 path_t *p;
530 char *old;
531{
532 p->p_end = old;
533 *p->p_end = 0;
534}
535
536
537/*
538 * Return basename of path. (Like basename(1).)
539 */
540char *
541path_basename(p)
542 path_t *p;
543{
544 char *basename;
545
546 basename = rindex(p->p_path, '/');
547
548 if (!basename)
549 basename = p->p_path;
1a4b831f 550
ae5e5236 551 return basename;
1a4b831f 552}