/*

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"

#define OLD_ACTION_Dummy            20000

#define OLD_ACTION_SET_USER         (OLD_ACTION_Dummy+0)
#define OLD_ACTION_SET_GROUP        (OLD_ACTION_Dummy+1)
#define OLD_ACTION_UID_TO_USERINFO  (OLD_ACTION_Dummy+2)
#define OLD_ACTION_GID_TO_GROUPINFO (OLD_ACTION_Dummy+3)

#define ACTION_UID_TO_USERINFO      1037
#define ACTION_GID_TO_GROUPINFO     1038

doparent(str)
char *str;
{
	int i,b,c;

	c=strlen(str);
	if (c==0) return(0);
	i=c-1;
	if (str[i]==':') {
		char tempbuf[256];

		expand_path(str,tempbuf);
		if ((LStrCmpI(str,tempbuf))==0 ||
			!(doparent(tempbuf))) return(0);
		strcpy(str,tempbuf);
		return(1);
	}
	if (str[i]=='/') --i;
	for (b=i;b>-1;b--) {
		if (str[b]=='/') { ++b; break; }
		if (str[b]==':') { ++b; break; }
	}
	if (b<0) expand_path("",str);
	else str[b]=0;
	return(1);
}

doroot(str)
char *str;
{
	int i,b,f=0,c;

	c=strlen(str);
	if (c==0) return(0);
	i=c;
	for (b=0;b<i;b++) if (str[b]==':') { ++b; f=1; break; }
	if (f==1 && b==i) {
		char tempbuf[256];

		expand_path(str,tempbuf);
		if ((LStrCmpI(str,tempbuf))==0 ||
			!(doroot(tempbuf))) return(0);
		strcpy(str,tempbuf);
		return(1);
	}
	else if (f>0) str[b]=0;
	else expand_path("",str);
	return(1);
}

addfile(dir,win,name,size,type,date,comment,protection,subtype,show,dispstr,addafter,ownerid,groupid)
struct DirectoryWindow *dir;
int win;
char *name;
int size,type;
struct DateStamp *date;
char *comment;
int protection,subtype,show;
char *dispstr;
struct Directory *addafter;
UWORD ownerid,groupid;
{
	struct Directory *addposition=NULL,*entry,*workfirst,*newentry;
	int a,b,reverse=0,check=0,sortmethod,endwhile,selsortmethod;
	char *description=NULL,*owner=NULL,*group=NULL,*ptr;

	if (status_iconified && status_flags&STATUS_ISINBUTTONS) return(1);

	if (config->sortflags&(1<<win)) reverse=1;

	selsortmethod=config->sortmethod[win];
	if (selsortmethod<0 || selsortmethod>DISPLAY_LAST) selsortmethod=DISPLAY_NAME;

	if (type==ENTRY_DEVICE) sortmethod=DISPLAY_NAME;
	else if (type>=ENTRY_DIRECTORY) {
		switch (selsortmethod) {
			case DISPLAY_SIZE:
			case DISPLAY_FILETYPE:
				sortmethod=DISPLAY_NAME;
				break;
			case DISPLAY_OWNER:
			case DISPLAY_GROUP:
			case DISPLAY_NETPROT:
				if (!AccountsBase) {
					sortmethod=DISPLAY_NAME;
					break;
				}
			default:
				sortmethod=selsortmethod;
				break;
		}
	}
	else {
		if (!AccountsBase &&
			(selsortmethod==DISPLAY_OWNER ||
			selsortmethod==DISPLAY_GROUP ||
			selsortmethod==DISPLAY_NETPROT))
			sortmethod=DISPLAY_NAME;
		else sortmethod=selsortmethod;
	}

	if (win<0 || !dir || (type!=ENTRY_CUSTOM && protection&128 && config->hiddenbit))
		return(-1);

	if (type<=ENTRY_FILE) {
		if (config->showpat[0] && !(LMatchPatternI(config->showpatparsed,name)))
			return(-1);
		if (config->hidepat[0] && (LMatchPatternI(config->hidepatparsed,name)))
			return(-1);
	}

	if (type==ENTRY_CUSTOM) {
		workfirst=dir->firstentry;
		if (addafter) addposition=addafter;
		else {
			switch (subtype) {
				case CUSTOMENTRY_BUFFERLIST:
					entry=dir->firstentry;
					while (entry) {
						if (inter_sort(comment,entry->comment)<=0) {
							if (entry->last) addposition=entry->last;
							else addposition=(struct Directory *)-1;
							break;
						}
						entry=entry->next;
					}
					break;
			}
		}
	}
	else {
		if (type!=ENTRY_DEVICE) {
			for (a=0;a<DISPLAY_LAST+1;a++) {
				if (config->displaypos[win][a]==DISPLAY_FILETYPE && !description)
					description=getfiledescription(name,win);
				else if (AccountsBase) {
					if (config->displaypos[win][a]==DISPLAY_OWNER && !owner && ownerid) {
						if (dir->last_owner==ownerid) owner=dir->owner_name;
						else if (user_info &&
							!(EnvoyPacket(dir->realdevice,
								ACTION_UID_TO_USERINFO,OLD_ACTION_UID_TO_USERINFO,ownerid,user_info))) {
							owner=user_info->ui_UserName;
							dir->last_owner=ownerid;
							strcpy(dir->owner_name,owner);
						}
					}
					else if (config->displaypos[win][a]==DISPLAY_GROUP && !group && groupid) {
						if (dir->last_group==groupid) group=dir->group_name;
						else if (group_info &&
							!(EnvoyPacket(dir->realdevice,
								ACTION_GID_TO_GROUPINFO,OLD_ACTION_GID_TO_GROUPINFO,groupid,group_info))) {
							group=group_info->gi_GroupName;
							dir->last_group=groupid;
							strcpy(dir->group_name,group);
						}
					}
				}
			}
		}
		workfirst=addposition=NULL;

		if (addafter) {
			if (!(workfirst=addafter->next)) workfirst=addposition=addafter;
		}
		else {
			switch (config->separatemethod[win]) {
				case SEPARATE_DIRSFIRST:
					if (dir->firstfile || dir->firstentry) {
						if (type<=ENTRY_FILE) {
							workfirst=dir->firstfile;
							if (!workfirst) {
								workfirst=dir->firstentry;
								while (workfirst->next) workfirst=workfirst->next;
								addposition=workfirst;
							}
						}
						else workfirst=dir->firstentry;
					}
					check=1;
					break;
				case SEPARATE_FILESFIRST:
					if (dir->firstdir || dir->firstentry) {
						if (type>=ENTRY_DEVICE) {
							workfirst=dir->firstdir;
							if (!workfirst) {
								workfirst=dir->firstentry;
								while (workfirst->next) workfirst=workfirst->next;
								addposition=workfirst;
							}
						}
						else workfirst=dir->firstentry;
					}
					check=1;
					break;
				default:
					workfirst=dir->firstentry;
					break;
			}
		}

		if (workfirst && !addposition) {
			entry=workfirst; endwhile=0;
			while (entry) {
				if (check) {
					switch (config->separatemethod[win]) {
						case SEPARATE_DIRSFIRST:
							if (entry->type<=ENTRY_FILE && type>=ENTRY_DEVICE) {
								if (entry->last) addposition=entry->last;
								else addposition=(struct Directory *)-1;
								endwhile=1;
							}
							break;
						case SEPARATE_FILESFIRST:
							if (type<=ENTRY_FILE && entry->type>=ENTRY_DEVICE) {
								if (entry->last) addposition=entry->last;
								else addposition=(struct Directory *)-1;
								endwhile=1;
							}
							break;
					}
				}
				switch (sortmethod) {
					case -1:
						if (!entry->next) {
							addposition=entry;
							endwhile=2;
						}
						break;

					case DISPLAY_NAME:
sortname:
						if ((reverse && (namesort(name,entry->name)>=0)) ||
							(!reverse && (namesort(name,entry->name)<=0))) endwhile=1;
						break;

					case DISPLAY_SIZE:
						if (size==entry->size) goto sortname;
						if ((reverse && size>=entry->size) || (!reverse && size<=entry->size))
							endwhile=1;
						break;

					case DISPLAY_PROTECT:
						a=(entry->protection&255)^15;
						b=(protection&255)^15;
						if (a==b) goto sortname;
						if ((reverse && b>=a) || (!reverse && b<=a)) endwhile=1;
						break;

					case DISPLAY_DATE:
						if ((a=CompareDate(date,(&entry->date)))==0) goto sortname;
						if ((reverse && a>=0) || (!reverse && a<=0)) endwhile=1;
						break;

					case DISPLAY_COMMENT:
						if (comment && entry->comment)
							a=LStrCmpI(comment,entry->comment);
						else if (comment) a=1;
						else if (entry->comment) a=-1;
						else a=0;
						if (a==0) goto sortname;
						if ((reverse && a>=0) || (!reverse && a<=0)) endwhile=1;
						break;

					case DISPLAY_FILETYPE:
						if (description && entry->description)
							a=LStrCmpI(description,entry->description);
						else if (description) a=1;
						else if (entry->description) a=-1;
						else a=0;
						if (a==0) goto sortname;
						if ((reverse && a>=0) || (!reverse && a<=0)) endwhile=1;
						break;

					case DISPLAY_OWNER:
						if (owner && entry->network && entry->network->owner)
							a=LStrCmpI(owner,entry->network->owner);
						else if (owner) a=1;
						else if (entry->network && entry->network->owner) a=-1;
						else a=0;
						if (a==0) goto sortname;
						if ((reverse && a>=0) || (!reverse && a<=0)) endwhile=1;
						break;

					case DISPLAY_GROUP:
						if (group && entry->network && entry->network->group)
							a=LStrCmpI(group,entry->network->group);
						else if (group) a=1;
						else if (entry->network && entry->network->group) a=-1;
						else a=0;
						if (a==0) goto sortname;
						if ((reverse && a>=0) || (!reverse && a<=0)) endwhile=1;
						break;

					case DISPLAY_NETPROT:
						a=entry->protection&(~255);
						b=protection&(~255);
						if (a==b) goto sortname;
						if ((reverse && b>=a) || (!reverse && b<=a)) endwhile=1;
						break;
				}
				if (endwhile) {
					if (endwhile==1) {
						if (entry->last) addposition=entry->last;
						else addposition=(struct Directory *)-1;
					}
					break;
				}
				entry=entry->next;
			}
		}
		else workfirst=NULL;
	}

	if (!(newentry=LibAllocPooled(dir_memory_pool,sizeof(struct Directory))))
		return(0);
	newentry->last=NULL;
	newentry->next=NULL;
	newentry->comment=NULL;
	newentry->dispstr=NULL;
	newentry->description=NULL;
	newentry->network=NULL;

	if (addposition==(struct Directory *)-1) {	/* Add at head of list */
		newentry->next=workfirst;
		if (workfirst) workfirst->last=newentry;
		switch (config->separatemethod[win]) {
			case SEPARATE_DIRSFIRST:
				if (type<=ENTRY_FILE) dir->firstfile=newentry;
				break;
			case SEPARATE_FILESFIRST:
				if (type>=ENTRY_DEVICE) dir->firstdir=newentry;
				break;
		}
		dir->firstentry=newentry;
	}
	else if (addposition) {	/* Add after this entry */
		if (addposition->next) {
			newentry->next=addposition->next;
			addposition->next->last=newentry;
		}
		newentry->last=addposition;
		addposition->next=newentry;
		if (addposition->type>=ENTRY_DEVICE && type<=ENTRY_FILE)
			dir->firstfile=newentry;
		else if (addposition->type<=ENTRY_FILE && type>=ENTRY_DEVICE)
			dir->firstdir=newentry;
	}
	else {
		if (workfirst) {	/* Add at end of list */
			while (workfirst->next) workfirst=workfirst->next;
			workfirst->next=newentry;
			newentry->last=workfirst;
		}
		else {	/* Starting a new list */
			dir->firstentry=newentry;
			if (type<=ENTRY_FILE) dir->firstfile=newentry;
			else dir->firstdir=newentry;
		}
	}

	if (type==ENTRY_CUSTOM) {
		if (dispstr && dispstr[0]) {
			if (!(newentry->dispstr=LibAllocPooled(dir_memory_pool,strlen(dispstr)+1)))
				return(0);
			strcpy(newentry->dispstr,dispstr);
		}
		lsprintf(newentry->name,"%ld",dir->total);
	}
	else {
		LStrnCpy(newentry->name,name,30);
		if (type!=ENTRY_DEVICE && description &&
			(newentry->description=LibAllocPooled(dir_memory_pool,strlen(description)+1))) {
			strcpy(newentry->description,description);
		}
		if (AccountsBase &&
			(newentry->network=LibAllocPooled(dir_memory_pool,sizeof(struct NetworkStuff)))) {

			if (owner && (newentry->network->owner=LibAllocPooled(dir_memory_pool,strlen(owner)+1)))
				strcpy(newentry->network->owner,owner);

			if (group && (newentry->network->group=LibAllocPooled(dir_memory_pool,strlen(group)+1)))
				strcpy(newentry->network->group,group);

			ptr=newentry->network->net_protbuf;
			for (a=FIBB_GRP_READ,b=0;a>=FIBB_GRP_DELETE;a--,b++) {
				if (!(protection&(1<<a))) *ptr++=globstring[STR_PROTBIT_RWED][0];
				else *ptr++=globstring[STR_PROTBIT_RWED][b+4];
			}
			*ptr++=' ';
			for (a=FIBB_OTR_READ,b=0;a>=FIBB_OTR_DELETE;a--,b++) {
				if (!(protection&(1<<a))) *ptr++=globstring[STR_PROTBIT_RWED][0];
				else *ptr++=globstring[STR_PROTBIT_RWED][b+4];
			}
		}
	}
	newentry->size=size;
	newentry->owner_id=ownerid;
	newentry->group_id=groupid;

	if ((newentry->type=type)==ENTRY_DEVICE) {
		switch (size) {
			case DLT_DEVICE: comment="<DEV>"; break;
			case DLT_DIRECTORY: comment="<ASN>"; break;
			case DLT_VOLUME: comment="<VOL>"; break;
			case DLT_LATE: comment="<DFR>"; break;
			case DLT_NONBINDING: comment="<NBD>"; break;
		}
	}
	if (date) {
		newentry->date.ds_Days=date->ds_Days;
		newentry->date.ds_Minute=date->ds_Minute;
		newentry->date.ds_Tick=date->ds_Tick;
	}

	if (comment && comment[0]) {
		if ((newentry->comment=LibAllocPooled(dir_memory_pool,strlen(comment)+1)))
			strcpy(newentry->comment,comment);
		else if (type==ENTRY_CUSTOM) return(0);
	}

	newentry->protection=protection;
	if (type!=ENTRY_CUSTOM) getprot(protection,newentry->protbuf);

	if (date) seedate(date,newentry->datebuf,1);
	newentry->subtype=subtype;
	newentry->selected=FALSE;
	switch (ENTRYTYPE(type)) {
		case ENTRY_FILE:
			++dir->filetot;
			dir->bytestot+=size;
			break;
		case ENTRY_DIRECTORY:
			if (size>-1) dir->bytestot+=size;
		case ENTRY_DEVICE:
			++dir->dirtot;
			break;
	}
	++dir->total;

	if (newentry->next) {
		if (newentry->name[0] && (LStrCmpI(newentry->name,newentry->next->name))==0)
			removefile(newentry->next,dir,win,0);
		else if (type==ENTRY_CUSTOM && subtype==CUSTOMENTRY_BUFFERLIST &&
			(LStrCmpI(newentry->comment,newentry->next->comment))==0)
			removefile(newentry->next,dir,win,0);
	}
	if (show && type!=ENTRY_DEVICE) {
		refreshwindow(win,1);
		doposprop(win);
	}
	return((int)newentry);
}

