/*

Directory Opus 4
Original GPL release version 4.12
Copyright 1993-2000 Jonathan Potter

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

All users of Directory Opus 4 (including versions distributed
under the GPL) are entitled to upgrade to the latest version of
Directory Opus version 5 at a reduced price. Please see
http://www.gpsoft.com.au for more information.

The release of Directory Opus 4 under the GPL in NO WAY affects
the existing commercial status of Directory Opus 5.

*/

#include "DOpus.h"

struct recurse {
	struct recurse *last;
	char *dir,*dest;
	int data;
	APTR data2;
	APTR data3;
	BPTR lock;
	struct FileInfoBlock *info;
};

struct makedirlist {
	struct makedirlist *last,*next;
	char *path;
};

struct recurse *current_recurse;

recursedir(fdir,fdest,dowhat,fdata)
char *fdir,*fdest;
int dowhat,fdata;
{
	BPTR mylock;
	struct FileInfoBlock __aligned myfinfo;
	struct FileInfoBlock __aligned enfinfo;
	char *name,*dir,*dest,*dname,*ddir,*adir,*adest,*ndir,*ndest;
	int suc,cont,ret,a,err,w1=1-data_active_window,adata,depth,b,rtry,data,*pstuff,blocks;
	struct recpath *crec=NULL,*trec;
	struct RecursiveDirectory
		*cur_recurse,*addparent_recurse,*new_rec,*pos_rec,*cur_parent,
		*cur_lastparent=NULL;
	APTR data2=NULL,adata2=NULL,data3=NULL,adata3=NULL;
	struct DOpusRemember *recurserem=NULL;
	struct makedirlist *first_makedir=NULL;

	if (dowhat&R_STARDIR) {
		rec_firstpath=NULL;
		rec_pathkey=NULL;
	}

	if (dowhat&R_GETNAMES) {
		first_recurse=NULL;
		recurse_dir_key=NULL;
		cur_recurse=NULL;
		addparent_recurse=NULL;
	}

	data=fdata;

	current_recurse=NULL; ret=depth=0; recurse_max_depth=0;
	dos_global_bytecount=0;
	dos_global_copiedbytes=0;
	dos_global_deletedbytes=0;
	dos_global_blocksneeded=0;

	if (!(mylock=Lock(fdir,ACCESS_READ))) {
		doerror(IoErr());
		return(-1);
	}
	Examine(mylock,&myfinfo);

	if (!(name=LAllocRemember(&recurserem,2560,MEMF_CLEAR))) {
		doerror(IoErr());
		ret=-1;
		goto goaway;
	}
	dir=name+512; dest=name+1024; dname=name+1536; ddir=name+2048;
	if (fdir) strcpy(dir,fdir);
	if (fdest) strcpy(dest,fdest);

	if (dowhat&R_COPY) {
		strcpy(ddir,dest);
		if (!(a=copymakedir(&recurserem,&first_makedir,dest,&myfinfo)) || a==-1) {
			UnLock(mylock);
			if (a==0) ret=-3;
			else ret=-10;
			goto goaway;
		}
	}
	cont=ExNext(mylock,&myfinfo);

	FOREVER {
		if (status_haveaborted) {
			myabort();
			ret=-10;
			break;
		}
		if (!cont) {
			if (current_recurse) {
				if (mylock) UnLock(mylock);
				strcpy(dname,dir);
				mylock=current_recurse->lock;
				CopyMem((char *)current_recurse->info,(char *)&myfinfo,sizeof(struct FileInfoBlock));
				strcpy(dir,current_recurse->dir);
				strcpy(dest,current_recurse->dest);
				data=current_recurse->data;
				data2=current_recurse->data2;
				data3=current_recurse->data3;
				current_recurse=current_recurse->last;

				strcpy(name,dir);
				TackOn(name,myfinfo.fib_FileName,512);

				if (dowhat&R_GETNAMES) {
					cur_recurse=(struct RecursiveDirectory *)data2;
					cur_lastparent=(struct RecursiveDirectory *)data3;
					addparent_recurse=NULL;
				}

				if (dowhat&R_COPY) {
					strcpy(ddir,dest);
					if (config->copyflags&COPY_DATE) {
						TackOn(ddir,myfinfo.fib_FileName,512);
						setdate(ddir,&myfinfo.fib_Date);
						strcpy(ddir,dest);
					}
				}

				a=0;
				if (dowhat&R_COMMENT) {
					FOREVER {
						if (!(SetComment(name,dest))) {
							err=IoErr();
							doerror(err);
							a=checkerror(globstring[STR_COMMENTING],myfinfo.fib_FileName,err);
							if (a==1) continue;
						}
						break;
					}
					if (a==3) break;
				}

				if (dowhat&R_PROTECT) {
					pstuff=(int *)data;
					b=getnewprot(myfinfo.fib_Protection,pstuff[0],pstuff[1]);
					FOREVER {
						if (!(SetProtection(name,b))) {
							err=IoErr();
							doerror(err);
							a=checkerror(globstring[STR_PROTECTING],myfinfo.fib_FileName,err);
							if (a==1) continue;
						}
						break;
					}
					if (a==3) break;
				}

				if (dowhat&R_DATESTAMP) {
					FOREVER {
						if ((err=setdate(name,(struct DateStamp *)data))!=1) {
							doerror(err);
							a=checkerror(globstring[STR_DATESTAMPING],myfinfo.fib_FileName,err);
							if (a==1) continue;
						}
						break;
					}
					if (a==3) break;
				}

				cont=ExNext(mylock,&myfinfo);

				if (dowhat&R_DELETE && depth>0) {
					a=strlen(dname);
					if (a>0 && dname[a-1]=='/') dname[a-1]=0;
					if ((a=delfile(dname,BaseName(dname),globstring[STR_DELETING],
						glob_unprotect_all,1))==-1) {
						myabort();
						ret=-10;
						break;
					}
					if (a==2) glob_unprotect_all=1;
				}
				--depth;
				continue;
			}
			else break;
		}
		CopyMem((char *)&myfinfo,(char *)&enfinfo,sizeof(struct FileInfoBlock));

		strcpy(name,dir);
		TackOn(name,enfinfo.fib_FileName,512);

		if (enfinfo.fib_DirEntryType>0) {
			++dos_global_blocksneeded;
			if (enfinfo.fib_DirEntryType!=4) {
				adir=NULL;
				dofilename(name);
				if (dowhat&R_GETNAMES) {
					if (new_rec=LAllocRemember(&recurse_dir_key,sizeof(struct RecursiveDirectory),MEMF_CLEAR)) {
						strcpy(new_rec->name,enfinfo.fib_FileName);
						CopyMem((char *)&enfinfo.fib_Date,(char *)&new_rec->date,sizeof(struct DateStamp));
						if (addparent_recurse) {
							addparent_recurse->child=new_rec;
							cur_lastparent=addparent_recurse;
							cur_recurse=new_rec;
						}
						else if (!first_recurse) {
							first_recurse=new_rec;
							cur_recurse=new_rec;
						}
						else if (cur_recurse) {
							if (LStrCmpI(new_rec->name,cur_recurse->name)<0) {
								if (first_recurse==cur_recurse) first_recurse=new_rec;
								if (cur_lastparent && cur_lastparent->child==cur_recurse)
									cur_lastparent->child=new_rec;
								new_rec->next=cur_recurse;
								cur_recurse=new_rec;
							}
							else {
								pos_rec=cur_recurse;
								FOREVER {
									if (!pos_rec->next ||
										(LStrCmpI(new_rec->name,pos_rec->next->name)<0)) {
										new_rec->next=pos_rec->next;
										pos_rec->next=new_rec;
										break;
									}
									pos_rec=pos_rec->next;
								}
							}
						}
						else cur_recurse=new_rec;
						cur_parent=new_rec;
					}
					adir=dir; adest=dest; ndir=name; ndest=dest;
					adata2=(APTR)cur_recurse;
					adata3=(APTR)cur_lastparent;
					addparent_recurse=cur_parent;
				}
				strcpy(dname,dest);
				if (dowhat&R_COPY) {

					dotaskmsg(hotkeymsg_port,PROGRESS_UPDATE,-2,0,enfinfo.fib_FileName,1);

					TackOn(dname,enfinfo.fib_FileName,512);
					adir=dir; adest=dest; adata=data; ndir=name; ndest=dname;
					strcpy(ddir,dname);
					if ((a=copymakedir(&recurserem,&first_makedir,ddir,&enfinfo))==-1) {
						ret=-10;
						break;
					}
					else if (a==0) adir=NULL;
				}
				if (dowhat&R_DELETE) {
					rtry=1;
delloop:
					if (!DeleteFile(name)) {
						if (config->deleteflags&8 && rtry==1 && IoErr()==222) {
							rtry=0;
							SetProtection(name,0);
							goto delloop;
						}
						adir=dir; adest=dest; adata=data; ndir=name; ndest=dname;
					}
					else {
						if (config->dynamicflags&1) seename(data_active_window);
						adir=NULL;
					}
				}
				if (dowhat&R_HUNT || dowhat&R_SEARCH || dowhat&R_COMMENT ||
					dowhat&R_PROTECT || dowhat&R_DATESTAMP || dowhat&R_GETBYTES ||
					dowhat&R_STARDIR) {
					adir=dir; adest=dest; adata=data; ndir=name; ndest=dest;
					adata2=data2; adata3=data3;
				}
				if (adir) {
					if (!(addrecurse(&recurserem,adir,adest,adata,adata2,adata3,mylock,&enfinfo))) {
						cont=0;
						continue;
					}
					strcpy(dir,ndir);
					strcpy(dest,ndest);
					if (!(mylock=Lock(dir,ACCESS_READ))) {
						cont=0;
						continue;
					}
					Examine(mylock,&myfinfo);
					cont=ExNext(mylock,&myfinfo);
					++depth;
					if (depth>recurse_max_depth) recurse_max_depth=depth;
					continue;
				}
			}
		}
		cont=ExNext(mylock,&myfinfo);

		if (enfinfo.fib_DirEntryType<0) {
			a=0;
			dos_global_bytecount+=enfinfo.fib_Size;
			if (dowhat&R_GETBYTES && data) {
				blocks=(enfinfo.fib_Size+(data-1))/data;
				dos_global_blocksneeded+=blocks+(blocks/72)+1;
			}

			if (dowhat&R_GETNAMES) goto skipgetnam;
			dofilename(name);
			if (dowhat&R_STARDIR) {
				if ((trec=LAllocRemember(&rec_pathkey,sizeof(struct recpath),MEMF_CLEAR)) &&
					(trec->path=LAllocRemember(&rec_pathkey,(strlen(name)+1)-data,MEMF_CLEAR))) {
					trec->next=NULL;
					strcpy(trec->path,&name[data]);
					if (crec) crec->next=trec;
					crec=trec;
					if (!rec_firstpath) rec_firstpath=trec;
				}
			}
			if (!str_filter_parsed[0] ||
				LMatchPatternI(str_filter_parsed,enfinfo.fib_FileName)) {
				if (dowhat&R_COPY) {
					strcpy(dname,ddir);
					TackOn(dname,enfinfo.fib_FileName,512);

					dotaskmsg(hotkeymsg_port,PROGRESS_UPDATE,-2,0,enfinfo.fib_FileName,1);

					a=0;
					FOREVER {
						if (!(a=copyfile(name,dname,&err,-1,NULL,0))) {
							doerror(err);
							a=checkerror(globstring[STR_COPYING],enfinfo.fib_FileName,err);
							if (a==1) continue;
							if (a!=3) seename(data_active_window);
						}
						else if (a==-1) {
							myabort();
							a=3;
							break;
						}
						else {
							if (dowhat==R_COPY) {
								if (config->copyflags&COPY_ARC && !(enfinfo.fib_Protection&FIBF_ARCHIVE))
									SetProtection(name,enfinfo.fib_Protection|FIBF_ARCHIVE);
							}
							dos_global_copiedbytes+=enfinfo.fib_Size;
						}
						if (config->dynamicflags&1) seename(data_active_window);
						break;
					}
					if (a==3) {
						ret=-10;
						break;
					}
					if (config->dynamicflags&1) {
						seename(data_active_window); seename(w1);
					}
				}
				if (dowhat&R_DELETE) {
					if ((a=delfile(name,enfinfo.fib_FileName,globstring[STR_DELETING],
						glob_unprotect_all,1))==-1) {
						myabort();
						ret=-10;
						break;
					}
					if (a==2) glob_unprotect_all=1;
					if (config->dynamicflags&1) seename(data_active_window);
					dos_global_deletedbytes+=enfinfo.fib_Size;
				}
				if (dowhat&R_COMMENT) {
					FOREVER {
						if (!(SetComment(name,dest))) {
							err=IoErr();
							doerror(err);
							a=checkerror(globstring[STR_COMMENTING],enfinfo.fib_FileName,err);
							if (a==1) continue;
						}
						break;
					}
					if (a==3) {
						ret=-10;
						break;
					}
				}
				if (dowhat&R_PROTECT) {
					pstuff=(int *)data;
					b=getnewprot(enfinfo.fib_Protection,pstuff[0],pstuff[1]);
					FOREVER {
						if (!(SetProtection(name,b))) {
							err=IoErr();
							doerror(err);
							a=checkerror(globstring[STR_PROTECTING],enfinfo.fib_FileName,err);
							if (a==1) continue;
						}
						break;
					}
					if (a==3) {
						ret=-10;
						break;
					}
				}
				if (dowhat&R_DATESTAMP) {
					FOREVER {
						if ((err=setdate(name,(struct DateStamp *)data))!=1) {
							err=IoErr();
							doerror(err);
							a=checkerror(globstring[STR_DATESTAMPING],enfinfo.fib_FileName,err);
							if (a==1) continue;
						}
						break;
					}
					if (a==3) {
						ret=-10;
						break;
					}
				}
				if (dowhat&R_HUNT) {
					suc=huntfile(enfinfo.fib_FileName,name,&a);
					ret+=a;
					if (suc) {
						if (suc==-1) {
							myabort();
							ret=-10;
						}
						else ret=suc;
						break;
					}
				}
				if (dowhat&R_SEARCH) {
					suc=filesearch(name,&a,0);
					ret+=a;
					busy();
					if (suc==2) {
						if (!simplerequest(globstring[STR_CONTINUE_WITH_SEARCH],
							globstring[STR_CONTINUE],str_cancelstring,NULL)) {
							okay();
							ret=-2;
							break;
						}
					}
skipgetnam:
					if (suc==-1) {
						myabort();
						ret=-10;
						break;
					}
				}
			}
		}
	}
	if (mylock) UnLock(mylock);
	while (current_recurse) {
		UnLock(current_recurse->lock);
		current_recurse=current_recurse->last;
	}
	if (first_makedir &&
		dowhat==R_COPY &&
		str_filter_parsed[0]) {

		struct makedirlist *last;

		last=first_makedir;
		while (last->next) last=last->next;

		while (last) {
			DeleteFile(last->path);
			last=last->last;
		}
	}
goaway:
	if (recurserem) LFreeRemember(&recurserem);
	return(ret);
}

