xref: /onnv-gate/usr/src/lib/libcmd/common/cp.c (revision 12068:08a39a083754)
14887Schin /***********************************************************************
24887Schin *                                                                      *
34887Schin *               This software is part of the ast package               *
4*12068SRoger.Faulkner@Oracle.COM *          Copyright (c) 1992-2010 AT&T Intellectual Property          *
54887Schin *                      and is licensed under the                       *
64887Schin *                  Common Public License, Version 1.0                  *
78462SApril.Chin@Sun.COM *                    by AT&T Intellectual Property                     *
84887Schin *                                                                      *
94887Schin *                A copy of the License is available at                 *
104887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
114887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
124887Schin *                                                                      *
134887Schin *              Information and Software Systems Research               *
144887Schin *                            AT&T Research                             *
154887Schin *                           Florham Park NJ                            *
164887Schin *                                                                      *
174887Schin *                 Glenn Fowler <gsf@research.att.com>                  *
184887Schin *                  David Korn <dgk@research.att.com>                   *
194887Schin *                                                                      *
204887Schin ***********************************************************************/
214887Schin #pragma prototyped
224887Schin /*
234887Schin  * Glenn Fowler
244887Schin  * AT&T Research
254887Schin  *
264887Schin  * cp/ln/mv -- copy/link/move files
274887Schin  */
284887Schin 
294887Schin static const char usage_head[] =
30*12068SRoger.Faulkner@Oracle.COM "[-?@(#)$Id: cp (AT&T Research) 2010-01-20 $\n]"
314887Schin USAGE_LICENSE
324887Schin ;
334887Schin 
344887Schin static const char usage_cp[] =
354887Schin "[+NAME?cp - copy files]"
364887Schin "[+DESCRIPTION?If the last argument names an existing directory, \bcp\b"
374887Schin "	copies each \afile\a into a file with the same name in that"
384887Schin "	directory. Otherwise, if only two files are given, \bcp\b copies"
394887Schin "	the first onto the second. It is an error if the last argument is"
404887Schin "	not a directory and more than two files are given. By default"
414887Schin "	directories are not copied.]"
424887Schin 
434887Schin "[a:archive?Preserve as much as possible of the structure and attributes of"
444887Schin "	the original files in the copy. Equivalent to \b--physical\b"
454887Schin "	\b--preserve\b \b--recursive\b.]"
464887Schin "[p:preserve?Preserve file owner, group, permissions and timestamps.]"
474887Schin "[h:hierarchy|parents?Form the name of each destination file by appending"
484887Schin "	to the target directory a slash and the specified source file name."
494887Schin "	The last argument must be an existing directory. Missing destination"
504887Schin "	directories are created.]"
514887Schin "[H:metaphysical?Follow command argument symbolic links, otherwise don't"
524887Schin "	follow.]"
534887Schin "[l:link?Make hard links to destination files instead of copies.]"
544887Schin "[L:logical|dereference?Follow symbolic links and copy the files"
554887Schin "	they point to.]"
564887Schin "[P|d:physical|nodereference?Don't follow symbolic links; copy symbolic"
574887Schin "	rather than the files they point to.]"
584887Schin ;
594887Schin 
604887Schin static const char usage_ln[] =
614887Schin "[+NAME?ln - link files]"
624887Schin "[+DESCRIPTION?If the last argument names an existing directory, \bln\b"
634887Schin "	links each \afile\a into a file with the same name in that"
644887Schin "	directory. Otherwise, if only two files are given, \bln\b links"
654887Schin "	the first onto the second. It is an error if the last argument is"
664887Schin "	not a directory and more than two files are given. By default"
674887Schin "	directories are not linked.]"
684887Schin ;
694887Schin 
704887Schin static const char usage_mv[] =
714887Schin "[+NAME?mv - rename files]"
724887Schin "[+DESCRIPTION?If the last argument names an existing directory, \bmv\b"
734887Schin "	renames each \afile\a into a file with the same name in that"
744887Schin "	directory. Otherwise, if only two files are given, \bmv\b renames"
754887Schin "	the first onto the second. It is an error if the last argument is"
764887Schin "	not a directory and more than two files are given. If a source and"
774887Schin "	destination file reside on different filesystems then \bmv\b copies"
784887Schin "	the file contents to the destination and then deletes the source"
794887Schin "	file.]"
804887Schin ;
814887Schin 
824887Schin static const char usage_tail[] =
834887Schin "[f:force?Replace existing destination files.]"
844887Schin "[i:interactive|prompt?Prompt whether to replace existing destination files."
854887Schin "	An affirmative response (\by\b or \bY\b) replaces the file, a quit"
864887Schin "	response (\bq\b or \bQ\b) exits immediately, and all other"
874887Schin "	responses skip the file.]"
884887Schin "[r|R:recursive?Operate on the contents of directories recursively.]"
894887Schin "[s:symlink|symbolic-link?Make symbolic links to destination files.]"
904887Schin "[u:update?Replace a destination file only if its modification time is older"
914887Schin "	than the corresponding source file modification time.]"
924887Schin "[v:verbose?Print the name of each file before operating on it.]"
934887Schin "[b:backup?Make backups of files that are about to be replaced. See"
944887Schin "	\b--suffix\b and \b--version-control\b for more information.]"
954887Schin "[F:fsync|sync?\bfsync\b(2) each file after it is copied.]"
964887Schin "[S:backup-suffix|suffix?A backup file is made by renaming the file to the"
974887Schin "	same name with the backup suffix appended. The backup suffix is"
984887Schin "	determined in this order: this option, the \bSIMPLE_BACKUP_SUFFIX\b,"
994887Schin "	environment variable, or the default value \b~\b.]:[suffix]"
1004887Schin "[V:backup-type|version-control?The backup type is determined in this order:"
1014887Schin "	this option, the \bVERSION_CONTROL\b environment variable, or the"
1024887Schin "	default value \bexisting\b. \atype\a may be one of:]:[type]{"
1034887Schin "		[+numbered|t?Always make numbered backups. The numbered backup"
1044887Schin "			suffix is \b.\aSNS\a, where \aS\a is the"
1054887Schin "			\bbackup-suffix\b and \aN\a is the version number,"
1064887Schin "			starting at 1, incremented with each version.]"
1074887Schin "		[+existing|nil?Make numbered backups of files that already"
1084887Schin "			have them, otherwise simple backups.]"
1094887Schin "		[+simple|never?Always make simple backups.]"
1104887Schin "}"
1114887Schin "[x|X|l:xdev|local|mount|one-file-system?Do not descend into directories in"
1124887Schin "	different filesystems than their parents.]"
1134887Schin 
1144887Schin "\n"
1154887Schin "\nsource destination\n"
1164887Schin "file ... directory\n"
1174887Schin "\n"
1184887Schin 
1194887Schin "[+SEE ALSO?\bpax\b(1), \bfsync\b(2), \brename\b(2), \bunlink\b(2),"
1204887Schin "	\bremove\b(3)]"
1214887Schin ;
1224887Schin 
1234887Schin #include <cmd.h>
1244887Schin #include <ls.h>
1254887Schin #include <times.h>
126*12068SRoger.Faulkner@Oracle.COM #include <fts_fix.h>
1274887Schin #include <fs3d.h>
1284887Schin #include <hashkey.h>
1294887Schin #include <stk.h>
1304887Schin #include <tmx.h>
1314887Schin 
1324887Schin #define PATH_CHUNK	256
1334887Schin 
1344887Schin #define CP		1
1354887Schin #define LN		2
1364887Schin #define MV		3
1374887Schin 
1384887Schin #define BAK_replace	0		/* no backup -- just replace	*/
1394887Schin #define BAK_existing	1		/* number if already else simple*/
1404887Schin #define BAK_number	2		/* append .suffix number suffix	*/
1414887Schin #define BAK_simple	3		/* append suffix		*/
1424887Schin 
1434887Schin typedef struct State_s			/* program state		*/
1444887Schin {
14510898Sroland.mainz@nrubsig.org 	void*		context;	/* builtin context		*/
1464887Schin 	int		backup;		/* BAK_* type			*/
1474887Schin 	int		directory;	/* destination is directory	*/
1484887Schin 	int		flags;		/* FTS_* flags			*/
1494887Schin 	int		force;		/* force approval		*/
1504887Schin 	int		fs3d;		/* 3d fs enabled		*/
1514887Schin 	int		hierarchy;	/* preserve hierarchy		*/
1524887Schin 	int		interactive;	/* prompt for approval		*/
1534887Schin 	int		missmode;	/* default missing dir mode	*/
1544887Schin 	int		official;	/* move to next view		*/
1554887Schin 	int		op;		/* {CP,LN,MV}			*/
1564887Schin 	int		perm;		/* permissions to preserve	*/
1574887Schin 	int		postsiz;	/* state.path post index	*/
1584887Schin 	int		presiz;		/* state.path pre index		*/
1594887Schin 	int		preserve;	/* preserve { id mode time }	*/
1604887Schin 	int		recursive;	/* subtrees too			*/
1614887Schin 	int		suflen;		/* strlen(state.suffix)		*/
1624887Schin 	int		sync;		/* fsync() each file after copy	*/
1634887Schin 	int		uid;		/* caller uid			*/
1644887Schin 	int		update;		/* replace only if newer	*/
1654887Schin 	int		verbose;	/* list each file before op	*/
1668462SApril.Chin@Sun.COM 	int		wflags;		/* open() for write flags	*/
1674887Schin 
1684887Schin 	int		(*link)(const char*, const char*);	/* link	*/
1694887Schin 	int		(*stat)(const char*, struct stat*);	/* stat	*/
1704887Schin 
1718462SApril.Chin@Sun.COM #define INITSTATE	pathsiz		/* (re)init state before this	*/
1728462SApril.Chin@Sun.COM 	int		pathsiz;	/* state.path buffer size	*/
1738462SApril.Chin@Sun.COM 
1748462SApril.Chin@Sun.COM 
1754887Schin 	char*		path;		/* to pathname buffer		*/
1764887Schin 	char*		opname;		/* state.op message string	*/
1774887Schin 	char*		suffix;		/* backup suffix		*/
1784887Schin 
1794887Schin 	Sfio_t*		tmp;		/* tmp string stream		*/
1804887Schin 
1814887Schin 	char		text[PATH_MAX];	/* link text buffer		*/
1824887Schin } State_t;
1834887Schin 
1844887Schin static const char	dot[2] = { '.' };
1854887Schin 
1864887Schin /*
1874887Schin  * preserve support
1884887Schin  */
1894887Schin 
1904887Schin static void
preserve(State_t * state,const char * path,struct stat * ns,struct stat * os)1914887Schin preserve(State_t* state, const char* path, struct stat* ns, struct stat* os)
1924887Schin {
1934887Schin 	int	n;
1944887Schin 
1954887Schin 	if (tmxtouch(path, tmxgetatime(os), tmxgetmtime(os), TMX_NOTIME, 0))
1964887Schin 		error(ERROR_SYSTEM|2, "%s: cannot reset access and modify times", path);
1974887Schin 	n = ((ns->st_uid != os->st_uid) << 1) | (ns->st_gid != os->st_gid);
1984887Schin 	if (n && chown(state->path, os->st_uid, os->st_gid))
1994887Schin 		switch (n)
2004887Schin 		{
2014887Schin 		case 01:
2024887Schin 			error(ERROR_SYSTEM|2, "%s: cannot reset group to %s", path, fmtgid(os->st_gid));
2034887Schin 			break;
2044887Schin 		case 02:
2054887Schin 			error(ERROR_SYSTEM|2, "%s: cannot reset owner to %s", path, fmtuid(os->st_uid));
2064887Schin 			break;
2074887Schin 		case 03:
2084887Schin 			error(ERROR_SYSTEM|2, "%s: cannot reset owner to %s and group to %s", path, fmtuid(os->st_uid), fmtgid(os->st_gid));
2094887Schin 			break;
2104887Schin 		}
2114887Schin }
2124887Schin 
2134887Schin /*
2144887Schin  * visit a single file and state.op to the destination
2154887Schin  */
2164887Schin 
2174887Schin static int
visit(State_t * state,register FTSENT * ent)2184887Schin visit(State_t* state, register FTSENT* ent)
2194887Schin {
2204887Schin 	register char*	base;
2214887Schin 	register int	n;
2224887Schin 	register int	len;
2234887Schin 	int		rm;
2244887Schin 	int		rfd;
2254887Schin 	int		wfd;
2264887Schin 	int		m;
2274887Schin 	int		v;
2284887Schin 	char*		s;
2294887Schin 	char*		e;
2304887Schin 	char*		protection;
2314887Schin 	Sfio_t*		ip;
2324887Schin 	Sfio_t*		op;
2334887Schin 	FTS*		fts;
2344887Schin 	FTSENT*		sub;
2354887Schin 	struct stat	st;
2364887Schin 
2374887Schin 	if (ent->fts_info == FTS_DC)
2384887Schin 	{
2394887Schin 		error(2, "%s: directory causes cycle", ent->fts_path);
2404887Schin 		fts_set(NiL, ent, FTS_SKIP);
2414887Schin 		return 0;
2424887Schin 	}
2434887Schin 	if (ent->fts_level == 0)
2444887Schin 	{
2454887Schin 		base = ent->fts_name;
2464887Schin 		len = ent->fts_namelen;
2474887Schin 		if (state->hierarchy)
2484887Schin 			state->presiz = -1;
2494887Schin 		else
2504887Schin 		{
2514887Schin 			state->presiz = ent->fts_pathlen;
2524887Schin 			while (*base == '.' && *(base + 1) == '/')
2534887Schin 				for (base += 2; *base == '/'; base++);
2544887Schin 			if (*base == '.' && !*(base + 1))
2554887Schin 				state->presiz--;
2564887Schin 			else if (*base)
2574887Schin 				state->presiz -= base - ent->fts_name;
2584887Schin 			base = ent->fts_name + len;
2594887Schin 			while (base > ent->fts_name && *(base - 1) == '/')
2604887Schin 				base--;
2614887Schin 			while (base > ent->fts_name && *(base - 1) != '/')
2624887Schin 				base--;
2634887Schin 			len -= base - ent->fts_name;
2644887Schin 			if (state->directory)
2654887Schin 				state->presiz -= len + 1;
2664887Schin 		}
2674887Schin 	}
2684887Schin 	else
2694887Schin 	{
2704887Schin 		base = ent->fts_path + state->presiz + 1;
2714887Schin 		len = ent->fts_pathlen - state->presiz - 1;
2724887Schin 	}
2734887Schin 	len++;
2744887Schin 	if (state->directory)
2754887Schin 	{
2764887Schin 		if ((state->postsiz + len) > state->pathsiz && !(state->path = newof(state->path, char, state->pathsiz = roundof(state->postsiz + len, PATH_CHUNK), 0)))
277*12068SRoger.Faulkner@Oracle.COM 			error(ERROR_SYSTEM|3, "out of space");
2784887Schin 		if (state->hierarchy && ent->fts_level == 0 && strchr(base, '/'))
2794887Schin 		{
2804887Schin 			s = state->path + state->postsiz;
2814887Schin 			memcpy(s, base, len);
2824887Schin 			while (e = strchr(s, '/'))
2834887Schin 			{
2844887Schin 				*e = 0;
2854887Schin 				if (access(state->path, F_OK))
2864887Schin 				{
2874887Schin 					st.st_mode = state->missmode;
2884887Schin 					if (s = strrchr(s, '/'))
2894887Schin 					{
2904887Schin 						*s = 0;
2914887Schin 						stat(state->path, &st);
2924887Schin 						*s = '/';
2934887Schin 					}
2944887Schin 					if (mkdir(state->path, st.st_mode & S_IPERM))
2954887Schin 					{
2964887Schin 						error(ERROR_SYSTEM|2, "%s: cannot create directory -- %s ignored", state->path, ent->fts_path);
2974887Schin 						fts_set(NiL, ent, FTS_SKIP);
2984887Schin 						return 0;
2994887Schin 					}
3004887Schin 				}
3014887Schin 				*e++ = '/';
3024887Schin 				s = e;
3034887Schin 			}
3044887Schin 		}
3054887Schin 	}
3064887Schin 	switch (ent->fts_info)
3074887Schin 	{
3084887Schin 	case FTS_DP:
3094887Schin 		if (state->preserve && state->op != LN || ent->fts_level > 0 && (ent->fts_statp->st_mode & S_IRWXU) != S_IRWXU)
3104887Schin 		{
3114887Schin 			if (len && ent->fts_level > 0)
3124887Schin 				memcpy(state->path + state->postsiz, base, len);
3134887Schin 			else
3144887Schin 				state->path[state->postsiz] = 0;
3154887Schin 			if (stat(state->path, &st))
3164887Schin 				error(ERROR_SYSTEM|2, "%s: cannot stat", state->path);
3174887Schin 			else
3184887Schin 			{
3194887Schin 				if ((ent->fts_statp->st_mode & S_IPERM) != (st.st_mode & S_IPERM) && chmod(state->path, ent->fts_statp->st_mode & S_IPERM))
3204887Schin 					error(ERROR_SYSTEM|2, "%s: cannot reset directory mode to %s", state->path, fmtmode(st.st_mode & S_IPERM, 0) + 1);
3214887Schin 				if (state->preserve)
3224887Schin 					preserve(state, state->path, &st, ent->fts_statp);
3234887Schin 			}
3244887Schin 		}
3254887Schin 		return 0;
3264887Schin 	case FTS_DNR:
3274887Schin 	case FTS_DNX:
3284887Schin 	case FTS_D:
3294887Schin 		if (!state->recursive)
3304887Schin 		{
3314887Schin 			fts_set(NiL, ent, FTS_SKIP);
3324887Schin 			if (state->op == CP)
3334887Schin 				error(1, "%s: directory -- copying as plain file", ent->fts_path);
3344887Schin 			else if (state->link == link && !state->force)
3354887Schin 			{
3364887Schin 				error(2, "%s: cannot link directory", ent->fts_path);
3374887Schin 				return 0;
3384887Schin 			}
3394887Schin 		}
3404887Schin 		else switch (ent->fts_info)
3414887Schin 		{
3424887Schin 		case FTS_DNR:
3434887Schin 			error(2, "%s: cannot read directory", ent->fts_path);
3444887Schin 			return 0;
3454887Schin 		case FTS_DNX:
3464887Schin 			error(2, "%s: cannot search directory", ent->fts_path);
3474887Schin 			fts_set(NiL, ent, FTS_SKIP);
3484887Schin 
3494887Schin 			/*FALLTHROUGH*/
3504887Schin 		case FTS_D:
3514887Schin 			if (state->directory)
3524887Schin 				memcpy(state->path + state->postsiz, base, len);
3534887Schin 			if (!(*state->stat)(state->path, &st))
3544887Schin 			{
3554887Schin 				if (!S_ISDIR(st.st_mode))
3564887Schin 				{
3574887Schin 					error(2, "%s: not a directory -- %s ignored", state->path, ent->fts_path);
3584887Schin 					return 0;
3594887Schin 				}
3604887Schin 			}
3614887Schin 			else if (mkdir(state->path, (ent->fts_statp->st_mode & S_IPERM)|(ent->fts_info == FTS_D ? S_IRWXU : 0)))
3624887Schin 			{
3634887Schin 				error(ERROR_SYSTEM|2, "%s: cannot create directory -- %s ignored", state->path, ent->fts_path);
3644887Schin 				fts_set(NiL, ent, FTS_SKIP);
3654887Schin 			}
3664887Schin 			if (!state->directory)
3674887Schin 			{
3684887Schin 				state->directory = 1;
3694887Schin 				state->path[state->postsiz++] = '/';
3704887Schin 				state->presiz--;
3714887Schin 			}
3724887Schin 			return 0;
3734887Schin 		}
3744887Schin 		break;
3754887Schin 	case FTS_ERR:
3764887Schin 	case FTS_NS:
3774887Schin 	case FTS_SLNONE:
3784887Schin 		if (state->link != pathsetlink)
3794887Schin 		{
3804887Schin 			error(2, "%s: not found", ent->fts_path);
3814887Schin 			return 0;
3824887Schin 		}
3834887Schin 		break;
3844887Schin #if 0
3854887Schin 	case FTS_SL:
3864887Schin 		if (state->op == CP)
3874887Schin 		{
3884887Schin 			error(2, "%s: cannot copy non-terminal symbolic link", ent->fts_path);
3894887Schin 			return 0;
3904887Schin 		}
3914887Schin 		break;
3924887Schin #endif
3934887Schin 	}
3944887Schin 	if (state->directory)
3954887Schin 		memcpy(state->path + state->postsiz, base, len);
3964887Schin 	if ((*state->stat)(state->path, &st))
3974887Schin 		st.st_mode = 0;
3984887Schin 	else if (state->update && !S_ISDIR(st.st_mode) && (unsigned long)ent->fts_statp->st_mtime < (unsigned long)st.st_mtime)
3994887Schin 	{
4004887Schin 		fts_set(NiL, ent, FTS_SKIP);
4014887Schin 		return 0;
4024887Schin 	}
4034887Schin 	else if (!state->fs3d || !iview(&st))
4044887Schin 	{
4054887Schin 		/*
4064887Schin 		 * target is in top 3d view
4074887Schin 		 */
4084887Schin 
4094887Schin 		if (st.st_dev == ent->fts_statp->st_dev && st.st_ino == ent->fts_statp->st_ino)
4104887Schin 		{
4114887Schin 			if (state->op == MV)
4124887Schin 			{
4134887Schin 				/*
4144887Schin 				 * let rename() handle it
4154887Schin 				 */
4164887Schin 
4174887Schin 				if (state->verbose)
4184887Schin 					sfputr(sfstdout, state->path, '\n');
4194887Schin 				goto operate;
4204887Schin 			}
4214887Schin 			if (!state->official)
4224887Schin 				error(2, "%s: identical to %s", state->path, ent->fts_path);
4234887Schin 			return 0;
4244887Schin 		}
4254887Schin 		if (S_ISDIR(st.st_mode))
4264887Schin 		{
4274887Schin 			error(2, "%s: cannot %s existing directory", state->path, state->opname);
4284887Schin 			return 0;
4294887Schin 		}
4304887Schin 		if (state->verbose)
4314887Schin 			sfputr(sfstdout, state->path, '\n');
4324887Schin 		rm = state->op == LN || ent->fts_info == FTS_SL;
4334887Schin 		if (!rm || !state->force)
4344887Schin 		{
4354887Schin 			if ((n = open(state->path, O_RDWR|O_BINARY)) >= 0)
4364887Schin 			{
4374887Schin 				close(n);
4384887Schin 				if (state->force)
4394887Schin 					/* ok */;
4404887Schin 				else if (state->interactive)
4414887Schin 				{
44210898Sroland.mainz@nrubsig.org 					if (astquery(-1, "%s %s? ", state->opname, state->path) < 0 || sh_checksig(state->context))
4434887Schin 						return 0;
4444887Schin 				}
4454887Schin 				else if (state->op == LN)
4464887Schin 				{
4474887Schin 					error(2, "%s: cannot %s existing file", state->path, state->opname);
4484887Schin 					return 0;
4494887Schin 				}
4504887Schin 			}
4514887Schin 			else if (state->force)
4524887Schin 				rm = 1;
4534887Schin 			else
4544887Schin 			{
4554887Schin 				protection =
4564887Schin #ifdef ETXTBSY
4574887Schin 				    errno == ETXTBSY ? "``running program''" :
4584887Schin #endif
4594887Schin 				    st.st_uid != state->uid ? "``not owner''" :
4604887Schin 				    fmtmode(st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO), 0) + 1;
4614887Schin 				if (state->interactive)
4624887Schin 				{
46310898Sroland.mainz@nrubsig.org 					if (astquery(-1, "override protection %s for %s? ", protection, state->path) < 0 || sh_checksig(state->context))
4644887Schin 						return 0;
4654887Schin 					rm = 1;
4664887Schin 				}
4674887Schin 				else if (!rm)
4684887Schin 				{
4694887Schin 					error(2, "%s: cannot %s %s protection", state->path, state->opname, protection);
4704887Schin 					return 0;
4714887Schin 				}
4724887Schin 			}
4734887Schin 		}
4744887Schin 		switch (state->backup)
4754887Schin 		{
4764887Schin 		case BAK_existing:
4774887Schin 		case BAK_number:
4784887Schin 			v = 0;
4794887Schin 			if (s = strrchr(state->path, '/'))
4804887Schin 			{
4814887Schin 				e = state->path;
4824887Schin 				*s++ = 0;
4834887Schin 			}
4844887Schin 			else
4854887Schin 			{
4864887Schin 				e = (char*)dot;
4874887Schin 				s = state->path;
4884887Schin 			}
4894887Schin 			n = strlen(s);
4904887Schin 			if (fts = fts_open((char**)e, FTS_NOCHDIR|FTS_ONEPATH|FTS_PHYSICAL|FTS_NOPOSTORDER|FTS_NOSTAT|FTS_NOSEEDOTDIR, NiL))
4914887Schin 			{
4924887Schin 				while (sub = fts_read(fts))
4934887Schin 				{
4944887Schin 					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)
4954887Schin 						v = m;
4964887Schin 					if (sub->fts_level)
4974887Schin 						fts_set(NiL, sub, FTS_SKIP);
4984887Schin 				}
4994887Schin 				fts_close(fts);
5004887Schin 			}
5014887Schin 			if (s != state->path)
5024887Schin 				*--s = '/';
5034887Schin 			if (v || state->backup == BAK_number)
5044887Schin 			{
5054887Schin 				sfprintf(state->tmp, "%s.%s%d%s", state->path, state->suffix, v + 1, state->suffix);
5064887Schin 				goto backup;
5074887Schin 			}
5084887Schin 			/*FALLTHROUGH*/
5094887Schin 		case BAK_simple:
5104887Schin 			sfprintf(state->tmp, "%s%s", state->path, state->suffix);
5114887Schin 		backup:
5124887Schin 			if (!(s = sfstruse(state->tmp)))
5134887Schin 				error(ERROR_SYSTEM|3, "%s: out of space", state->path);
5144887Schin 			if (rename(state->path, s))
5154887Schin 			{
5164887Schin 				error(ERROR_SYSTEM|2, "%s: cannot backup to %s", state->path, s);
5174887Schin 				return 0;
5184887Schin 			}
5194887Schin 			break;
5204887Schin 		default:
5214887Schin 			if (rm && remove(state->path))
5224887Schin 			{
5234887Schin 				error(ERROR_SYSTEM|2, "%s: cannot remove", state->path);
5244887Schin 				return 0;
5254887Schin 			}
5264887Schin 			break;
5274887Schin 		}
5284887Schin 	}
5294887Schin  operate:
5304887Schin 	switch (state->op)
5314887Schin 	{
5324887Schin 	case MV:
5334887Schin 		for (;;)
5344887Schin 		{
5354887Schin 			if (!rename(ent->fts_path, state->path))
5364887Schin 				return 0;
5374887Schin 			if (errno == ENOENT)
5384887Schin 				rm = 1;
5394887Schin 			else if (!rm && st.st_mode && !remove(state->path))
5404887Schin 			{
5414887Schin 				rm = 1;
5424887Schin 				continue;
5434887Schin 			}
5444887Schin 			if (errno != EXDEV && (rm || S_ISDIR(ent->fts_statp->st_mode)))
5454887Schin 			{
5464887Schin 				error(ERROR_SYSTEM|2, "%s: cannot rename to %s", ent->fts_path, state->path);
5474887Schin 				return 0;
5484887Schin 			}
5494887Schin 			else
5504887Schin 				break;
5514887Schin 		}
5524887Schin 		/*FALLTHROUGH*/
5534887Schin 	case CP:
5544887Schin 		if (S_ISLNK(ent->fts_statp->st_mode))
5554887Schin 		{
5564887Schin 			if ((n = pathgetlink(ent->fts_path, state->text, sizeof(state->text) - 1)) < 0)
5574887Schin 			{
5584887Schin 				error(ERROR_SYSTEM|2, "%s: cannot read symbolic link text", ent->fts_path);
5594887Schin 				return 0;
5604887Schin 			}
5614887Schin 			state->text[n] = 0;
5624887Schin 			if (pathsetlink(state->text, state->path))
5634887Schin 			{
5644887Schin 				error(ERROR_SYSTEM|2, "%s: cannot copy symbolic link to %s", ent->fts_path, state->path);
5654887Schin 				return 0;
5664887Schin 			}
5674887Schin 		}
5684887Schin 		else if (state->op == CP || S_ISREG(ent->fts_statp->st_mode) || S_ISDIR(ent->fts_statp->st_mode))
5694887Schin 		{
5704887Schin 			if (ent->fts_statp->st_size > 0 && (rfd = open(ent->fts_path, O_RDONLY|O_BINARY)) < 0)
5714887Schin 			{
5724887Schin 				error(ERROR_SYSTEM|2, "%s: cannot read", ent->fts_path);
5734887Schin 				return 0;
5744887Schin 			}
5758462SApril.Chin@Sun.COM 			else if ((wfd = open(state->path, st.st_mode ? (state->wflags & ~O_EXCL) : state->wflags, ent->fts_statp->st_mode & state->perm)) < 0)
5764887Schin 			{
5774887Schin 				error(ERROR_SYSTEM|2, "%s: cannot write", state->path);
5784887Schin 				if (ent->fts_statp->st_size > 0)
5794887Schin 					close(rfd);
5804887Schin 				return 0;
5814887Schin 			}
5824887Schin 			else if (ent->fts_statp->st_size > 0)
5834887Schin 			{
5844887Schin 				if (!(ip = sfnew(NiL, NiL, SF_UNBOUND, rfd, SF_READ)))
5854887Schin 				{
5864887Schin 					error(ERROR_SYSTEM|2, "%s: %s read stream error", ent->fts_path, state->path);
5874887Schin 					close(rfd);
5884887Schin 					close(wfd);
5894887Schin 				}
5904887Schin 				else
5914887Schin 				{
5924887Schin 					n = 0;
5934887Schin 					if (!(op = sfnew(NiL, NiL, SF_UNBOUND, wfd, SF_WRITE)))
5944887Schin 					{
5954887Schin 						error(ERROR_SYSTEM|2, "%s: %s write stream error", ent->fts_path, state->path);
5964887Schin 						close(wfd);
5974887Schin 						sfclose(ip);
5984887Schin 					}
5994887Schin 					else
6004887Schin 					{
6014887Schin 						if (sfmove(ip, op, (Sfoff_t)SF_UNBOUND, -1) < 0)
6024887Schin 							n |= 3;
6034887Schin 						if (!sfeof(ip))
6044887Schin 							n |= 1;
6054887Schin 						if (sfsync(op) || state->sync && fsync(wfd) || sfclose(op))
6064887Schin 							n |= 2;
6074887Schin 						if (sfclose(ip))
6084887Schin 							n |= 1;
6094887Schin 						if (n)
6104887Schin 							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"));
6114887Schin 					}
6124887Schin 				}
6134887Schin 			}
6144887Schin 			else
6154887Schin 				close(wfd);
6164887Schin 		}
6174887Schin 		else if (S_ISBLK(ent->fts_statp->st_mode) || S_ISCHR(ent->fts_statp->st_mode) || S_ISFIFO(ent->fts_statp->st_mode))
6184887Schin 		{
6194887Schin 			if (mknod(state->path, ent->fts_statp->st_mode, idevice(ent->fts_statp)))
6204887Schin 			{
6214887Schin 				error(ERROR_SYSTEM|2, "%s: cannot copy special file to %s", ent->fts_path, state->path);
6224887Schin 				return 0;
6234887Schin 			}
6244887Schin 		}
6254887Schin 		else
6264887Schin 		{
6274887Schin 			error(2, "%s: cannot copy -- unknown file type 0%o", ent->fts_path, S_ITYPE(ent->fts_statp->st_mode));
6284887Schin 			return 0;
6294887Schin 		}
6304887Schin 		if (state->preserve)
6314887Schin 		{
6324887Schin 			if (ent->fts_info != FTS_SL)
6334887Schin 			{
6344887Schin 				if (stat(state->path, &st))
6354887Schin 					error(ERROR_SYSTEM|2, "%s: cannot stat", state->path);
6364887Schin 				else
6374887Schin 				{
6384887Schin 					if ((ent->fts_statp->st_mode & state->perm) != (st.st_mode & state->perm) && chmod(state->path, ent->fts_statp->st_mode & state->perm))
6394887Schin 						error(ERROR_SYSTEM|2, "%s: cannot reset mode to %s", state->path, fmtmode(st.st_mode & state->perm, 0) + 1);
6404887Schin 					preserve(state, state->path, &st, ent->fts_statp);
6414887Schin 				}
6424887Schin 			}
6434887Schin 			if (state->op == MV && remove(ent->fts_path))
6444887Schin 				error(ERROR_SYSTEM|1, "%s: cannot remove", ent->fts_path);
6454887Schin 		}
6464887Schin 		break;
6474887Schin 	case LN:
6484887Schin 		if ((*state->link)(ent->fts_path, state->path))
6494887Schin 			error(ERROR_SYSTEM|2, "%s: cannot link to %s", ent->fts_path, state->path);
6504887Schin 		break;
6514887Schin 	}
6524887Schin 	return 0;
6534887Schin }
6544887Schin 
6554887Schin int
b_cp(int argc,register char ** argv,void * context)6564887Schin b_cp(int argc, register char** argv, void* context)
6574887Schin {
6584887Schin 	register char*	file;
6594887Schin 	register char*	s;
6604887Schin 	char**		v;
6614887Schin 	char*		backup_type;
6624887Schin 	FTS*		fts;
66310898Sroland.mainz@nrubsig.org 	FTSENT*		ent;
6644887Schin 	const char*	usage;
6654887Schin 	int		path_resolve;
6664887Schin 	int		standard;
6674887Schin 	struct stat	st;
6688462SApril.Chin@Sun.COM 	State_t*	state;
6698462SApril.Chin@Sun.COM 	Shbltin_t*	sh;
670*12068SRoger.Faulkner@Oracle.COM 	void*		cleanup = context;
6714887Schin 
6724887Schin 	cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
6738462SApril.Chin@Sun.COM 	if (!(sh = CMD_CONTEXT(context)) || !(state = (State_t*)sh->ptr))
6748462SApril.Chin@Sun.COM 	{
6758462SApril.Chin@Sun.COM 		if (!(state = newof(0, State_t, 1, 0)))
6768462SApril.Chin@Sun.COM 			error(ERROR_SYSTEM|3, "out of space");
6778462SApril.Chin@Sun.COM 		if (sh)
6788462SApril.Chin@Sun.COM 			sh->ptr = state;
6798462SApril.Chin@Sun.COM 	}
6808462SApril.Chin@Sun.COM 	else
6818462SApril.Chin@Sun.COM 		memset(state, 0, offsetof(State_t, INITSTATE));
68210898Sroland.mainz@nrubsig.org 	state->context = context;
6838462SApril.Chin@Sun.COM 	state->presiz = -1;
6844887Schin 	backup_type = 0;
6858462SApril.Chin@Sun.COM 	state->flags = FTS_NOCHDIR|FTS_NOSEEDOTDIR;
6868462SApril.Chin@Sun.COM 	state->uid = geteuid();
6878462SApril.Chin@Sun.COM 	state->wflags = O_WRONLY|O_CREAT|O_TRUNC|O_BINARY;
6888462SApril.Chin@Sun.COM 	if (!state->tmp && !(state->tmp = sfstropen()))
6894887Schin 		error(ERROR_SYSTEM|3, "out of space [tmp string]");
6908462SApril.Chin@Sun.COM 	sfputr(state->tmp, usage_head, -1);
6914887Schin 	standard = !strcmp(astconf("CONFORMANCE", NiL, NiL), "standard");
6924887Schin 	switch (error_info.id[0])
6934887Schin 	{
6944887Schin 	case 'c':
6954887Schin 	case 'C':
6968462SApril.Chin@Sun.COM 		sfputr(state->tmp, usage_cp, -1);
6978462SApril.Chin@Sun.COM 		state->op = CP;
6988462SApril.Chin@Sun.COM 		state->stat = stat;
6994887Schin 		path_resolve = -1;
7004887Schin 		break;
7014887Schin 	case 'l':
7024887Schin 	case 'L':
7038462SApril.Chin@Sun.COM 		sfputr(state->tmp, usage_ln, -1);
7048462SApril.Chin@Sun.COM 		state->op = LN;
7058462SApril.Chin@Sun.COM 		state->flags |= FTS_PHYSICAL;
7068462SApril.Chin@Sun.COM 		state->link = link;
7078462SApril.Chin@Sun.COM 		state->stat = lstat;
7084887Schin 		path_resolve = 1;
7094887Schin 		break;
7104887Schin 	case 'm':
7114887Schin 	case 'M':
7128462SApril.Chin@Sun.COM 		sfputr(state->tmp, usage_mv, -1);
7138462SApril.Chin@Sun.COM 		state->op = MV;
7148462SApril.Chin@Sun.COM 		state->flags |= FTS_PHYSICAL;
7158462SApril.Chin@Sun.COM 		state->preserve = 1;
7168462SApril.Chin@Sun.COM 		state->stat = lstat;
7174887Schin 		path_resolve = 1;
7184887Schin 		break;
7194887Schin 	default:
7204887Schin 		error(3, "not implemented");
7214887Schin 		break;
7224887Schin 	}
7238462SApril.Chin@Sun.COM 	sfputr(state->tmp, usage_tail, -1);
7248462SApril.Chin@Sun.COM 	if (!(usage = sfstruse(state->tmp)))
7258462SApril.Chin@Sun.COM 		error(ERROR_SYSTEM|3, "%s: out of space", state->path);
7268462SApril.Chin@Sun.COM 	state->opname = state->op == CP ? ERROR_translate(0, 0, 0, "overwrite") : ERROR_translate(0, 0, 0, "replace");
7274887Schin 	for (;;)
7284887Schin 	{
7294887Schin 		switch (optget(argv, usage))
7304887Schin 		{
7314887Schin 		case 'a':
7328462SApril.Chin@Sun.COM 			state->flags |= FTS_PHYSICAL;
7338462SApril.Chin@Sun.COM 			state->preserve = 1;
7348462SApril.Chin@Sun.COM 			state->recursive = 1;
7354887Schin 			path_resolve = 1;
7364887Schin 			continue;
7374887Schin 		case 'b':
7388462SApril.Chin@Sun.COM 			state->backup = 1;
7394887Schin 			continue;
7404887Schin 		case 'f':
7418462SApril.Chin@Sun.COM 			state->force = 1;
7428462SApril.Chin@Sun.COM 			if (state->op != CP || !standard)
7438462SApril.Chin@Sun.COM 				state->interactive = 0;
7444887Schin 			continue;
7454887Schin 		case 'h':
7468462SApril.Chin@Sun.COM 			state->hierarchy = 1;
7474887Schin 			continue;
7484887Schin 		case 'i':
7498462SApril.Chin@Sun.COM 			state->interactive = 1;
7508462SApril.Chin@Sun.COM 			if (state->op != CP || !standard)
7518462SApril.Chin@Sun.COM 				state->force = 0;
7524887Schin 			continue;
7534887Schin 		case 'l':
7548462SApril.Chin@Sun.COM 			state->op = LN;
7558462SApril.Chin@Sun.COM 			state->link = link;
7568462SApril.Chin@Sun.COM 			state->stat = lstat;
7574887Schin 			continue;
7584887Schin 		case 'p':
7598462SApril.Chin@Sun.COM 			state->preserve = 1;
7604887Schin 			continue;
7614887Schin 		case 'r':
7628462SApril.Chin@Sun.COM 			state->recursive = 1;
7634887Schin 			if (path_resolve < 0)
7644887Schin 				path_resolve = 0;
7654887Schin 			continue;
7664887Schin 		case 's':
7678462SApril.Chin@Sun.COM 			state->op = LN;
7688462SApril.Chin@Sun.COM 			state->link = pathsetlink;
7698462SApril.Chin@Sun.COM 			state->stat = lstat;
7704887Schin 			continue;
7714887Schin 		case 'u':
7728462SApril.Chin@Sun.COM 			state->update = 1;
7734887Schin 			continue;
7744887Schin 		case 'v':
7758462SApril.Chin@Sun.COM 			state->verbose = 1;
7764887Schin 			continue;
7774887Schin 		case 'x':
7788462SApril.Chin@Sun.COM 			state->flags |= FTS_XDEV;
7794887Schin 			continue;
7804887Schin 		case 'F':
7814887Schin #if _lib_fsync
7828462SApril.Chin@Sun.COM 			state->sync = 1;
7834887Schin #else
7844887Schin 			error(1, "%s not implemented on this system", opt_info.name);
7854887Schin #endif
7864887Schin 			continue;
7874887Schin 		case 'H':
7888462SApril.Chin@Sun.COM 			state->flags |= FTS_META|FTS_PHYSICAL;
7894887Schin 			path_resolve = 1;
7904887Schin 			continue;
7914887Schin 		case 'L':
7928462SApril.Chin@Sun.COM 			state->flags &= ~FTS_PHYSICAL;
7934887Schin 			path_resolve = 1;
7944887Schin 			continue;
7954887Schin 		case 'P':
7968462SApril.Chin@Sun.COM 			state->flags &= ~FTS_META;
7978462SApril.Chin@Sun.COM 			state->flags |= FTS_PHYSICAL;
7984887Schin 			path_resolve = 1;
7994887Schin 			continue;
8004887Schin 		case 'R':
8018462SApril.Chin@Sun.COM 			state->recursive = 1;
8028462SApril.Chin@Sun.COM 			state->flags &= ~FTS_META;
8038462SApril.Chin@Sun.COM 			state->flags |= FTS_PHYSICAL;
8044887Schin 			path_resolve = 1;
8054887Schin 			continue;
8064887Schin 		case 'S':
8078462SApril.Chin@Sun.COM 			state->suffix = opt_info.arg;
8084887Schin 			continue;
8094887Schin 		case 'V':
8104887Schin 			backup_type = opt_info.arg;
8114887Schin 			continue;
8124887Schin 		case '?':
8134887Schin 			error(ERROR_USAGE|4, "%s", opt_info.arg);
8144887Schin 			continue;
8154887Schin 		case ':':
8164887Schin 			error(2, "%s", opt_info.arg);
8174887Schin 			continue;
8184887Schin 		}
8194887Schin 		break;
8204887Schin 	}
8214887Schin 	argc -= opt_info.index + 1;
8224887Schin 	argv += opt_info.index;
8234887Schin 	if (*argv && streq(*argv, "-") && !streq(*(argv - 1), "--"))
8244887Schin 	{
8254887Schin 		argc--;
8264887Schin 		argv++;
8274887Schin 	}
8284887Schin 	if (!(v = (char**)stkalloc(stkstd, (argc + 2) * sizeof(char*))))
829*12068SRoger.Faulkner@Oracle.COM 		error(ERROR_SYSTEM|3, "out of space");
8304887Schin 	memcpy(v, argv, (argc + 1) * sizeof(char*));
8314887Schin 	argv = v;
8328462SApril.Chin@Sun.COM 	if (!standard)
8334887Schin 	{
8348462SApril.Chin@Sun.COM 		state->wflags |= O_EXCL;
8358462SApril.Chin@Sun.COM 		if (!argc)
8368462SApril.Chin@Sun.COM 		{
8378462SApril.Chin@Sun.COM 			argc++;
8388462SApril.Chin@Sun.COM 			argv[1] = (char*)dot;
8398462SApril.Chin@Sun.COM 		}
8404887Schin 	}
8418462SApril.Chin@Sun.COM 	if (state->backup)
8424887Schin 	{
8434887Schin 		if (!(file = backup_type) && !(backup_type = getenv("VERSION_CONTROL")))
8448462SApril.Chin@Sun.COM 			state->backup = BAK_existing;
8454887Schin 		else
8464887Schin 			switch (strkey(backup_type))
8474887Schin 			{
8484887Schin 			case HASHKEY6('e','x','i','s','t','i'):
8494887Schin 			case HASHKEY5('e','x','i','s','t'):
8504887Schin 			case HASHKEY4('e','x','i','s'):
8514887Schin 			case HASHKEY3('e','x','i'):
8524887Schin 			case HASHKEY2('e','x'):
8534887Schin 			case HASHKEY1('e'):
8544887Schin 			case HASHKEY3('n','i','l'):
8554887Schin 			case HASHKEY2('n','i'):
8568462SApril.Chin@Sun.COM 				state->backup = BAK_existing;
8574887Schin 				break;
8584887Schin 			case HASHKEY5('n','e','v','e','r'):
8594887Schin 			case HASHKEY4('n','e','v','e'):
8604887Schin 			case HASHKEY3('n','e','v'):
8614887Schin 			case HASHKEY2('n','e'):
8624887Schin 			case HASHKEY6('s','i','m','p','l','e'):
8634887Schin 			case HASHKEY5('s','i','m','p','l'):
8644887Schin 			case HASHKEY4('s','i','m','p'):
8654887Schin 			case HASHKEY3('s','i','m'):
8664887Schin 			case HASHKEY2('s','i'):
8674887Schin 			case HASHKEY1('s'):
8688462SApril.Chin@Sun.COM 				state->backup = BAK_simple;
8694887Schin 				break;
8704887Schin 			case HASHKEY6('n','u','m','b','e','r'):
8714887Schin 			case HASHKEY5('n','u','m','b','e'):
8724887Schin 			case HASHKEY4('n','u','m','b'):
8734887Schin 			case HASHKEY3('n','u','m'):
8744887Schin 			case HASHKEY2('n','u'):
8754887Schin 			case HASHKEY1('t'):
8768462SApril.Chin@Sun.COM 				state->backup = BAK_number;
8774887Schin 				break;
8784887Schin 			default:
8794887Schin 				if (file)
8804887Schin 					error(2, "%s: unknown backup type", backup_type);
8814887Schin 				break;
8824887Schin 			}
8838462SApril.Chin@Sun.COM 		if (!state->suffix && !(state->suffix = getenv("SIMPLE_BACKUP_SUFFIX")))
8848462SApril.Chin@Sun.COM 			state->suffix = "~";
8858462SApril.Chin@Sun.COM 		state->suflen = strlen(state->suffix);
8864887Schin 	}
8874887Schin 	if (argc <= 0 || error_info.errors)
8884887Schin 		error(ERROR_USAGE|4, "%s", optusage(NiL));
8894887Schin 	if (!path_resolve)
8908462SApril.Chin@Sun.COM 		state->flags |= fts_flags();
8914887Schin 	file = argv[argc];
8924887Schin 	argv[argc] = 0;
8934887Schin 	if (s = strrchr(file, '/'))
8944887Schin 	{
8954887Schin 		while (*s == '/')
8964887Schin 			s++;
8974887Schin 		if (!(!*s || *s == '.' && (!*++s || *s == '.' && !*++s)))
8984887Schin 			s = 0;
8994887Schin 	}
9004887Schin 	if (file != (char*)dot)
9014887Schin 		pathcanon(file, 0);
9028462SApril.Chin@Sun.COM 	if (!(state->directory = !stat(file, &st) && S_ISDIR(st.st_mode)) && argc > 1)
9034887Schin 		error(ERROR_USAGE|4, "%s", optusage(NiL));
9048462SApril.Chin@Sun.COM 	if (s && !state->directory)
9054887Schin 		error(3, "%s: not a directory", file);
9068462SApril.Chin@Sun.COM 	if ((state->fs3d = fs3d(FS3D_TEST)) && strmatch(file, "...|*/...|.../*"))
9078462SApril.Chin@Sun.COM 		state->official = 1;
9088462SApril.Chin@Sun.COM 	state->postsiz = strlen(file);
9098462SApril.Chin@Sun.COM 	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*12068SRoger.Faulkner@Oracle.COM 		error(ERROR_SYSTEM|3, "out of space");
9118462SApril.Chin@Sun.COM 	memcpy(state->path, file, state->postsiz + 1);
9128462SApril.Chin@Sun.COM 	if (state->directory && state->path[state->postsiz - 1] != '/')
9138462SApril.Chin@Sun.COM 		state->path[state->postsiz++] = '/';
9148462SApril.Chin@Sun.COM 	if (state->hierarchy)
9154887Schin 	{
9168462SApril.Chin@Sun.COM 		if (!state->directory)
9174887Schin 			error(3, "%s: last argument must be a directory", file);
9188462SApril.Chin@Sun.COM 		state->missmode = st.st_mode;
9194887Schin 	}
9208462SApril.Chin@Sun.COM 	state->perm = state->uid ? S_IPERM : (S_IPERM & ~S_ISVTX);
9218462SApril.Chin@Sun.COM 	if (!state->recursive)
9228462SApril.Chin@Sun.COM 		state->flags |= FTS_TOP;
9238462SApril.Chin@Sun.COM 	if (fts = fts_open(argv, state->flags, NiL))
9244887Schin 	{
9258462SApril.Chin@Sun.COM 		while (!sh_checksig(context) && (ent = fts_read(fts)) && !visit(state, ent));
9264887Schin 		fts_close(fts);
9274887Schin 	}
9288462SApril.Chin@Sun.COM 	else if (state->link != pathsetlink)
9298462SApril.Chin@Sun.COM 		switch (state->op)
9304887Schin 		{
9314887Schin 		case CP:
9324887Schin 			error(ERROR_SYSTEM|2, "%s: cannot copy", argv[0]);
9334887Schin 			break;
9344887Schin 		case LN:
9354887Schin 			error(ERROR_SYSTEM|2, "%s: cannot link", argv[0]);
9364887Schin 			break;
9374887Schin 		case MV:
9384887Schin 			error(ERROR_SYSTEM|2, "%s: cannot move", argv[0]);
9394887Schin 			break;
9404887Schin 		}
9418462SApril.Chin@Sun.COM 	else if ((*state->link)(*argv, state->path))
9428462SApril.Chin@Sun.COM 		error(ERROR_SYSTEM|2, "%s: cannot link to %s", *argv, state->path);
943*12068SRoger.Faulkner@Oracle.COM 	if (cleanup && !sh)
944*12068SRoger.Faulkner@Oracle.COM 	{
945*12068SRoger.Faulkner@Oracle.COM 		if (state->path)
946*12068SRoger.Faulkner@Oracle.COM 			free(state->path);
947*12068SRoger.Faulkner@Oracle.COM 		free(state);
948*12068SRoger.Faulkner@Oracle.COM 	}
9494887Schin 	return error_info.errors != 0;
9504887Schin }
951