namesort(str1,str2)
char *str1,*str2;
{
	int n1,n2;

	if (isdigit(str1[0]) && isdigit(str2[0])) {
		n1=atoi(str1); n2=atoi(str2);
		if (n1!=n2) return((n1-n2));
		str1=getstrafternum(str1);
		str2=getstrafternum(str2);
	}
	return(inter_sort(str1,str2));
}

inter_sort(str1,str2)
char *str1,*str2;
{
	if (UtilityBase) return(Stricmp(str1,str2));
	return(LStrCmpI(str1,str2));
}

char *getstrafternum(str)
char *str;
{
	char *ptr=str;

	while (*ptr) {
		if (!isdigit(*ptr)) break;
		++ptr;
	}
	return(ptr);
}

void removefile(file,dir,win,show)
struct Directory *file;
struct DirectoryWindow *dir;
int win,show;
{
	int ty=file->type,sz=file->size,se=file->selected;

	if (win<0 || !dir || (status_iconified && status_flags&STATUS_ISINBUTTONS)) return;

	if (file==last_selected_entry) last_selected_entry=NULL;

	if (file==dir->firstfile) {
		if (file->next) {
			dir->firstfile=file->next;
			if (file->last) dir->firstfile->last=file->last;
			else dir->firstfile->last=NULL;
		}
		else dir->firstfile=NULL;
	}
	else if (file==dir->firstdir) {
		if (file->next) {
			dir->firstdir=file->next;
			if (file->last) dir->firstdir->last=file->last;
			else dir->firstdir->last=NULL;
		}
		else dir->firstdir=NULL;
	}
	if (file==dir->firstentry) {
		if (file->next) {
			dir->firstentry=file->next;
			if (dir->firstentry->next) dir->firstentry->next->last=dir->firstentry;
			dir->firstentry->last=NULL;
		}
		else dir->firstentry=NULL;
	}
	else {
		if (file->next) {
			file->last->next=file->next;
			file->next->last=file->last;
		}
		else file->last->next=NULL;
	}
	if (func_single_entry && func_single_entry==file) func_entry_deleted=1;
	free_file_memory(file);
	--dir->total;
	if (ty<0) {
		--dir->filetot; dir->bytestot-=sz;
		if (se) {
			--dir->filesel;
			dir->bytessel-=sz;
		}
	}
	else {
		--dir->dirtot;
		if (ty>0 && sz>0) dir->bytestot-=sz;
		if (se) {
			--dir->dirsel;
			if (ty>0 && sz>0) dir->bytessel-=sz;
		}
	}
	if (dir->offset>(dir->total-scrdata_dispwin_lines))
		dir->offset=dir->total-scrdata_dispwin_lines;
	if (dir->offset<0) dir->offset=0;
	if (show) refreshwindow(win,1);
}