addrecurse(key,dir,dest,data,data2,data3,lock,info)
struct DOpusRemember **key;
char *dir,*dest;
int data;
APTR data2,data3;
BPTR lock;
struct FileInfoBlock *info;
{
	struct recurse *rec;

	if (!(rec=LAllocRemember(key,sizeof(struct recurse),MEMF_CLEAR)))
		return(0);
	if (current_recurse) rec->last=current_recurse;
	else rec->last=NULL;
	if (!(rec->dir=LAllocRemember(key,strlen(dir)+1,MEMF_CLEAR)) ||
		!(rec->dest=LAllocRemember(key,strlen(dest)+1,MEMF_CLEAR)))
		return(0);
	strcpy(rec->dir,dir); strcpy(rec->dest,dest);
	rec->data=data;
	rec->data2=data2;
	rec->data3=data3;
	rec->lock=lock;
	if (!(rec->info=LAllocRemember(key,sizeof(struct FileInfoBlock),0)))
		return(0);
	CopyMem((char *)info,(char *)rec->info,sizeof(struct FileInfoBlock));
	current_recurse=rec;
	return(1);
}

copymakedir(key,first,dirname,finfo)
struct DOpusRemember **key;
struct makedirlist **first;
char *dirname;
struct FileInfoBlock *finfo;
{
	int exist,a,err;
	BPTR mylock;

	exist=CheckExist(dirname,NULL);
	if (exist<=0) {
loop:
		if (exist<0 || !(mylock=CreateDir(dirname))) {
			if (exist<0) err=203;
			else err=IoErr();
			doerror(err);
			a=checkerror(globstring[STR_CREATING],finfo->fib_FileName,err);
			if (a==1) goto loop;
			if (a==2) return(0);
			return(-1);
		}
		if (mylock) {
			struct makedirlist *list,*pos;

			UnLock(mylock);

			if (config->copyflags&COPY_DATE) setdate(dirname,&finfo->fib_Date);
			if (config->copyflags&COPY_PROT) SetProtection(dirname,finfo->fib_Protection);
			if (config->copyflags&COPY_NOTE) SetComment(dirname,finfo->fib_Comment);

			if ((list=LAllocRemember(key,sizeof(struct makedirlist),MEMF_CLEAR)) &&
				(list->path=LAllocRemember(key,strlen(dirname)+1,0))) {
				strcpy(list->path,dirname);
				if (!(pos=*first)) *first=list;
				else {
					while (pos->next) pos=pos->next;
					pos->next=list;
					list->last=pos;
				}
			}
		}
	}
	return(1);
}
