/*
 * Copyright (c) 2003, Daniel Bryan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or 
 * without modification, are permitted provided that the following 
 * conditions are met:
 * - Redistributions of source code must retain the above copyright 
 *   notice, this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in 
 *   the documentation and/or other materials provided with the 
 *   distribution.
 * - Neither the name of the author nor the names of its contributors
 *   may be used to endorse or promote products derived from this 
 *   software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <err.h>
#include <errno.h>
#include <string.h>

#include "common.h"
#include "path.h"
#include "misc.h"
#include "copy.h"
#include "screen.h"

int copyall(char *args[],int fts_opt,int type) {
	FTS *ftsarg;
	FTSENT *ftsf;
	struct stat st;
	int dne = 0;
	int ret = 0;
	mode_t mode,mask;
	int base = 0;
		
	mask = ~umask(0777);
	umask(~mask);
		
	if(pathchsize(dest.opath,dest.opath)) {
		ret = 1;
		return 1;
	}
	
	scrn_updatefc(1);
	
	ftsarg = fts_open(args,fts_opt,0);
	if(ftsarg == NULL) {
		logadds(LOG_ERR,"fts: %s",strerror(errno),NULL);
		ret = 1;
		return 1;
	}
	
	while((ftsf = fts_read(ftsarg)) != NULL) {
		
		switch(ftsf->fts_info) {
		case FTS_NS:
		case FTS_DNR:
		case FTS_ERR:
			print_error(ftsf->fts_path,ftsf->fts_errno);
			/* cntfiles() does not count FTS_DNR */
			if(ftsf->fts_info != FTS_DNR)
				curfile++;
			ret = 1;
			continue;
		case FTS_DC:
			logadds(LOG_ERR,"%s: directory causes a cycle",ftsf->fts_path,NULL);
			ret = 1;
			continue;
		}

		dest.path[0] = '\0';
		strcpy(dest.path,dest.opath);
			
		if(type != T_FILE) {
			if(type == T_DIR) {
				if(ftsf->fts_level == FTS_ROOTLEVEL) {
					base = pathnamed(ftsf->fts_path);
				}
			} else if(type == T_NED) {
				if(ftsf->fts_level == FTS_ROOTLEVEL)
					base = ftsf->fts_pathlen;
			}
			if(pathdadd(dest.path,ftsf->fts_path+base))
				continue;
		}

		if(ftsf->fts_info == FTS_DP) {
			if(!ftsf->fts_number) {
				continue;
			}
			if(setperm(dest.path,ftsf->fts_statp) != 0) {
				mode = ftsf->fts_statp->st_mode;
				if((mode & (S_ISUID | S_ISGID)) || \
				  ((mode | S_IRWXU) & mask) != (mode & mask)) {
				  	if(chmod(dest.path,mode & mask) != 0) {
				  		logadds(LOG_ERR,"chmod: %s",strerror(errno),NULL);
				  	}
				}
			}
			continue;
		}
		
		if(stat(dest.path,&st) == -1) {
			dne = 1;
		} else {
			if((st.st_dev==ftsf->fts_statp->st_dev) &&
			   (st.st_ino==ftsf->fts_statp->st_ino)) {
				logadds(LOG_ERR,"%s and %s are identical, not copied",\
					ftsf->fts_path,dest.path);
				if(S_ISDIR(st.st_mode))
					fts_set(ftsarg,ftsf,FTS_SKIP);
				continue;
			}
			if(!S_ISDIR(ftsf->fts_statp->st_mode)&&(S_ISDIR(st.st_mode))){
				logadds(LOG_ERR,"Can not overwrite %s with non-directory %s",\
					dest.path,ftsf->fts_path);
				ret = 1;
				continue;
			}
			dne = 0;
		}
			
		switch(ftsf->fts_statp->st_mode & S_IFMT) {
		case S_IFLNK:
			curfile++;
			copylink(ftsf->fts_path,dest.path) ? ret = 1 : 0;
			break;
		case S_IFDIR:
			if(!Rflag) {
				logadds(LOG_ERR,\
					"%s is a directory, not copied",ftsf->fts_path,NULL);
				fts_set(ftsarg,ftsf,FTS_SKIP);
			} else {
				if(dne) {
					if(mkdir(dest.path,\
						ftsf->fts_statp->st_mode | S_IRWXU) < 0) {
						logadds(LOG_ERR,"%s: Can not create directory",\
							dest.path,NULL);
						fts_set(ftsarg,ftsf,FTS_SKIP);
					} else
						logadds(LOG_VRB,"%s: Created",dest.path,NULL);
				} else if (!S_ISDIR(st.st_mode)) {
					logadds(LOG_ERR,\
						"Can not overwrite non-directory %s with %s",\
						dest.path,ftsf->fts_path);
					fts_set(ftsarg,ftsf,FTS_SKIP);
				}
				ftsf->fts_number = (pflag) || dne;
			}
			break;
		case S_IFBLK:
		case S_IFCHR:
			curfile++;
			if(Rflag)
				copyspecial(dest.path,ftsf->fts_statp) ? ret = 1 : 0;
			else
				copyfile(ftsf->fts_path,dest.path,ftsf->fts_statp)\
					? ret = 1 : 0;
			break;
		case S_IFIFO:
			curfile++;
			if(Rflag)
				copyfifo(dest.path,ftsf->fts_statp) ? ret = 1 : 0;
			else
				copyfile(ftsf->fts_path,dest.path,ftsf->fts_statp) ? ret = 1 : 0;
			break;
		default:
			curfile++;
			copyfile(ftsf->fts_path,dest.path,ftsf->fts_statp)\
				? ret = 1 : 0;
			break;
		}
	}
	if(errno) {
		logadds(LOG_ERR,"%s",strerror(ftsf->fts_errno),NULL);
		ret = 1;
		return 1;
	}
	return 0;
}