dorun(name,state,workbench)
char *name;
int state,workbench;
{
	char argbuf[256],louise[80],buf[256];
	int rec,len;
	struct dopusfuncpar par;

	argbuf[0]=0;
	if (workbench) {
		buf[0]=FC_WORKBENCH;
		buf[1]=0;
	}
	else buf[0]=0;
	StrConcat(buf,"\"",256);
	StrConcat(buf,name,256);
	StrConcat(buf,"\"",256);

	len=256-strlen(buf);
	if (state) {
		lsprintf(louise,globstring[STR_ENTER_ARGUMENTS_FOR],BaseName(name));
		if (!(rec=whatsit(louise,len,argbuf,globstring[STR_SKIP]))) {
			myabort();
			return(-1);
		}
		if (rec==2) return(-2);
	}
	else rec=1;
	if (!rec) {
		myabort();
		return(0);
	}
	if (argbuf[0]) {
		StrConcat(buf," ",256);
		StrConcat(buf,argbuf,256);
	}
	defaultpar(&par);
	dofunctionstring(buf,NULL,BaseName(name),&par);
	return(1);
}

void busy()
{
	if (!(status_flags&STATUS_BUSY)) {
		size_gadgets[0].GadgetType=GTYP_BOOLGADGET;
		size_gadgets[1].GadgetType=GTYP_BOOLGADGET;
		if (!Window->FirstRequest) {
			InitRequester(&dopus_busy_requester);
			dopus_busy_requester.Flags=NOISYREQ;
			Request(&dopus_busy_requester,Window);
		}
		endnotifies();
		status_flags|=STATUS_BUSY;
	}
	SetBusyPointer(Window);
}

