1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1992-2010 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Common Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * Glenn Fowler <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
19 * *
20 ***********************************************************************/
21 #pragma prototyped
22 /*
23 * Glenn Fowler
24 * AT&T Research
25 *
26 * cp/ln/mv -- copy/link/move files
27 */
28
29 static const char usage_head[] =
30 "[-?@(#)$Id: cp (AT&T Research) 2010-01-20 $\n]"
31 USAGE_LICENSE
32 ;
33
34 static const char usage_cp[] =
35 "[+NAME?cp - copy files]"
36 "[+DESCRIPTION?If the last argument names an existing directory, \bcp\b"
37 " copies each \afile\a into a file with the same name in that"
38 " directory. Otherwise, if only two files are given, \bcp\b copies"
39 " the first onto the second. It is an error if the last argument is"
40 " not a directory and more than two files are given. By default"
41 " directories are not copied.]"
42
43 "[a:archive?Preserve as much as possible of the structure and attributes of"
44 " the original files in the copy. Equivalent to \b--physical\b"
45 " \b--preserve\b \b--recursive\b.]"
46 "[p:preserve?Preserve file owner, group, permissions and timestamps.]"
47 "[h:hierarchy|parents?Form the name of each destination file by appending"
48 " to the target directory a slash and the specified source file name."
49 " The last argument must be an existing directory. Missing destination"
50 " directories are created.]"
51 "[H:metaphysical?Follow command argument symbolic links, otherwise don't"
52 " follow.]"
53 "[l:link?Make hard links to destination files instead of copies.]"
54 "[L:logical|dereference?Follow symbolic links and copy the files"
55 " they point to.]"
56 "[P|d:physical|nodereference?Don't follow symbolic links; copy symbolic"
57 " rather than the files they point to.]"
58 ;
59
60 static const char usage_ln[] =
61 "[+NAME?ln - link files]"
62 "[+DESCRIPTION?If the last argument names an existing directory, \bln\b"
63 " links each \afile\a into a file with the same name in that"
64 " directory. Otherwise, if only two files are given, \bln\b links"
65 " the first onto the second. It is an error if the last argument is"
66 " not a directory and more than two files are given. By default"
67 " directories are not linked.]"
68 ;
69
70 static const char usage_mv[] =
71 "[+NAME?mv - rename files]"
72 "[+DESCRIPTION?If the last argument names an existing directory, \bmv\b"
73 " renames each \afile\a into a file with the same name in that"
74 " directory. Otherwise, if only two files are given, \bmv\b renames"
75 " the first onto the second. It is an error if the last argument is"
76 " not a directory and more than two files are given. If a source and"
77 " destination file reside on different filesystems then \bmv\b copies"
78 " the file contents to the destination and then deletes the source"
79 " file.]"
80 ;
81
82 static const char usage_tail[] =
83 "[f:force?Replace existing destination files.]"
84 "[i:interactive|prompt?Prompt whether to replace existing destination files."
85 " An affirmative response (\by\b or \bY\b) replaces the file, a quit"
86 " response (\bq\b or \bQ\b) exits immediately, and all other"
87 " responses skip the file.]"
88 "[r|R:recursive?Operate on the contents of directories recursively.]"
89 "[s:symlink|symbolic-link?Make symbolic links to destination files.]"
90 "[u:update?Replace a destination file only if its modification time is older"
91 " than the corresponding source file modification time.]"
92 "[v:verbose?Print the name of each file before operating on it.]"
93 "[b:backup?Make backups of files that are about to be replaced. See"
94 " \b--suffix\b and \b--version-control\b for more information.]"
95 "[F:fsync|sync?\bfsync\b(2) each file after it is copied.]"
96 "[S:backup-suffix|suffix?A backup file is made by renaming the file to the"
97 " same name with the backup suffix appended. The backup suffix is"
98 " determined in this order: this option, the \bSIMPLE_BACKUP_SUFFIX\b,"
99 " environment variable, or the default value \b~\b.]:[suffix]"
100 "[V:backup-type|version-control?The backup type is determined in this order:"
101 " this option, the \bVERSION_CONTROL\b environment variable, or the"
102 " default value \bexisting\b. \atype\a may be one of:]:[type]{"
103 " [+numbered|t?Always make numbered backups. The numbered backup"
104 " suffix is \b.\aSNS\a, where \aS\a is the"
105 " \bbackup-suffix\b and \aN\a is the version number,"
106 " starting at 1, incremented with each version.]"
107 " [+existing|nil?Make numbered backups of files that already"
108 " have them, otherwise simple backups.]"
109 " [+simple|never?Always make simple backups.]"
110 "}"
111 "[x|X|l:xdev|local|mount|one-file-system?Do not descend into directories in"
112 " different filesystems than their parents.]"
113
114 "\n"
115 "\nsource destination\n"
116 "file ... directory\n"
117 "\n"
118
119 "[+SEE ALSO?\bpax\b(1), \bfsync\b(2), \brename\b(2), \bunlink\b(2),"
120 " \bremove\b(3)]"
121 ;
122
123 #include <cmd.h>
124 #include <ls.h>
125 #include <times.h>
126 #include <fts_fix.h>
127 #include <fs3d.h>
128 #include <hashkey.h>
129 #include <stk.h>
130 #include <tmx.h>
131
132 #define PATH_CHUNK 256
133
134 #define CP 1
135 #define LN 2
136 #define MV 3
137
138 #define BAK_replace 0 /* no backup -- just replace */
139 #define BAK_existing 1 /* number if already else simple*/
140 #define BAK_number 2 /* append .suffix number suffix */
141 #define BAK_simple 3 /* append suffix */
142
143 typedef struct State_s /* program state */
144 {
145 void* context; /* builtin context */
146 int backup; /* BAK_* type */
147 int directory; /* destination is directory */
148 int flags; /* FTS_* flags */
149 int force; /* force approval */
150 int fs3d; /* 3d fs enabled */
151 int hierarchy; /* preserve hierarchy */
152 int interactive; /* prompt for approval */
153 int missmode; /* default missing dir mode */
154 int official; /* move to next view */
155 int op; /* {CP,LN,MV} */
156 int perm; /* permissions to preserve */
157 int postsiz; /* state.path post index */
158 int presiz; /* state.path pre index */
159 int preserve; /* preserve { id mode time } */
160 int recursive; /* subtrees too */
161 int suflen; /* strlen(state.suffix) */
162 int sync; /* fsync() each file after copy */
163 int uid; /* caller uid */
164 int update; /* replace only if newer */
165 int verbose; /* list each file before op */
166 int wflags; /* open() for write flags */
167
168 int (*link)(const char*, const char*); /* link */
169 int (*stat)(const char*, struct stat*); /* stat */
170
171 #define INITSTATE pathsiz /* (re)init state before this */
172 int pathsiz; /* state.path buffer size */
173
174
175 char* path; /* to pathname buffer */
176 char* opname; /* state.op message string */
177 char* suffix; /* backup suffix */
178
179 Sfio_t* tmp; /* tmp string stream */
180
181 char text[PATH_MAX]; /* link text buffer */
182 } State_t;
183
184 static const char dot[2] = { '.' };
185
186 /*
187 * preserve support
188 */
189
190 static void
preserve(State_t * state,const char * path,struct stat * ns,struct stat * os)191 preserve(State_t* state, const char* path, struct stat* ns, struct stat* os)
192 {
193 int n;
194
195 if (tmxtouch(path, tmxgetatime(os), tmxgetmtime(os), TMX_NOTIME, 0))
196 error(ERROR_SYSTEM|2, "%s: cannot reset access and modify times", path);
197 n = ((ns->st_uid != os->st_uid) << 1) | (ns->st_gid != os->st_gid);
198 if (n && chown(state->path, os->st_uid, os->st_gid))
199 switch (n)
200 {
201 case 01:
202 error(ERROR_SYSTEM|2, "%s: cannot reset group to %s", path, fmtgid(os->st_gid));
203 break;
204 case 02:
205 error(ERROR_SYSTEM|2, "%s: cannot reset owner to %s", path, fmtuid(os->st_uid));
206 break;
207 case 03:
208 error(ERROR_SYSTEM|2, "%s: cannot reset owner to %s and group to %s", path, fmtuid(os->st_uid), fmtgid(os->st_gid));
209 break;
210 }
211 }
212
213 /*
214 * visit a single file and state.op to the destination
215 */
216
217 static int
visit(State_t * state,register FTSENT * ent)218 visit(State_t* state, register FTSENT* ent)
219 {
220 register char* base;
221 register int n;
222 register int len;
223 int rm;
224 int rfd;
225 int wfd;
226 int m;
227 int v;
228 char* s;
229 char* e;
230 char* protection;
231 Sfio_t* ip;
232 Sfio_t* op;
233 FTS* fts;
234 FTSENT* sub;
235 struct stat st;
236
237 if (ent->fts_info == FTS_DC)
238 {
239 error(2, "%s: directory causes cycle", ent->fts_path);
240 fts_set(NiL, ent, FTS_SKIP);
241 return 0;
242 }
243 if (ent->fts_level == 0)
244 {
245 base = ent->fts_name;
246 len = ent->fts_namelen;
247 if (state->hierarchy)
248 state->presiz = -1;
249 else
250 {
251 state->presiz = ent->fts_pathlen;
252 while (*base == '.' && *(base + 1) == '/')
253 for (base += 2; *base == '/'; base++);
254 if (*base == '.' && !*(base + 1))
255 state->presiz--;
256 else if (*base)
257 state->presiz -= base - ent->fts_name;
258 base = ent->fts_name + len;
259 while (base > ent->fts_name && *(base - 1) == '/')
260 base--;
261 while (base > ent->fts_name && *(base - 1) != '/')
262 base--;
263 len -= base - ent->fts_name;
264 if (state->directory)
265 state->presiz -= len + 1;
266 }
267 }
268 else
269 {
270 base = ent->fts_path + state->presiz + 1;
271 len = ent->fts_pathlen - state->presiz - 1;
272 }
273 len++;
274 if (state->directory)
275 {
276 if ((state->postsiz + len) > state->pathsiz && !(state->path = newof(state->path, char, state->pathsiz = roundof(state->postsiz + len, PATH_CHUNK), 0)))
277 error(ERROR_SYSTEM|3, "out of space");
278 if (state->hierarchy && ent->fts_level == 0 && strchr(base, '/'))
279 {
280 s = state->path + state->postsiz;
281 memcpy(s, base, len);
282 while (e = strchr(s, '/'))
283 {
284 *e = 0;
285 if (access(state->path, F_OK))
286 {
287 st.st_mode = state->missmode;
288 if (s = strrchr(s, '/'))
289 {
290 *s = 0;
291 stat(state->path, &st);
292 *s = '/';
293 }
294 if (mkdir(state->path, st.st_mode & S_IPERM))
295 {
296 error(ERROR_SYSTEM|2, "%s: cannot create directory -- %s ignored", state->path, ent->fts_path);
297 fts_set(NiL, ent, FTS_SKIP);
298 return 0;
299 }
300 }
301 *e++ = '/';
302 s = e;
303 }
304 }
305 }
306 switch (ent->fts_info)
307 {
308 case FTS_DP:
309 if (state->preserve && state->op != LN || ent->fts_level > 0 && (ent->fts_statp->st_mode & S_IRWXU) != S_IRWXU)
310 {
311 if (len && ent->fts_level > 0)
312 memcpy(state->path + state->postsiz, base, len);
313 else
314 state->path[state->postsiz] = 0;
315 if (stat(state->path, &st))
316 error(ERROR_SYSTEM|2, "%s: cannot stat", state->path);
317 else
318 {
319 if ((ent->fts_statp->st_mode & S_IPERM) != (st.st_mode & S_IPERM) && chmod(state->path, ent->fts_statp->st_mode & S_IPERM))
320 error(ERROR_SYSTEM|2, "%s: cannot reset directory mode to %s", state->path, fmtmode(st.st_mode & S_IPERM, 0) + 1);
321 if (state->preserve)
322 preserve(state, state->path, &st, ent->fts_statp);
323 }
324 }
325 return 0;
326 case FTS_DNR:
327 case FTS_DNX:
328 case FTS_D:
329 if (!state->recursive)
330 {
331 fts_set(NiL, ent, FTS_SKIP);
332 if (state->op == CP)
333 error(1, "%s: directory -- copying as plain file", ent->fts_path);
334 else if (state->link == link && !state->force)
335 {
336 error(2, "%s: cannot link directory", ent->fts_path);
337 return 0;
338 }
339 }
340 else switch (ent->fts_info)
341 {
342 case FTS_DNR:
343 error(2, "%s: cannot read directory", ent->fts_path);
344 return 0;
345 case FTS_DNX:
346 error(2, "%s: cannot search directory", ent->fts_path);
347 fts_set(NiL, ent, FTS_SKIP);
348
349 /*FALLTHROUGH*/
350 case FTS_D:
351 if (state->directory)
352 memcpy(state->path + state->postsiz, base, len);
353 if (!(*state->stat)(state->path, &st))
354 {
355 if (!S_ISDIR(st.st_mode))
356 {
357 error(2, "%s: not a directory -- %s ignored", state->path, ent->fts_path);
358 return 0;
359 }
360 }
361 else if (mkdir(state->path, (ent->fts_statp->st_mode & S_IPERM)|(ent->fts_info == FTS_D ? S_IRWXU : 0)))
362 {
363 error(ERROR_SYSTEM|2, "%s: cannot create directory -- %s ignored", state->path, ent->fts_path);
364 fts_set(NiL, ent, FTS_SKIP);
365 }
366 if (!state->directory)
367 {
368 state->directory = 1;
369 state->path[state->postsiz++] = '/';
370 state->presiz--;
371 }
372 return 0;
373 }
374 break;
375 case FTS_ERR:
376 case FTS_NS:
377 case FTS_SLNONE:
378 if (state->link != pathsetlink)
379 {
380 error(2, "%s: not found", ent->fts_path);
381 return 0;
382 }
383 break;
384 #if 0
385 case FTS_SL:
386 if (state->op == CP)
387 {
388 error(2, "%s: cannot copy non-terminal symbolic link", ent->fts_path);
389 return 0;
390 }
391 break;
392 #endif
393 }
394 if (state->directory)
395 memcpy(state->path + state->postsiz, base, len);
396 if ((*state->stat)(state->path, &st))
397 st.st_mode = 0;
398 else if (state->update && !S_ISDIR(st.st_mode) && (unsigned long)ent->fts_statp->st_mtime < (unsigned long)st.st_mtime)
399 {
400 fts_set(NiL, ent, FTS_SKIP);
401 return 0;
402 }
403 else if (!state->fs3d || !iview(&st))
404 {
405 /*
406 * target is in top 3d view
407 */
408
409 if (st.st_dev == ent->fts_statp->st_dev && st.st_ino == ent->fts_statp->st_ino)
410 {
411 if (state->op == MV)
412 {
413 /*
414 * let rename() handle it
415 */
416
417 if (state->verbose)
418 sfputr(sfstdout, state->path, '\n');
419 goto operate;
420 }
421 if (!state->official)
422 error(2, "%s: identical to %s", state->path, ent->fts_path);
423 return 0;
424 }
425 if (S_ISDIR(st.st_mode))
426 {
427 error(2, "%s: cannot %s existing directory", state->path, state->opname);
428 return 0;
429 }
430 if (state->verbose)
431 sfputr(sfstdout, state->path, '\n');
432 rm = state->op == LN || ent->fts_info == FTS_SL;
433 if (!rm || !state->force)
434 {
435 if ((n = open(state->path, O_RDWR|O_BINARY)) >= 0)
436 {
437 close(n);
438 if (state->force)
439 /* ok */;
440 else if (state->interactive)
441 {
442 if (astquery(-1, "%s %s? ", state->opname, state->path) < 0 || sh_checksig(state->context))
443 return 0;
444 }
445 else if (state->op == LN)
446 {
447 error(2, "%s: cannot %s existing file", state->path, state->opname);
448 return 0;
449 }
450 }
451 else if (state->force)
452 rm = 1;
453 else
454 {
455 protection =
456 #ifdef ETXTBSY
457 errno == ETXTBSY ? "``running program''" :
458 #endif
459 st.st_uid != state->uid ? "``not owner''" :
460 fmtmode(st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO), 0) + 1;
461 if (state->interactive)
462 {
463 if (astquery(-1, "override protection %s for %s? ", protection, state->path) < 0 || sh_checksig(state->context))
464 return 0;
465 rm = 1;
466 }
467 else if (!rm)
468 {
469 error(2, "%s: cannot %s %s protection", state->path, state->opname, protection);
470 return 0;
471 }
472 }
473 }
474 switch (state->backup)
475 {
476 case BAK_existing:
477 case BAK_number:
478 v = 0;
479 if (s = strrchr(state->path, '/'))
480 {
481 e = state->path;
482 *s++ = 0;
483 }
484 else
485 {
486 e = (char*)dot;
487 s = state->path;
488 }
489 n = strlen(s);
490 if (fts = fts_open((char**)e, FTS_NOCHDIR|FTS_ONEPATH|FTS_PHYSICAL|FTS_NOPOSTORDER|FTS_NOSTAT|FTS_NOSEEDOTDIR, NiL))
491 {
492 while (sub = fts_read(fts))
493 {
494 if (strneq(s, sub->fts_name, n) && sub->fts_name[n] == '.' && strneq(sub->fts_name + n + 1, state->suffix, state->suflen) && (m = strtol(sub->fts_name + n + state->suflen + 1, &e, 10)) && streq(e, state->suffix) && m > v)
495 v = m;
496 if (sub->fts_level)
497 fts_set(NiL, sub, FTS_SKIP);
498 }
499 fts_close(fts);
500 }
501 if (s != state->path)
502 *--s = '/';
503 if (v || state->backup == BAK_number)
504 {
505 sfprintf(state->tmp, "%s.%s%d%s", state->path, state->suffix, v + 1, state->suffix);
506 goto backup;
507 }
508 /*FALLTHROUGH*/
509 case BAK_simple:
510 sfprintf(state->tmp, "%s%s", state->path, state->suffix);
511 backup:
512 if (!(s = sfstruse(state->tmp)))
513 error(ERROR_SYSTEM|3, "%s: out of space", state->path);
514 if (rename(state->path, s))
515 {
516 error(ERROR_SYSTEM|2, "%s: cannot backup to %s", state->path, s);
517 return 0;
518 }
519 break;
520 default:
521 if (rm && remove(state->path))
522 {
523 error(ERROR_SYSTEM|2, "%s: cannot remove", state->path);
524 return 0;
525 }
526 break;
527 }
528 }
529 operate:
530 switch (state->op)
531 {
532 case MV:
533 for (;;)
534 {
535 if (!rename(ent->fts_path, state->path))
536 return 0;
537 if (errno == ENOENT)
538 rm = 1;
539 else if (!rm && st.st_mode && !remove(state->path))
540 {
541 rm = 1;
542 continue;
543 }
544 if (errno != EXDEV && (rm || S_ISDIR(ent->fts_statp->st_mode)))
545 {
546 error(ERROR_SYSTEM|2, "%s: cannot rename to %s", ent->fts_path, state->path);
547 return 0;
548 }
549 else
550 break;
551 }
552 /*FALLTHROUGH*/
553 case CP:
554 if (S_ISLNK(ent->fts_statp->st_mode))
555 {
556 if ((n = pathgetlink(ent->fts_path, state->text, sizeof(state->text) - 1)) < 0)
557 {
558 error(ERROR_SYSTEM|2, "%s: cannot read symbolic link text", ent->fts_path);
559 return 0;
560 }
561 state->text[n] = 0;
562 if (pathsetlink(state->text, state->path))
563 {
564 error(ERROR_SYSTEM|2, "%s: cannot copy symbolic link to %s", ent->fts_path, state->path);
565 return 0;
566 }
567 }
568 else if (state->op == CP || S_ISREG(ent->fts_statp->st_mode) || S_ISDIR(ent->fts_statp->st_mode))
569 {
570 if (ent->fts_statp->st_size > 0 && (rfd = open(ent->fts_path, O_RDONLY|O_BINARY)) < 0)
571 {
572 error(ERROR_SYSTEM|2, "%s: cannot read", ent->fts_path);
573 return 0;
574 }
575 else if ((wfd = open(state->path, st.st_mode ? (state->wflags & ~O_EXCL) : state->wflags, ent->fts_statp->st_mode & state->perm)) < 0)
576 {
577 error(ERROR_SYSTEM|2, "%s: cannot write", state->path);
578 if (ent->fts_statp->st_size > 0)
579 close(rfd);
580 return 0;
581 }
582 else if (ent->fts_statp->st_size > 0)
583 {
584 if (!(ip = sfnew(NiL, NiL, SF_UNBOUND, rfd, SF_READ)))
585 {
586 error(ERROR_SYSTEM|2, "%s: %s read stream error", ent->fts_path, state->path);
587 close(rfd);
588 close(wfd);
589 }
590 else
591 {
592 n = 0;
593 if (!(op = sfnew(NiL, NiL, SF_UNBOUND, wfd, SF_WRITE)))
594 {
595 error(ERROR_SYSTEM|2, "%s: %s write stream error", ent->fts_path, state->path);
596 close(wfd);
597 sfclose(ip);
598 }
599 else
600 {
601 if (sfmove(ip, op, (Sfoff_t)SF_UNBOUND, -1) < 0)
602 n |= 3;
603 if (!sfeof(ip))
604 n |= 1;
605 if (sfsync(op) || state->sync && fsync(wfd) || sfclose(op))
606 n |= 2;
607 if (sfclose(ip))
608 n |= 1;
609 if (n)
610 error(ERROR_SYSTEM|2, "%s: %s %s error", ent->fts_path, state->path, n == 1 ? ERROR_translate(0, 0, 0, "read") : n == 2 ? ERROR_translate(0, 0, 0, "write") : ERROR_translate(0, 0, 0, "io"));
611 }
612 }
613 }
614 else
615 close(wfd);
616 }
617 else if (S_ISBLK(ent->fts_statp->st_mode) || S_ISCHR(ent->fts_statp->st_mode) || S_ISFIFO(ent->fts_statp->st_mode))
618 {
619 if (mknod(state->path, ent->fts_statp->st_mode, idevice(ent->fts_statp)))
620 {
621 error(ERROR_SYSTEM|2, "%s: cannot copy special file to %s", ent->fts_path, state->path);
622 return 0;
623 }
624 }
625 else
626 {
627 error(2, "%s: cannot copy -- unknown file type 0%o", ent->fts_path, S_ITYPE(ent->fts_statp->st_mode));
628 return 0;
629 }
630 if (state->preserve)
631 {
632 if (ent->fts_info != FTS_SL)
633 {
634 if (stat(state->path, &st))
635 error(ERROR_SYSTEM|2, "%s: cannot stat", state->path);
636 else
637 {
638 if ((ent->fts_statp->st_mode & state->perm) != (st.st_mode & state->perm) && chmod(state->path, ent->fts_statp->st_mode & state->perm))
639 error(ERROR_SYSTEM|2, "%s: cannot reset mode to %s", state->path, fmtmode(st.st_mode & state->perm, 0) + 1);
640 preserve(state, state->path, &st, ent->fts_statp);
641 }
642 }
643 if (state->op == MV && remove(ent->fts_path))
644 error(ERROR_SYSTEM|1, "%s: cannot remove", ent->fts_path);
645 }
646 break;
647 case LN:
648 if ((*state->link)(ent->fts_path, state->path))
649 error(ERROR_SYSTEM|2, "%s: cannot link to %s", ent->fts_path, state->path);
650 break;
651 }
652 return 0;
653 }
654
655 int
b_cp(int argc,register char ** argv,void * context)656 b_cp(int argc, register char** argv, void* context)
657 {
658 register char* file;
659 register char* s;
660 char** v;
661 char* backup_type;
662 FTS* fts;
663 FTSENT* ent;
664 const char* usage;
665 int path_resolve;
666 int standard;
667 struct stat st;
668 State_t* state;
669 Shbltin_t* sh;
670 void* cleanup = context;
671
672 cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
673 if (!(sh = CMD_CONTEXT(context)) || !(state = (State_t*)sh->ptr))
674 {
675 if (!(state = newof(0, State_t, 1, 0)))
676 error(ERROR_SYSTEM|3, "out of space");
677 if (sh)
678 sh->ptr = state;
679 }
680 else
681 memset(state, 0, offsetof(State_t, INITSTATE));
682 state->context = context;
683 state->presiz = -1;
684 backup_type = 0;
685 state->flags = FTS_NOCHDIR|FTS_NOSEEDOTDIR;
686 state->uid = geteuid();
687 state->wflags = O_WRONLY|O_CREAT|O_TRUNC|O_BINARY;
688 if (!state->tmp && !(state->tmp = sfstropen()))
689 error(ERROR_SYSTEM|3, "out of space [tmp string]");
690 sfputr(state->tmp, usage_head, -1);
691 standard = !strcmp(astconf("CONFORMANCE", NiL, NiL), "standard");
692 switch (error_info.id[0])
693 {
694 case 'c':
695 case 'C':
696 sfputr(state->tmp, usage_cp, -1);
697 state->op = CP;
698 state->stat = stat;
699 path_resolve = -1;
700 break;
701 case 'l':
702 case 'L':
703 sfputr(state->tmp, usage_ln, -1);
704 state->op = LN;
705 state->flags |= FTS_PHYSICAL;
706 state->link = link;
707 state->stat = lstat;
708 path_resolve = 1;
709 break;
710 case 'm':
711 case 'M':
712 sfputr(state->tmp, usage_mv, -1);
713 state->op = MV;
714 state->flags |= FTS_PHYSICAL;
715 state->preserve = 1;
716 state->stat = lstat;
717 path_resolve = 1;
718 break;
719 default:
720 error(3, "not implemented");
721 break;
722 }
723 sfputr(state->tmp, usage_tail, -1);
724 if (!(usage = sfstruse(state->tmp)))
725 error(ERROR_SYSTEM|3, "%s: out of space", state->path);
726 state->opname = state->op == CP ? ERROR_translate(0, 0, 0, "overwrite") : ERROR_translate(0, 0, 0, "replace");
727 for (;;)
728 {
729 switch (optget(argv, usage))
730 {
731 case 'a':
732 state->flags |= FTS_PHYSICAL;
733 state->preserve = 1;
734 state->recursive = 1;
735 path_resolve = 1;
736 continue;
737 case 'b':
738 state->backup = 1;
739 continue;
740 case 'f':
741 state->force = 1;
742 if (state->op != CP || !standard)
743 state->interactive = 0;
744 continue;
745 case 'h':
746 state->hierarchy = 1;
747 continue;
748 case 'i':
749 state->interactive = 1;
750 if (state->op != CP || !standard)
751 state->force = 0;
752 continue;
753 case 'l':
754 state->op = LN;
755 state->link = link;
756 state->stat = lstat;
757 continue;
758 case 'p':
759 state->preserve = 1;
760 continue;
761 case 'r':
762 state->recursive = 1;
763 if (path_resolve < 0)
764 path_resolve = 0;
765 continue;
766 case 's':
767 state->op = LN;
768 state->link = pathsetlink;
769 state->stat = lstat;
770 continue;
771 case 'u':
772 state->update = 1;
773 continue;
774 case 'v':
775 state->verbose = 1;
776 continue;
777 case 'x':
778 state->flags |= FTS_XDEV;
779 continue;
780 case 'F':
781 #if _lib_fsync
782 state->sync = 1;
783 #else
784 error(1, "%s not implemented on this system", opt_info.name);
785 #endif
786 continue;
787 case 'H':
788 state->flags |= FTS_META|FTS_PHYSICAL;
789 path_resolve = 1;
790 continue;
791 case 'L':
792 state->flags &= ~FTS_PHYSICAL;
793 path_resolve = 1;
794 continue;
795 case 'P':
796 state->flags &= ~FTS_META;
797 state->flags |= FTS_PHYSICAL;
798 path_resolve = 1;
799 continue;
800 case 'R':
801 state->recursive = 1;
802 state->flags &= ~FTS_META;
803 state->flags |= FTS_PHYSICAL;
804 path_resolve = 1;
805 continue;
806 case 'S':
807 state->suffix = opt_info.arg;
808 continue;
809 case 'V':
810 backup_type = opt_info.arg;
811 continue;
812 case '?':
813 error(ERROR_USAGE|4, "%s", opt_info.arg);
814 continue;
815 case ':':
816 error(2, "%s", opt_info.arg);
817 continue;
818 }
819 break;
820 }
821 argc -= opt_info.index + 1;
822 argv += opt_info.index;
823 if (*argv && streq(*argv, "-") && !streq(*(argv - 1), "--"))
824 {
825 argc--;
826 argv++;
827 }
828 if (!(v = (char**)stkalloc(stkstd, (argc + 2) * sizeof(char*))))
829 error(ERROR_SYSTEM|3, "out of space");
830 memcpy(v, argv, (argc + 1) * sizeof(char*));
831 argv = v;
832 if (!standard)
833 {
834 state->wflags |= O_EXCL;
835 if (!argc)
836 {
837 argc++;
838 argv[1] = (char*)dot;
839 }
840 }
841 if (state->backup)
842 {
843 if (!(file = backup_type) && !(backup_type = getenv("VERSION_CONTROL")))
844 state->backup = BAK_existing;
845 else
846 switch (strkey(backup_type))
847 {
848 case HASHKEY6('e','x','i','s','t','i'):
849 case HASHKEY5('e','x','i','s','t'):
850 case HASHKEY4('e','x','i','s'):
851 case HASHKEY3('e','x','i'):
852 case HASHKEY2('e','x'):
853 case HASHKEY1('e'):
854 case HASHKEY3('n','i','l'):
855 case HASHKEY2('n','i'):
856 state->backup = BAK_existing;
857 break;
858 case HASHKEY5('n','e','v','e','r'):
859 case HASHKEY4('n','e','v','e'):
860 case HASHKEY3('n','e','v'):
861 case HASHKEY2('n','e'):
862 case HASHKEY6('s','i','m','p','l','e'):
863 case HASHKEY5('s','i','m','p','l'):
864 case HASHKEY4('s','i','m','p'):
865 case HASHKEY3('s','i','m'):
866 case HASHKEY2('s','i'):
867 case HASHKEY1('s'):
868 state->backup = BAK_simple;
869 break;
870 case HASHKEY6('n','u','m','b','e','r'):
871 case HASHKEY5('n','u','m','b','e'):
872 case HASHKEY4('n','u','m','b'):
873 case HASHKEY3('n','u','m'):
874 case HASHKEY2('n','u'):
875 case HASHKEY1('t'):
876 state->backup = BAK_number;
877 break;
878 default:
879 if (file)
880 error(2, "%s: unknown backup type", backup_type);
881 break;
882 }
883 if (!state->suffix && !(state->suffix = getenv("SIMPLE_BACKUP_SUFFIX")))
884 state->suffix = "~";
885 state->suflen = strlen(state->suffix);
886 }
887 if (argc <= 0 || error_info.errors)
888 error(ERROR_USAGE|4, "%s", optusage(NiL));
889 if (!path_resolve)
890 state->flags |= fts_flags();
891 file = argv[argc];
892 argv[argc] = 0;
893 if (s = strrchr(file, '/'))
894 {
895 while (*s == '/')
896 s++;
897 if (!(!*s || *s == '.' && (!*++s || *s == '.' && !*++s)))
898 s = 0;
899 }
900 if (file != (char*)dot)
901 pathcanon(file, 0);
902 if (!(state->directory = !stat(file, &st) && S_ISDIR(st.st_mode)) && argc > 1)
903 error(ERROR_USAGE|4, "%s", optusage(NiL));
904 if (s && !state->directory)
905 error(3, "%s: not a directory", file);
906 if ((state->fs3d = fs3d(FS3D_TEST)) && strmatch(file, "...|*/...|.../*"))
907 state->official = 1;
908 state->postsiz = strlen(file);
909 if (state->pathsiz < roundof(state->postsiz + 2, PATH_CHUNK) && !(state->path = newof(state->path, char, state->pathsiz = roundof(state->postsiz + 2, PATH_CHUNK), 0)))
910 error(ERROR_SYSTEM|3, "out of space");
911 memcpy(state->path, file, state->postsiz + 1);
912 if (state->directory && state->path[state->postsiz - 1] != '/')
913 state->path[state->postsiz++] = '/';
914 if (state->hierarchy)
915 {
916 if (!state->directory)
917 error(3, "%s: last argument must be a directory", file);
918 state->missmode = st.st_mode;
919 }
920 state->perm = state->uid ? S_IPERM : (S_IPERM & ~S_ISVTX);
921 if (!state->recursive)
922 state->flags |= FTS_TOP;
923 if (fts = fts_open(argv, state->flags, NiL))
924 {
925 while (!sh_checksig(context) && (ent = fts_read(fts)) && !visit(state, ent));
926 fts_close(fts);
927 }
928 else if (state->link != pathsetlink)
929 switch (state->op)
930 {
931 case CP:
932 error(ERROR_SYSTEM|2, "%s: cannot copy", argv[0]);
933 break;
934 case LN:
935 error(ERROR_SYSTEM|2, "%s: cannot link", argv[0]);
936 break;
937 case MV:
938 error(ERROR_SYSTEM|2, "%s: cannot move", argv[0]);
939 break;
940 }
941 else if ((*state->link)(*argv, state->path))
942 error(ERROR_SYSTEM|2, "%s: cannot link to %s", *argv, state->path);
943 if (cleanup && !sh)
944 {
945 if (state->path)
946 free(state->path);
947 free(state);
948 }
949 return error_info.errors != 0;
950 }
951