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