void unbusy()
{
	if (status_flags&STATUS_BUSY) {
		struct Message *msg;

		if (Window->FirstRequest) EndRequest(&dopus_busy_requester,Window);
		flushidcmp();
		while (msg=GetMsg(count_port)) ReplyMsg(msg);
		startnotifies();
		size_gadgets[0].GadgetType=GTYP_SIZING;
		size_gadgets[1].GadgetType=GTYP_SIZING;
		status_flags&=~STATUS_BUSY;
	}
	ClearPointer(Window);
}

void flushidcmp()
{
	while (getintuimsg()) ReplyMsg((struct Message *)IMsg);
}

void setnullpointer(wind)
struct Window *wind;
{
	SetPointer(wind,null_pointer,1,16,0,0);
}

void free_file_memory(file)
struct Directory *file;
{
	if (file) {
		if (file->comment)
			LibFreePooled(dir_memory_pool,file->comment,strlen(file->comment)+1);

		if (file->type==ENTRY_CUSTOM && file->dispstr)
			LibFreePooled(dir_memory_pool,file->dispstr,strlen(file->dispstr)+1);

		if (file->description)
			LibFreePooled(dir_memory_pool,file->description,strlen(file->description)+1);

		if (file->network) {
			if (file->network->owner)
				LibFreePooled(dir_memory_pool,file->network->owner,strlen(file->network->owner)+1);

			if (file->network->group)
				LibFreePooled(dir_memory_pool,file->network->group,strlen(file->network->group)+1);

			LibFreePooled(dir_memory_pool,file->network,sizeof(struct NetworkStuff));
		}

		LibFreePooled(dir_memory_pool,file,sizeof(struct Directory));
	}
}
