/*

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 "diskop.h"

void main(argc,argv)
int argc;
char *argv[];
{
	struct DOpusRemember *memkey=NULL;
	struct VisInfo *vis;
	char **dummy_args,*port,*opbuf,*stringname;
	struct StringData *stringdata;
	int arg;

	if (!(DOpusBase=(struct DOpusBase *)OpenLibrary("dopus.library",18)))
#ifdef __SASC_60
		__exit(0);
#else
		_exit(0);
#endif

	if ((vis=LAllocRemember(&memkey,sizeof(struct VisInfo),MEMF_CLEAR)) &&
		(dummy_args=LAllocRemember(&memkey,sizeof(char *)*16,MEMF_CLEAR)) &&
		(opbuf=LAllocRemember(&memkey,80,MEMF_CLEAR)) &&
		(stringname=LAllocRemember(&memkey,80,MEMF_CLEAR)) &&
		(stringdata=LAllocRemember(&memkey,sizeof(struct StringData),MEMF_CLEAR))) {

		IntuitionBase=DOpusBase->IntuitionBase;
		GfxBase=DOpusBase->GfxBase;
		IconBase=OpenLibrary("icon.library",0);

		if (argc==0) {
			struct WBStartup *startup;
			int arg;

			startup=(struct WBStartup *)argv;
			for (arg=0;arg<startup->sm_NumArgs && arg<16;arg++) {
				dummy_args[arg]=startup->sm_ArgList[arg].wa_Name;
				++argc;
			}
			argv=dummy_args;

			if (IconBase) {
				struct DiskObject *dobj;
				char *str;

				if (dobj=GetDiskObject(argv[0])) {
					if (str=FindToolType(dobj->do_ToolTypes,"OPERATION")) {
						LStrnCpy(opbuf,str,80);
						argv[1]=opbuf;
						if (argc<2) argc=2;
					}
					FreeDiskObject(dobj);
				}
			}
		}

		if (argc>1) {
			if (argc>2 && argv[2][0]=='&') {
				arg=3;
				port=&argv[2][1];
			}
			else {
				arg=2;
				port=NULL;
			}
			get_vis_info(vis,port);

			stringdata->default_table=default_strings;
			stringdata->string_count=STR_STRING_COUNT;
			stringdata->min_version=STRING_VERSION;

			if (vis->vi_language)
				lsprintf(stringname,"DOpus:Modules/S/DM_Disk_%s.STR",vis->vi_language);
			else stringname[0]=0;

			if (ReadStringFile(stringdata,stringname)) {
				string_table=stringdata->string_table;

				switch (argv[1][0]) {
					case 'f':
					case 'F':
						diskop_format(vis,port,argc-arg,&argv[arg]);
						break;
					case 'd':
					case 'D':
						diskop_diskcopy(vis,port,argc-arg,&argv[arg]);
						break;
					case 'i':
					case 'I':
						diskop_install(vis,argc-arg,&argv[arg]);
						break;
				}
			}
			FreeStringFile(stringdata);
		}

		if (IconBase) CloseLibrary(IconBase);
	}

	LFreeRemember(&memkey);

	CloseLibrary((struct Library *)DOpusBase);
#ifdef __SASC_60
	__exit(0);
#else
	_exit(0);
#endif
}

void get_vis_info(vis,portname)
struct VisInfo *vis;
char *portname;
{
	vis->vi_fg=1; vis->vi_bg=0;
	vis->vi_shine=2; vis->vi_shadow=1;
	vis->vi_font=NULL;
	vis->vi_screen=NULL;
	vis->vi_stringcol[0]=1; vis->vi_stringcol[1]=0;
	vis->vi_activestringcol[0]=1; vis->vi_activestringcol[1]=0;
	vis->vi_flags=VISF_WINDOW;
	vis->vi_language=NULL;

	if (dopus_message(DOPUSMSG_GETVIS,(APTR)vis,portname)) return;

	if (IntuitionBase->LibNode.lib_Version>35) {
		struct DrawInfo *drinfo;
		struct Screen *pub;

		if (pub=LockPubScreen(NULL)) {
			drinfo=GetScreenDrawInfo(pub);
			vis->vi_shine=drinfo->dri_Pens[SHINEPEN];
			vis->vi_shadow=drinfo->dri_Pens[SHADOWPEN];
			vis->vi_fg=drinfo->dri_Pens[TEXTPEN];
			vis->vi_bg=drinfo->dri_Pens[BACKGROUNDPEN];
			FreeScreenDrawInfo(pub,drinfo);
			UnlockPubScreen(NULL,pub);
		}
	}
}

dopus_message(cmd,data,portname)
int cmd;
APTR data;
char *portname;
{
	struct MsgPort *port,*replyport;
	struct DOpusMessage msg;

	Forbid();
	if (portname && portname[0] &&
		(port=FindPort(portname)) &&
		(replyport=LCreatePort(NULL,0))) {
		msg.msg.mn_Node.ln_Type=NT_MESSAGE;
		msg.msg.mn_Node.ln_Name=NULL;
		msg.msg.mn_ReplyPort=replyport;
		msg.msg.mn_Length=(UWORD)sizeof(struct DOpusMessage);
		msg.command=cmd;
		msg.data=data;
		PutMsg(port,(struct Message *)&msg);
		Permit();
		WaitPort(replyport);
		GetMsg(replyport);
		LDeletePort(replyport);
		return(1);
	}
	Permit();
	return(0);
}

void fill_out_req(req,vis)
struct RequesterBase *req;
struct VisInfo *vis;
{
	req->rb_fg=vis->vi_fg;
	req->rb_bg=vis->vi_bg;
	req->rb_shine=vis->vi_shine;
	req->rb_shadow=vis->vi_shadow;
	req->rb_font=vis->vi_font;
	req->rb_flags&=~RBF_WINDOWCENTER;
	if (vis->vi_screen) {
		if (vis->vi_flags&VISF_WINDOW) {
			req->rb_screen=((struct Window *)vis->vi_screen)->WScreen;
			req->rb_window=(struct Window *)vis->vi_screen;
			req->rb_flags|=RBF_WINDOWCENTER;
		}
		else req->rb_screen=vis->vi_screen;
	}
	else req->rb_screen=NULL;
}

struct Gadget *addreqgadgets(reqbase,gadgets,mask,count)
struct RequesterBase *reqbase;
struct TagItem **gadgets;
int mask,*count;
{
	int gad;
	struct Gadget *gadget=NULL,*newgadget,*firstgadget;

	for (gad=0;;gad++) {
		if (!gadgets[gad]) break;
		if (!(mask&(1<<gad))) {
			if (!(newgadget=(struct Gadget *)
				AddRequesterObject(reqbase,gadgets[gad]))) return(NULL);
			if (gadget) gadget->NextGadget=newgadget;
			else firstgadget=newgadget;
			gadget=newgadget;
			++*count;
		}
	}
	AddGadgets(reqbase->rb_window,firstgadget,NULL,gad,reqbase->rb_shine,reqbase->rb_shadow,1);
	return(firstgadget);
}

int error_rets[]={1,0};

check_error(reqbase,str,gadtxt)
struct RequesterBase *reqbase;
char *str;
int gadtxt;
{
	struct DOpusSimpleRequest req;
	char *error_gads[3];

	req.text=str;
	error_gads[0]=string_table[gadtxt];
	error_gads[1]=string_table[STR_CANCEL];
	error_gads[2]=NULL;
	req.gads=error_gads;
	req.rets=error_rets;
	req.hi=reqbase->rb_shine;
	req.lo=reqbase->rb_shadow;
	req.fg=reqbase->rb_fg;
	req.bg=reqbase->rb_bg;
	req.strbuf=NULL;
	req.flags=SRF_RECESSHI;
	if (reqbase->rb_flags&RBF_BORDERS) req.flags|=SRF_BORDERS;
	req.font=reqbase->rb_font;
	req.title="DOpus_Disk";
	return(DoSimpleRequest(reqbase->rb_window,&req));
}

ULONG do_checksum(sector)
ULONG *sector;
{
	ULONG sum;
	int i;

	for (sum=0,i=0;i<(TD_SECTOR>>2);i++) sum+=sector[i];
	return(sum);
}

do_writeblock(device_req,buffer,offset)
struct IOExtTD *device_req;
APTR buffer;
ULONG offset;
{
	device_req->iotd_Req.io_Command=CMD_WRITE;
	device_req->iotd_Req.io_Data=buffer;
	device_req->iotd_Req.io_Length=TD_SECTOR;
	device_req->iotd_Req.io_Offset=offset*TD_SECTOR;
	return((int)DoIO((struct IORequest *)device_req));
}

void inhibit_drive(device,state)
char *device;
ULONG state;
{
	struct MsgPort *handler;

	if (DOSBase->dl_lib.lib_Version<36) {
		if (handler=(struct MsgPort *)DeviceProc(device))
			SendPacket(handler,ACTION_INHIBIT,&state,1);
	}
	else Inhibit(device,state);
}

void border_text(reqbase,border,infobuf)
struct RequesterBase *reqbase;
Object_Border *border;
char *infobuf;
{
	struct RastPort *rp;

	rp=reqbase->rb_window->RPort;
	SetAPen(rp,reqbase->rb_bg);
	RectFill(rp,border->ob_left,border->ob_top,
		border->ob_left+border->ob_width-1,border->ob_top+border->ob_height-1);
	SetAPen(rp,reqbase->rb_fg);

	if (infobuf) {
		ObjectText(reqbase,
			border->ob_left,
			border->ob_top,
			border->ob_width,
			border->ob_height,
			infobuf,
			TEXTPOS_CENTER|TEXTPOS_F_NOUSCORE);
	}
}

struct DeviceNode *find_device(name)
char *name;
{
	struct RootNode *rootnode;
	struct DosInfo *dosinfo;
	struct DeviceNode *devnode;
	char matchname[32],devname[32];
	int a;

	if (!name) return(NULL);

	for (a=0;name[a];a++) {
		if (name[a]==':') break;
		matchname[a]=name[a];
	}
	matchname[a]=0;

	Forbid();

	rootnode=(struct RootNode *) DOSBase->dl_Root;
	dosinfo=(struct DosInfo *) BADDR(rootnode->rn_Info);
	devnode=(struct DeviceNode *) BADDR(dosinfo->di_DevInfo);

	while (devnode) {
		if (devnode->dn_Type==DLT_DEVICE && devnode->dn_Task) {
			BtoCStr((BPTR)devnode->dn_Name,devname,32);
			if (strcmp(devname,matchname)==0) {
				Permit();
				return(devnode);
			}
		}
		devnode=(struct DeviceNode *) BADDR(devnode->dn_Next);
	}

	Permit();
	return(NULL);
}

char **get_device_list(key,alike)
struct DOpusRemember **key;
char *alike;
{
	struct RootNode *rootnode;
	struct DosInfo *dosinfo;
	struct DeviceNode *devnode,*alikenode=NULL;
	int count=1;
	char **listtable,devname[32];

	if (alike) alikenode=find_device(alike);

	Forbid();

	rootnode=(struct RootNode *) DOSBase->dl_Root;
	dosinfo=(struct DosInfo *) BADDR(rootnode->rn_Info);
	devnode=(struct DeviceNode *) BADDR(dosinfo->di_DevInfo);

	while (devnode) {
		if (devnode->dn_Type==DLT_DEVICE && devnode->dn_Task &&
			devnode->dn_Startup>512) {
			if (!alikenode || like_devices(devnode,alikenode))
				++count;
		}
		devnode=(struct DeviceNode *) BADDR(devnode->dn_Next);
	}

	if (listtable=LAllocRemember(key,count*4,MEMF_CLEAR)) {
		devnode=(struct DeviceNode *) BADDR(dosinfo->di_DevInfo);
		count=0;
		while (devnode) {
			if (devnode->dn_Type==DLT_DEVICE && devnode->dn_Task &&
				devnode->dn_Startup>512) {
				if (!alikenode || like_devices(devnode,alikenode)) {
					BtoCStr((BPTR)devnode->dn_Name,devname,32);
					strcat(devname,":");
					if (listtable[count]=LAllocRemember(key,strlen(devname)+1,0))
						strcpy(listtable[count],devname);
					++count;
				}
			}
			devnode=(struct DeviceNode *) BADDR(devnode->dn_Next);
		}
		sort_device_list(listtable);
	}

	Permit();
	return(listtable);
}

void sort_device_list(table)
char **table;
{
	int count,gap,i,j;
	char *temp;

	for (count=0;table[count];count++);

	for (gap=count/2;gap>0;gap/=2)
		for (i=gap;i<count;i++)
			for (j=i-gap;j>=0;j-=gap) {
				if (LStrCmpI(table[j],table[j+gap])<=0) break;
				temp=table[j];
				table[j]=table[j+gap];
				table[j+gap]=temp;
			}
}

check_disk(reqbase,device_req,name,prot)
struct RequesterBase *reqbase;
struct IOExtTD *device_req;
char *name;
int prot;
{
	char errorbuf[80];

	FOREVER {
		device_req->iotd_Req.io_Command=TD_CHANGESTATE;
		DoIO((struct IORequest *)device_req);
		if (device_req->iotd_Req.io_Actual) {
			lsprintf(errorbuf,string_table[STR_NODISKPRESENT],name);
			if (!(check_error(reqbase,errorbuf,STR_RETRY))) return(0);
		}
		else break;
	}
	if (prot) {
		FOREVER {
			device_req->iotd_Req.io_Command=TD_PROTSTATUS;
			DoIO((struct IORequest *)device_req);
			if (device_req->iotd_Req.io_Actual) {
				lsprintf(errorbuf,string_table[STR_DISKWRITEPROTECTED],name);
				if (!(check_error(reqbase,errorbuf,STR_RETRY))) return(0);
			}
			else break;
		}
	}
	return(1);
}

check_abort(window)
struct Window *window;
{
	struct IntuiMessage *msg;
	int abort=0;

	while (msg=(struct IntuiMessage *)GetMsg(window->UserPort)) {
		if (msg->Class==IDCMP_MOUSEBUTTONS && msg->Code==MENUDOWN)
			abort=1;
		ReplyMsg((struct Message *)msg);
	}
	return(abort);
}

check_blank_disk(reqbase,device,action)
struct RequesterBase *reqbase;
char *device,*action;
{
	BPTR lock;
	struct InfoData __aligned info;
	struct FileInfoBlock __aligned fib;
	struct Process *myproc;
	APTR wsave;
	char blank=0,*message,diskname[40],sizebuf[20];
	int ret=1;

	if (!(message=AllocMem(400,MEMF_CLEAR))) return(1);

	myproc=(struct Process *)FindTask(NULL);
	wsave=myproc->pr_WindowPtr;
	myproc->pr_WindowPtr=(APTR)-1;

	if (lock=Lock(device,ACCESS_READ)) {
		Info(lock,&info);
		switch (info.id_DiskType) {
			case ID_UNREADABLE_DISK:
			case ID_NOT_REALLY_DOS:
				blank=1;
				break;
			default:
				Examine(lock,&fib);
				strcpy(diskname,fib.fib_FileName);
				if (!(ExNext(lock,&fib))) blank=1;
				break;
		}
		UnLock(lock);
	}
	else blank=1;

	myproc->pr_WindowPtr=wsave;

	if (!blank && action) {
		getsizestring(sizebuf,info.id_NumBlocksUsed*info.id_BytesPerBlock);

		lsprintf(message,string_table[STR_DISK_NOT_BLANK],
			device,diskname,sizebuf,action);

		if (!(check_error(reqbase,message,STR_PROCEED))) ret=0;
	}
	FreeMem(message,400);
	return(ret);
}

void set_env(action,gadget,count,list)
char *action;
struct Gadget *gadget;
int count;
struct DOpusListView *list;
{
	int file,listid='LIST';
	UWORD len;
	char envname[80],null=0;

	lsprintf(envname,"ENV:DOpus_%s.prefs",action);
	if (!(file=Open(envname,MODE_NEWFILE))) return;

	while (gadget && count--) {
		if (gadget->Activation&GACT_TOGGLESELECT) {
			Write(file,&gadget->GadgetID,sizeof(UWORD));
			Write(file,&gadget->GadgetType,sizeof(UWORD));
			switch (gadget->GadgetType) {
				case GTYP_BOOLGADGET:
					Write(file,&gadget->Flags,sizeof(UWORD));
					break;
				case GTYP_STRGADGET:
					len=strlen(((struct StringInfo *)gadget->SpecialInfo)->Buffer)+1;
					Write(file,&len,sizeof(UWORD));
					Write(file,((struct StringInfo *)gadget->SpecialInfo)->Buffer,len);
					if (len%2) Write(file,&null,1);
					break;
			}
		}
		else ++count;
		gadget=gadget->NextGadget;
	}
	while (list) {
		Write(file,&listid,sizeof(ULONG));
		Write(file,list->items[list->itemselected],
			(len=(strlen(list->items[list->itemselected])+1)));
		if (len%2) Write(file,&null,1);
		list=list->next;
	}
	Close(file);
}

void get_env(action,firstgadget,count,list)
char *action;
struct Gadget *firstgadget;
int count;
struct DOpusListView *list;
{
	int file,size,a,b,*lbuf;
	char envname[80],*nptr;
	struct Gadget *gadget;
	UWORD gadgettype,gadgetid,len,*buf;

	lsprintf(envname,"ENV:DOpus_%s.prefs",action);
	if (!(file=Open(envname,MODE_OLDFILE))) return;

	Seek(file,0,OFFSET_END);
	size=Seek(file,0,OFFSET_BEGINNING);

	if (!(buf=(UWORD *)AllocMem(size,MEMF_CLEAR))) {
		Close(file);
		return;
	}

	Read(file,(char *)buf,size);
	Close(file);

	for (a=0;a<size/2;) {
		lbuf=(int *)&buf[a];
		if (lbuf[0]=='LIST') {
			a+=2;
			nptr=(char *)&buf[a];
			if (list) {
				for (b=0;list->items[b];b++) {
					if (LStrCmpI(nptr,list->items[b])==0) {
						list->itemselected=b;
						break;
					}
				}
				list=list->next;
			}
			b=strlen(nptr)+1;
			if (b%2) ++b;
			a+=b/2;
			continue;
		}
		gadgetid=buf[a++];
		gadgettype=buf[a++];
		gadget=firstgadget;
		while (gadget) {
			if (gadget->GadgetID==gadgetid && gadget->GadgetType==gadgettype)
				break;
			gadget=gadget->NextGadget;
		}
		if (!count--) gadget=NULL;
		switch (gadgettype) {
			case GTYP_BOOLGADGET:
				if (gadget) gadget->Flags=buf[a];
				++a;
				break;
			case GTYP_STRGADGET:
				len=buf[a++];
				if (gadget) strcpy(((struct StringInfo *)gadget->SpecialInfo)->Buffer,(char *)&buf[a]);
				if (len%2) ++len;
				a+=len/2;
				break;
		}
	}
	FreeMem(buf,size);
}

void fix_listview(reqbase,list)
struct RequesterBase *reqbase;
struct DOpusListView *list;
{
	list->window=reqbase->rb_window;
	list->flags|=DLVF_LEAVE|DLVF_SLOW;
	list->sliderwidth=8;
	list->slidercol=reqbase->rb_fg;
	list->sliderbgcol=reqbase->rb_bg;
	list->textcol=reqbase->rb_fg;
	list->boxhi=reqbase->rb_shine;
	list->boxlo=reqbase->rb_shadow;
	list->arrowfg=reqbase->rb_fg;
	list->arrowbg=reqbase->rb_bg;
	list->itemfg=reqbase->rb_fg;
	list->itembg=reqbase->rb_bg;
}

void select_device(list,exclude)
struct DOpusListView *list;
char *exclude;
{
	int def=-1,item;

	for (item=0;list->items[item];item++) {
		if (!exclude || LStrCmpI(list->items[item],exclude)) {
			if (list->items[item][0]=='D' &&
				list->items[item][1]=='F' &&
				list->items[item][3]==':') {
				def=item;
				break;
			}
			if (def==-1) def=item;
		}
	}
	if (def==-1) def=0;
	list->itemselected=def;
	list->topitem=def;
}

like_devices(node,likenode)
struct DeviceNode *node,*likenode;
{
	struct DosEnvec *envec,*likeenvec;
	struct FileSysStartupMsg *startup,*likestartup;

	startup=(struct FileSysStartupMsg *)BADDR(node->dn_Startup);
	likestartup=(struct FileSysStartupMsg *)BADDR(likenode->dn_Startup);
	envec=(struct DosEnvec *)BADDR(startup->fssm_Environ);
	likeenvec=(struct DosEnvec *)BADDR(likestartup->fssm_Environ);

	if (envec->de_SizeBlock!=likeenvec->de_SizeBlock ||
		envec->de_Surfaces!=likeenvec->de_Surfaces ||
		envec->de_BlocksPerTrack!=likeenvec->de_BlocksPerTrack ||
		((envec->de_HighCyl-envec->de_LowCyl))!=
			((likeenvec->de_HighCyl-likeenvec->de_LowCyl)))
		return(0);
	return(1);
}

open_device(device,handle)
char *device;
struct DeviceHandle *handle;
{
	struct DeviceNode *devnode;
	char devicename[40];

	handle->startup=NULL;
	handle->dosenvec=NULL;
	handle->device_port=NULL;
	handle->device_req=NULL;

	if (!device || !(devnode=find_device(device))) return(0);

	handle->startup=(struct FileSysStartupMsg *)BADDR(devnode->dn_Startup);
	handle->dosenvec=(struct DosEnvec *)BADDR(handle->startup->fssm_Environ);
	BtoCStr((BPTR)handle->startup->fssm_Device,devicename,40);

	if (!(handle->device_port=LCreatePort(NULL,0))) return(0);
	if (handle->device_req=(struct IOExtTD *)
		LCreateExtIO(handle->device_port,sizeof(struct IOExtTD))) {
		if (!(OpenDevice(devicename,handle->startup->fssm_Unit,
			(struct IORequest *)handle->device_req,handle->startup->fssm_Flags)))
			return(1);
		LDeleteExtIO((struct IORequest *)handle->device_req);
		handle->device_req=NULL;
	}
	return(0);
}

void close_device(handle)
struct DeviceHandle *handle;
{
	if (handle->device_req) {
		CloseDevice((struct IORequest *)handle->device_req);
		LDeleteExtIO((struct IORequest *)handle->device_req);
		handle->device_req=NULL;
	}
	if (handle->device_port) {
		LDeletePort(handle->device_port);
		handle->device_port=NULL;
	}
}

void drive_motor(req,state)
struct IOExtTD *req;
int state;
{
	req->iotd_Req.io_Command=TD_MOTOR;
	req->iotd_Req.io_Length=state;
	DoIO((struct IORequest *)req);
}

void show_sel_item(list)
struct DOpusListView *list;
{
	while (list) {
		if (list->itemselected<list->topitem ||
			list->itemselected>(list->topitem+list->lines-1)) {
			list->topitem=list->itemselected;
			RefreshListView(list,1);
		}
		list=list->next;
	}
}
