1 /***********************************************************************
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 *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
13 * Information and Software Systems Research *
17 * Glenn Fowler <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
20 ***********************************************************************/
26 * cp/ln/mv -- copy/link/move files
29 static const char usage_head
[] =
30 "[-?@(#)$Id: cp (AT&T Research) 2010-01-20 $\n]"
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.]"
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"
53 "[l:link?Make hard links to destination files instead of copies.]"
54 "[L:logical|dereference?Follow symbolic links and copy the files"
56 "[P|d:physical|nodereference?Don't follow symbolic links; copy symbolic"
57 " rather than the files they point to.]"
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.]"
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"
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.]"
111 "[x|X|l:xdev|local|mount|one-file-system?Do not descend into directories in"
112 " different filesystems than their parents.]"
115 "\nsource destination\n"
116 "file ... directory\n"
119 "[+SEE ALSO?\bpax\b(1), \bfsync\b(2), \brename\b(2), \bunlink\b(2),"
132 #define PATH_CHUNK 256
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 */
143 typedef struct State_s
/* program state */
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 */
168 int (*link
)(const char*, const char*); /* link */
169 int (*stat
)(const char*, struct stat
*); /* stat */
171 #define INITSTATE pathsiz /* (re)init state before this */
172 int pathsiz
; /* state.path buffer size */
175 char* path
; /* to pathname buffer */
176 char* opname
; /* state.op message string */
177 char* suffix
; /* backup suffix */
179 Sfio_t
* tmp
; /* tmp string stream */
181 char text
[PATH_MAX
]; /* link text buffer */
184 static const char dot
[2] = { '.' };
191 preserve(State_t
* state
, const char* path
, struct stat
* ns
, struct stat
* os
)
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
))
202 error(ERROR_SYSTEM
|2, "%s: cannot reset group to %s", path
, fmtgid(os
->st_gid
));
205 error(ERROR_SYSTEM
|2, "%s: cannot reset owner to %s", path
, fmtuid(os
->st_uid
));
208 error(ERROR_SYSTEM
|2, "%s: cannot reset owner to %s and group to %s", path
, fmtuid(os
->st_uid
), fmtgid(os
->st_gid
));
214 * visit a single file and state.op to the destination
218 visit(State_t
* state
, register FTSENT
* ent
)
237 if (ent
->fts_info
== FTS_DC
)
239 error(2, "%s: directory causes cycle", ent
->fts_path
);
240 fts_set(NiL
, ent
, FTS_SKIP
);
243 if (ent
->fts_level
== 0)
245 base
= ent
->fts_name
;
246 len
= ent
->fts_namelen
;
247 if (state
->hierarchy
)
251 state
->presiz
= ent
->fts_pathlen
;
252 while (*base
== '.' && *(base
+ 1) == '/')
253 for (base
+= 2; *base
== '/'; base
++);
254 if (*base
== '.' && !*(base
+ 1))
257 state
->presiz
-= base
- ent
->fts_name
;
258 base
= ent
->fts_name
+ len
;
259 while (base
> ent
->fts_name
&& *(base
- 1) == '/')
261 while (base
> ent
->fts_name
&& *(base
- 1) != '/')
263 len
-= base
- ent
->fts_name
;
264 if (state
->directory
)
265 state
->presiz
-= len
+ 1;
270 base
= ent
->fts_path
+ state
->presiz
+ 1;
271 len
= ent
->fts_pathlen
- state
->presiz
- 1;
274 if (state
->directory
)
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
, '/'))
280 s
= state
->path
+ state
->postsiz
;
281 memcpy(s
, base
, len
);
282 while (e
= strchr(s
, '/'))
285 if (access(state
->path
, F_OK
))
287 st
.st_mode
= state
->missmode
;
288 if (s
= strrchr(s
, '/'))
291 stat(state
->path
, &st
);
294 if (mkdir(state
->path
, st
.st_mode
& S_IPERM
))
296 error(ERROR_SYSTEM
|2, "%s: cannot create directory -- %s ignored", state
->path
, ent
->fts_path
);
297 fts_set(NiL
, ent
, FTS_SKIP
);
306 switch (ent
->fts_info
)
309 if (state
->preserve
&& state
->op
!= LN
|| ent
->fts_level
> 0 && (ent
->fts_statp
->st_mode
& S_IRWXU
) != S_IRWXU
)
311 if (len
&& ent
->fts_level
> 0)
312 memcpy(state
->path
+ state
->postsiz
, base
, len
);
314 state
->path
[state
->postsiz
] = 0;
315 if (stat(state
->path
, &st
))
316 error(ERROR_SYSTEM
|2, "%s: cannot stat", state
->path
);
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);
322 preserve(state
, state
->path
, &st
, ent
->fts_statp
);
329 if (!state
->recursive
)
331 fts_set(NiL
, ent
, FTS_SKIP
);
333 error(1, "%s: directory -- copying as plain file", ent
->fts_path
);
334 else if (state
->link
== link
&& !state
->force
)
336 error(2, "%s: cannot link directory", ent
->fts_path
);
340 else switch (ent
->fts_info
)
343 error(2, "%s: cannot read directory", ent
->fts_path
);
346 error(2, "%s: cannot search directory", ent
->fts_path
);
347 fts_set(NiL
, ent
, FTS_SKIP
);
351 if (state
->directory
)
352 memcpy(state
->path
+ state
->postsiz
, base
, len
);
353 if (!(*state
->stat
)(state
->path
, &st
))
355 if (!S_ISDIR(st
.st_mode
))
357 error(2, "%s: not a directory -- %s ignored", state
->path
, ent
->fts_path
);
361 else if (mkdir(state
->path
, (ent
->fts_statp
->st_mode
& S_IPERM
)|(ent
->fts_info
== FTS_D
? S_IRWXU
: 0)))
363 error(ERROR_SYSTEM
|2, "%s: cannot create directory -- %s ignored", state
->path
, ent
->fts_path
);
364 fts_set(NiL
, ent
, FTS_SKIP
);
366 if (!state
->directory
)
368 state
->directory
= 1;
369 state
->path
[state
->postsiz
++] = '/';
378 if (state
->link
!= pathsetlink
)
380 error(2, "%s: not found", ent
->fts_path
);
388 error(2, "%s: cannot copy non-terminal symbolic link", ent
->fts_path
);
394 if (state
->directory
)
395 memcpy(state
->path
+ state
->postsiz
, base
, len
);
396 if ((*state
->stat
)(state
->path
, &st
))
398 else if (state
->update
&& !S_ISDIR(st
.st_mode
) && (unsigned long)ent
->fts_statp
->st_mtime
< (unsigned long)st
.st_mtime
)
400 fts_set(NiL
, ent
, FTS_SKIP
);
403 else if (!state
->fs3d
|| !iview(&st
))
406 * target is in top 3d view
409 if (st
.st_dev
== ent
->fts_statp
->st_dev
&& st
.st_ino
== ent
->fts_statp
->st_ino
)
414 * let rename() handle it
418 sfputr(sfstdout
, state
->path
, '\n');
421 if (!state
->official
)
422 error(2, "%s: identical to %s", state
->path
, ent
->fts_path
);
425 if (S_ISDIR(st
.st_mode
))
427 error(2, "%s: cannot %s existing directory", state
->path
, state
->opname
);
431 sfputr(sfstdout
, state
->path
, '\n');
432 rm
= state
->op
== LN
|| ent
->fts_info
== FTS_SL
;
433 if (!rm
|| !state
->force
)
435 if ((n
= open(state
->path
, O_RDWR
|O_BINARY
)) >= 0)
440 else if (state
->interactive
)
442 if (astquery(-1, "%s %s? ", state
->opname
, state
->path
) < 0 || sh_checksig(state
->context
))
445 else if (state
->op
== LN
)
447 error(2, "%s: cannot %s existing file", state
->path
, state
->opname
);
451 else if (state
->force
)
457 errno
== ETXTBSY
? "``running program''" :
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
)
463 if (astquery(-1, "override protection %s for %s? ", protection
, state
->path
) < 0 || sh_checksig(state
->context
))
469 error(2, "%s: cannot %s %s protection", state
->path
, state
->opname
, protection
);
474 switch (state
->backup
)
479 if (s
= strrchr(state
->path
, '/'))
490 if (fts
= fts_open((char**)e
, FTS_NOCHDIR
|FTS_ONEPATH
|FTS_PHYSICAL
|FTS_NOPOSTORDER
|FTS_NOSTAT
|FTS_NOSEEDOTDIR
, NiL
))
492 while (sub
= fts_read(fts
))
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
)
497 fts_set(NiL
, sub
, FTS_SKIP
);
501 if (s
!= state
->path
)
503 if (v
|| state
->backup
== BAK_number
)
505 sfprintf(state
->tmp
, "%s.%s%d%s", state
->path
, state
->suffix
, v
+ 1, state
->suffix
);
510 sfprintf(state
->tmp
, "%s%s", state
->path
, state
->suffix
);
512 if (!(s
= sfstruse(state
->tmp
)))
513 error(ERROR_SYSTEM
|3, "%s: out of space", state
->path
);
514 if (rename(state
->path
, s
))
516 error(ERROR_SYSTEM
|2, "%s: cannot backup to %s", state
->path
, s
);
521 if (rm
&& remove(state
->path
))
523 error(ERROR_SYSTEM
|2, "%s: cannot remove", state
->path
);
535 if (!rename(ent
->fts_path
, state
->path
))
539 else if (!rm
&& st
.st_mode
&& !remove(state
->path
))
544 if (errno
!= EXDEV
&& (rm
|| S_ISDIR(ent
->fts_statp
->st_mode
)))
546 error(ERROR_SYSTEM
|2, "%s: cannot rename to %s", ent
->fts_path
, state
->path
);
554 if (S_ISLNK(ent
->fts_statp
->st_mode
))
556 if ((n
= pathgetlink(ent
->fts_path
, state
->text
, sizeof(state
->text
) - 1)) < 0)
558 error(ERROR_SYSTEM
|2, "%s: cannot read symbolic link text", ent
->fts_path
);
562 if (pathsetlink(state
->text
, state
->path
))
564 error(ERROR_SYSTEM
|2, "%s: cannot copy symbolic link to %s", ent
->fts_path
, state
->path
);
568 else if (state
->op
== CP
|| S_ISREG(ent
->fts_statp
->st_mode
) || S_ISDIR(ent
->fts_statp
->st_mode
))
570 if (ent
->fts_statp
->st_size
> 0 && (rfd
= open(ent
->fts_path
, O_RDONLY
|O_BINARY
)) < 0)
572 error(ERROR_SYSTEM
|2, "%s: cannot read", ent
->fts_path
);
575 else if ((wfd
= open(state
->path
, st
.st_mode
? (state
->wflags
& ~O_EXCL
) : state
->wflags
, ent
->fts_statp
->st_mode
& state
->perm
)) < 0)
577 error(ERROR_SYSTEM
|2, "%s: cannot write", state
->path
);
578 if (ent
->fts_statp
->st_size
> 0)
582 else if (ent
->fts_statp
->st_size
> 0)
584 if (!(ip
= sfnew(NiL
, NiL
, SF_UNBOUND
, rfd
, SF_READ
)))
586 error(ERROR_SYSTEM
|2, "%s: %s read stream error", ent
->fts_path
, state
->path
);
593 if (!(op
= sfnew(NiL
, NiL
, SF_UNBOUND
, wfd
, SF_WRITE
)))
595 error(ERROR_SYSTEM
|2, "%s: %s write stream error", ent
->fts_path
, state
->path
);
601 if (sfmove(ip
, op
, (Sfoff_t
)SF_UNBOUND
, -1) < 0)
605 if (sfsync(op
) || state
->sync
&& fsync(wfd
) || sfclose(op
))
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"));
617 else if (S_ISBLK(ent
->fts_statp
->st_mode
) || S_ISCHR(ent
->fts_statp
->st_mode
) || S_ISFIFO(ent
->fts_statp
->st_mode
))
619 if (mknod(state
->path
, ent
->fts_statp
->st_mode
, idevice(ent
->fts_statp
)))
621 error(ERROR_SYSTEM
|2, "%s: cannot copy special file to %s", ent
->fts_path
, state
->path
);
627 error(2, "%s: cannot copy -- unknown file type 0%o", ent
->fts_path
, S_ITYPE(ent
->fts_statp
->st_mode
));
632 if (ent
->fts_info
!= FTS_SL
)
634 if (stat(state
->path
, &st
))
635 error(ERROR_SYSTEM
|2, "%s: cannot stat", state
->path
);
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
);
643 if (state
->op
== MV
&& remove(ent
->fts_path
))
644 error(ERROR_SYSTEM
|1, "%s: cannot remove", ent
->fts_path
);
648 if ((*state
->link
)(ent
->fts_path
, state
->path
))
649 error(ERROR_SYSTEM
|2, "%s: cannot link to %s", ent
->fts_path
, state
->path
);
656 b_cp(int argc
, register char** argv
, void* context
)
670 void* cleanup
= context
;
672 cmdinit(argc
, argv
, context
, ERROR_CATALOG
, ERROR_NOTIFY
);
673 if (!(sh
= CMD_CONTEXT(context
)) || !(state
= (State_t
*)sh
->ptr
))
675 if (!(state
= newof(0, State_t
, 1, 0)))
676 error(ERROR_SYSTEM
|3, "out of space");
681 memset(state
, 0, offsetof(State_t
, INITSTATE
));
682 state
->context
= context
;
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])
696 sfputr(state
->tmp
, usage_cp
, -1);
703 sfputr(state
->tmp
, usage_ln
, -1);
705 state
->flags
|= FTS_PHYSICAL
;
712 sfputr(state
->tmp
, usage_mv
, -1);
714 state
->flags
|= FTS_PHYSICAL
;
720 error(3, "not implemented");
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");
729 switch (optget(argv
, usage
))
732 state
->flags
|= FTS_PHYSICAL
;
734 state
->recursive
= 1;
742 if (state
->op
!= CP
|| !standard
)
743 state
->interactive
= 0;
746 state
->hierarchy
= 1;
749 state
->interactive
= 1;
750 if (state
->op
!= CP
|| !standard
)
762 state
->recursive
= 1;
763 if (path_resolve
< 0)
768 state
->link
= pathsetlink
;
778 state
->flags
|= FTS_XDEV
;
784 error(1, "%s not implemented on this system", opt_info
.name
);
788 state
->flags
|= FTS_META
|FTS_PHYSICAL
;
792 state
->flags
&= ~FTS_PHYSICAL
;
796 state
->flags
&= ~FTS_META
;
797 state
->flags
|= FTS_PHYSICAL
;
801 state
->recursive
= 1;
802 state
->flags
&= ~FTS_META
;
803 state
->flags
|= FTS_PHYSICAL
;
807 state
->suffix
= opt_info
.arg
;
810 backup_type
= opt_info
.arg
;
813 error(ERROR_USAGE
|4, "%s", opt_info
.arg
);
816 error(2, "%s", opt_info
.arg
);
821 argc
-= opt_info
.index
+ 1;
822 argv
+= opt_info
.index
;
823 if (*argv
&& streq(*argv
, "-") && !streq(*(argv
- 1), "--"))
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*));
834 state
->wflags
|= O_EXCL
;
838 argv
[1] = (char*)dot
;
843 if (!(file
= backup_type
) && !(backup_type
= getenv("VERSION_CONTROL")))
844 state
->backup
= BAK_existing
;
846 switch (strkey(backup_type
))
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'):
854 case HASHKEY3('n','i','l'):
855 case HASHKEY2('n','i'):
856 state
->backup
= BAK_existing
;
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'):
868 state
->backup
= BAK_simple
;
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'):
876 state
->backup
= BAK_number
;
880 error(2, "%s: unknown backup type", backup_type
);
883 if (!state
->suffix
&& !(state
->suffix
= getenv("SIMPLE_BACKUP_SUFFIX")))
885 state
->suflen
= strlen(state
->suffix
);
887 if (argc
<= 0 || error_info
.errors
)
888 error(ERROR_USAGE
|4, "%s", optusage(NiL
));
890 state
->flags
|= fts_flags();
893 if (s
= strrchr(file
, '/'))
897 if (!(!*s
|| *s
== '.' && (!*++s
|| *s
== '.' && !*++s
)))
900 if (file
!= (char*)dot
)
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
, "...|*/...|.../*"))
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
)
916 if (!state
->directory
)
917 error(3, "%s: last argument must be a directory", file
);
918 state
->missmode
= st
.st_mode
;
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
))
925 while (!sh_checksig(context
) && (ent
= fts_read(fts
)) && !visit(state
, ent
));
928 else if (state
->link
!= pathsetlink
)
932 error(ERROR_SYSTEM
|2, "%s: cannot copy", argv
[0]);
935 error(ERROR_SYSTEM
|2, "%s: cannot link", argv
[0]);
938 error(ERROR_SYSTEM
|2, "%s: cannot move", argv
[0]);
941 else if ((*state
->link
)(*argv
, state
->path
))
942 error(ERROR_SYSTEM
|2, "%s: cannot link to %s", *argv
, state
->path
);
949 return error_info
.errors
!= 0;