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
29static const char usage_head[] =
30"[-?@(#)$Id: cp (AT&T Research) 2010-01-20 $\n]"
31USAGE_LICENSE
32;
33
34static 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
60static 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
70static 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
82static 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
143typedef 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
184static const char	dot[2] = { '.' };
185
186/*
187 * preserve support
188 */
189
190static void
191preserve(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
217static int
218visit(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
655int
656b_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