*************************
*   HappyENV-Handler	*
*     1.0 05.06.97	*
*  (C) Martin Gierich	*
*************************

* MM's fixes:

* 1.0a
*
* - fixed ACTION_SET_FILE_SIZE packet
* - fixed ACTION_SAME_LOCK packet (circular entry error)
* - fixed NULL lock (ENVARC: assigned to root)
* - added ROMTAG structure
* - support for KS 1.3 removed
* - startup parms for resident operation are hardcoded into data section
* - workaround for some dependencies in notification support; noti is a real
*   mess in AmigaOS

* 1.3
*
* - added support for date
* - fixed ACTION_COPY_DIR with ZERO lock
* - removed hidden files mechanism
* - increased max path to 255 chars
* - cleaned up startup/die routines
* - fixed nasty bug in CalcFullName routine
* - fixed assigns
* - added protection bits (lowest byte only)


* Do not use this source code or parts of it in other programs !
* See documentation about Copyright&Disclaimer.
* Sorry about the not so well commented source.


* Switches for options, enabled if label is defined,
* disabled if semicolon is put in front of it.

;DEBUG		= 1	;allow debugging output via sushi (RawPutChar)
;VERBOSE	= 1	;verbose debug output
;EXPAND		= 1

;test=1	set for test, non-public versions
;rom=1	set if build for 3.x-like ROM image (TaggedOpen)

	mc68000

fdate	macro
	dc.b	'27.6.2002'
	endm
$VER	macro
	dc.b	'1.3'
	endm

	include lvos
	INCLUDE	dos/dosextens.i
	INCLUDE	dos/dostags.i
	include	exec/memory.i
	include	exec/tasks.i
	include	exec/ports.i
	include	exec/semaphores.i
	include	exec/resident.i
	INCLUDE	dos/filehandler.i
	INCLUDE	dos/notify.i
	INCLUDE	dos/rdargs.i
	ifd	DEBUG
	INCLUDE DH1:Actual/Trashcan/HappyENV/Source/debug.i
	endc

* other macros
* compare both operands and store minimum in the right one
* third argument is label name

MIN	MACRO
	cmp.l	\2,\1
	bhs.s	.\3
	move.l	\1,\2
.\3
	ENDM

* miscellaneous definitions

LF		= 10
MAXNAME		= 36	(32 + terminating 0 + longword alignment)
MAXPATH		= 256-MAXNAME
BLOCKSIZE	= 5000

	IFND	DEBUG
DBUG5	MACRO
	ENDM
DBUG4	MACRO
	ENDM
DBUG3	MACRO
	ENDM
DBUG2	MACRO
	ENDM
DBUG1	MACRO
	ENDM
DBUG0	MACRO
	ENDM
	ENDC

* EasyRequest structure (Intuition)
		RSRESET
es_StructSize	RS.L	1
es_Flags	RS.L	1
es_Title	RS.L	1
es_TextFormat	RS.L	1
es_GadgetFormat	RS.L	1
es_SIZEOF	RS.W	0


* notify list node structure
		RSRESET
myn_Link	RS.L	1
myn_Request	RS.L	1	;notify-request
myn_Key		RS.L	1	;pointer to file
myn_Name	RS.B	0	;path/file name (dynamic)

* lock structure for Lock() and open files

* when changing anything here, remember to fix CreateLock! -MM

		RSRESET
myl_Link	RS.L	1
myl_Key		RS.L	1
myl_Access	RS.L	1
myl_Task	RS.L	1
myl_Volume	RS.L	1
myl_Pos		RS.L	1	;position in file (read/write)
myl_Data	RS.L	1	;start of file data (read)
myl_Start	RS.L	1	;start of block list (write)
myl_Block	RS.L	1	;current block (write)
myl_BlockPos	RS.L	1	;position in current block (write)
myl_Size	RS.L	1	;size of file
myl_Mode	RS.B	1	;0=simple lock, -1=write, 1=read
myl_Pad		RS.B	1
myl_SIZEOF	RS.W	0

* structure for file or dir
		RSRESET
myf_Link	RS.L	1	;next file/dir
myf_Parent	RS.L	1	;parent dir (root: 0)
myf_First	RS.L	0	;dir:  points to first entry
myf_Size	RS.L	1	;file: size of filedata
myf_Date	rs.l	3	;datestamp
myf_Attrs	rs.b	1	;attributes
myf_Type	RS.B	1	;bit7: dir/file, [6:hidden (removed -MM)],
				;5:copied, 4:notify, 3:modified
myf_Locks	RS.B	1	;number of locks, -1=write lock
myf_DataOffs	RS.B	1	;offset to start of data
myf_Name	RS.B	0	;name (dynamic), then data

* private main structure
		RSRESET
CharTable	RS.B	256
ExecBase	rs.l	1
DosBase		RS.L	1
IntuiBase	RS.L	1
MyProcess	RS.L	1
MyMsgPort	RS.L	1
MyMemPool	RS.L	1
MyDeviceNode	RS.L	1
MyVolumeBPTR	RS.L	1
NotifyList	RS.L	1
;Copyfrom	RS.L	1
;CopyfromLock	RS.L	1
;CopyfromPort	RS.L	1
;EnvarcLock	rs.l	1
RootLock	RS.L	1
NotifyPort	RS.l	1
PacketPort	RS.l	1
MyPacket	RS.B	sp_SIZEOF	;68
VolumeNode	RS.B	DevList_SIZEOF	;44
FileSize	RS.L	1
TotalSize	RS.L	1
NumLocks	RS.L	1

ReqStruct	RS.B	es_SIZEOF	;20
FormatArgs	RS.L	2

*------- DO NOT REARRANGE -----------
StupVolname	RS.L	1
StupIcon	RS.l	1
StupNocopy	RS.l	1
StupNoreq	RS.l	1
StupExpand	RS.l	1
*------- DO NOT REARRANGE -----------

DosPort		rs.l	1
MyMsg		rs.b	MN_SIZE+8	;28
MyFIB		rs.b	fib_SIZEOF	;260
MyFH		rs.b	fh_SIZEOF	;44
MyML		rs.b	MLH_SIZE	;12

RootDir		RS.B	myf_Name+MAXNAME	;28+36=64

*------- DO NOT REARRANGE -----------
TempPath	RS.B	MAXPATH		;256-36
TempName	RS.B	MAXNAME		;36
*------- DO NOT REARRANGE -----------

VolumeName	RS.B	MAXNAME		;36

WriteProtected	RS.B	1
My_SIZEOF	RS.L	0

MyPkt		= MyPacket+sp_Pkt

*******************************************************

*******************************************************

* common register usage:
* a4=DOSpacket
* a5=private struct
* a6=execbase

start:	movem.l	d2-d7/a2-a6,-(sp)	;do some basic inits
	move.l	4.w,a6
	suba.l	a1,a1
	sys	FindTask
	movea.l	d0,a2
;	bset	#0,(pr_Flags+1,a2)	;unload seglist upon expunge
	lea	(pr_MsgPort,a2),a3
	movea.l	a3,a0
	sys	WaitPort
	movea.l	a3,a0
	sys	GetMsg		;get startup message
	movea.l	d0,a4
	movea.l	(sp_Msg+MN+LN_NAME,a4),a4
	clr.l	(dp_Res1,a4)
	move.l	#ERROR_NO_FREE_STORE,(dp_Res2,a4)
	move.l	dp_Arg3(a4),d7
	lsl.l	#2,d7
	movea.l	d7,a0
	clr.l	(dn_Task,a0)	;will restart the handler on failure
	move.l	#My_SIZEOF,d0
	move.l	#MEMF_CLEAR|MEMF_PUBLIC,d1
	sys	AllocVec
	tst.l	d0
	beq	exit1
	movea.l	d0,a5
	DBUG1	txstart,a5
	move.l	a6,(ExecBase,a5)
	move.l	a2,MyProcess(a5)
	move.l	a3,MyMsgPort(a5)

	IFD	rom
	moveq	#OLTAG_DOS,d0
	sys	TaggedOpenLibrary
	ELSE
	lea	dosname(pc),a1
	moveq	#37,d0
	sys	OpenLibrary
	ENDC

	move.l	d0,DosBase(a5)
	beq	exit2
	lea	(RootDir,a5),a1
	bsr	SetCurrentDate

	IFND	rom
	cmp.w	#39,(LIB_VERSION,a6)
	bcs	.OS20
	ENDC

	moveq	#1,d0
	move.l	#5000,d1
	move.l	#2000,d2
	sys	CreatePool
	move.l	d0,MyMemPool(a5)
.OS20	lea	(MyML,a5),a0
	NEWLIST	a0
	move.l	d7,MyDeviceNode(a5)
	movea.l	d7,a0
	movea.l	dn_Name(a0),a0
	lea	RootDir+myf_Name(a5),a1
	moveq	#6,d0
	bsr	CopyBSTR2Char

	IFND	rom
	lea	intuiname(pc),a1	;init "unknown packet" requester
	moveq	#36,d0
	sys	OpenLibrary
	move.l	d0,IntuiBase(a5)
	beq.s	.A
	lea	ReqStruct(a5),a0
	moveq	#es_SIZEOF,d0
	move.l	d0,(es_StructSize,a0)
	lea	reqtitle(pc),a1
	move.l	a1,(es_TextFormat,a0)
	lea	reqbutton(pc),a1
	move.l	a1,(es_GadgetFormat,a0)
.A	moveq	#0,d2

args:	move.l	DosBase(a5),a6
	move.l	MyDeviceNode(a5),a1
	move.l	dn_Startup(a1),d3
	beq	.A
	clr.l	dn_Startup(a1)
	movea.l	d3,a0
	lea	TempPath(a5),a1
	move.l	a1,a2
	move.l	#MAXPATH-1,d0
	bsr	CopyBSTR2Char
	moveq	#DOS_RDARGS,d1
;	moveq	#0,d2
	sys	AllocDosObject
	move.l	d0,d3
	beq	.A
	move.l	a2,a0
.D	move.b	(a0)+,d0
	beq.s	.C
	cmp.b	#"_",d0
	bne.s	.D
	move.b	#" ",-1(a0)
	bra.s	.D
.C	move.b	#LF,-1(a0)
	clr.b	(a0)
	sub.l	a2,a0
	lea	(StupVolname,a5),a3
	move.l	a3,d2			;Argument-Array
	move.l	d3,a1			;Rd-Args-Struct
	move.l	a2,(a1)+		;CSource.Buffer
	move.l	a0,(a1)			;CSource.Length
	lea	template(pc),a0
	move.l	a0,d1			;Template
	sys	ReadArgs		;process Startup line
	DBUG4	txargs,(a3),4(a3),8(a3),12(a3)
	move.l	d0,d2
	bne.s	.E

* Display requester for bad startup arguments

	move.l	IntuiBase(a5),d0
	beq.s	.A
	lea	ReqStruct(a5),a1
	lea	reqreadargs(pc),a0
	move.l	a0,(es_TextFormat,a1)
	sub.l	a2,a2
	sub.l	a3,a3
	sub.l	a0,a0
	movea.l	d0,a6
	sys	EasyRequestArgs
	movea.l	DosBase(a5),a6
	bra.s	.A

.E	movea.l	(a3)+,a0
	move.l	a0,d0
	bne.s	.F
	ENDC

.A	lea	volname1(pc),a0

	IFND	rom
	tst.l	StupIcon(a5)
	beq.s	.F
	lea	volname2(pc),a0
	ENDC

.F	lea	VolumeName(a5),a1
	moveq	#MAXNAME-2,d0
	bsr	CopyChar2BSTR
	move.l	d0,d4

	IFND	rom
	move.l	d2,d1
	sys	FreeArgs
	moveq	#DOS_RDARGS,d1
	move.l	d3,d2
	beq	.B
	sys	FreeDosObject
	ENDC

.B	move.l	d4,(StupVolname,a5)
	movea.l	(ExecBase,a5),a6

upconv:	bsr	InitConversion		;init upper case table
	sys	CreateMsgPort
	move.l	d0,(NotifyPort,a5)
	beq	exit3

	IFND	rom
	tst.l	StupNocopy(a5)
	bne	nocopy
	ENDC

	sys	CreateMsgPort
	move.l	d0,(PacketPort,a5)
	beq	exit4
	move.l	d0,d6
	clr.l	-(sp)
	pea	(procn,pc)
	move.l	#NP_Name,-(sp)
	pea	(procc,pc)
	move.l	#NP_Entry,-(sp)
	movea.l	(DosBase,a5),a6
	move.l	sp,d1
	sys	CreateNewProc
	movea.l	(ExecBase,a5),a6
	lea	(20,sp),sp
	tst.l	d0
	beq	exit5
	movea.l	d0,a2
	
	movea.l	a2,a1
	move.l	a5,(TC_Userdata,a2)	;stuff our data area to userdata
	move.l	#SIGBREAKF_CTRL_F,d0	;let's use CTRL-F for signalling
	sys	Signal			;we're done

	movea.l	d6,a0
	moveq	#1,d0
	move.b	(MP_SIGBIT,a0),d1
	lsl.l	d1,d0
	sys	Wait

	lea	(MyMsg,a5),a0		;init message
	moveq	#MN_SIZE+8,d0
	move.w	d0,(MN_LENGTH,a0)
	moveq	#NT_MESSAGE,d0
	move.b	d0,(LN_TYPE,a0)
	move.l	d6,(MN_REPLYPORT,a0)

nocopy:	lea	RootDir(a5),a2		;create Lock for DiskInfo
	moveq	#ACCESS_READ,d2
	bsr	CreateLock
	move.l	d0,RootLock(a5)
	beq	exit6

volnod:	lea	VolumeNode(a5),a2	;set up volume node
	move.l	a2,d0
	lsr.l	#2,d0
	move.l	d0,MyVolumeBPTR(a5)
	moveq	#DLT_VOLUME,d0
	move.l	d0,dl_Type(a2)
	move.l	MyMsgPort(a5),dl_Task(a2)
	move.l	#"DOS"<<8,dl_DiskType(a2)
	move.l	(StupVolname,a5),(dl_Name,a2)
	lea	(dl_VolumeDate,a2),a0
	move.l	a0,d1
	movea.l	(DosBase,a5),a6
	sys	DateStamp

	IFND	rom
	tst.l	StupIcon(a5)
	beq.s	.D
	moveq	#LDF_VOLUMES|LDF_WRITE,d1
	sys	AttemptLockDosList
	andi.l	#~1,d0
	beq	exit7
	move.l	a2,d1
	sys	AddDosEntry
	move.l	d0,d2
	moveq	#LDF_VOLUMES|LDF_WRITE,d1
	sys	UnLockDosList
	tst.l	d2
	beq	exit7
	ENDC

.D	movea.l	d7,a0		;DeviceNode
	move.l	(MyMsgPort,a5),(dn_Task,a0)
	moveq	#DOSTRUE,d2		;init ok: reply startup packet
	moveq	#0,d3
	move.l	a4,d1
	sys	ReplyPkt

loop:	sys	WaitPkt
	movea.l	d0,a4
	clr.l	(dp_Res1,a4)
	clr.l	(dp_Res2,a4)

* Handle replies of Notify messages
GetNotify:
\A	movea.l	NotifyPort(a5),a0
	movea.l	(ExecBase,a5),a6
	sys	GetMsg
	tst.l	d0
	beq.s	\E
	move.l	d0,a1
	DBUG1	txgnoti,a1
;	move.l	nm_NReq(a1),a0
;	subq.l	#1,nr_MsgCount(a0)
	moveq	#NotifyMessage_SIZEOF,d0
	bsr	FreeMem
	bra.s	\A

\E	move.l	dp_Type(a4),d0

	IFD	DEBUG
	move.l	dp_Port(a4),a1
	move.l	MP_SIGTASK(a1),a1
	move.l	LN_NAME(a1),a1
	DBUG5	txpack,d0,dp_Arg1(a4),dp_Arg2(a4),dp_Arg3(a4),a1
	ENDC

	move.l	d0,d1
	swap	d1
	tst.w	d1
	bne.s	.A
	lea	start(pc),a1
	lea	JumpTable(pc),a2
	moveq	#0,d2
.C	move.w	(a2)+,d1
	bpl.s	.E
.A	DBUG1	txerr1,d0
	IFND	rom
	bsr	RequestUnknown
	ENDC
	move.w	#ERROR_ACTION_NOT_KNOWN,(dp_Res2+2,a4)
	bra.s	.D

.E	move.w	(a2)+,d2
	cmp.w	d0,d1
	bne.s	.C
	add.l	d2,a1
				;***

* packets must preserve a5/a4; all others may be trashed

	jsr	(a1)		;jump into subroutine to process packet
				;***
.D
	IFD	DEBUG
	moveq	#DOSTRUE,d0
	cmp.l	dp_Res1(a4),d0
	beq.s	.\@H
	DBUG2	txres,dp_Res1(a4),dp_Res2(a4)
.\@H	ENDC

_rp	movea.l	(DosBase,a5),a6
_rp1	move.l	(dp_Res1,a4),d2
	move.l	(dp_Res2,a4),d3
	move.l	a4,d1
	sys	ReplyPkt
	bra	loop

ADie:	addq.l	#4,sp			;exit routine

	move.w	#ERROR_OBJECT_IN_USE,(dp_Res2+2,a4)
	move.l	NumLocks(a5),d0
	subq.l	#1,d0
	bne	_rp
	tst.l	(NotifyList,a5)
	bne	_rp

	IFND	rom
	tst.l	StupIcon(a5)
	beq	.A
	movea.l	(DosBase,a5),a6
	moveq	#LDF_DEVICES|LDF_VOLUMES|LDF_WRITE,d1
	sys	AttemptLockDosList
	andi.l	#~1,d0
	beq	_rp1
	lea	(VolumeNode,a5),a0
	move.l	a0,d1
	sys	RemDosEntry
	move.l	d0,d2
	beq	.B
	move.l	(MyDeviceNode,a5),d1
	sys	RemDosEntry
.B	moveq	#LDF_DEVICES|LDF_VOLUMES|LDF_WRITE,d1
	sys	UnLockDosList
	tst.l	d2
	beq	_rp1
	movea.l	(ExecBase,a5),a6
	ENDC
.A

* Reply all packets that are waiting to be processed (for exit)
FlushPackets:
.A	move.l	MyMsgPort(a5),a0
	sys	GetMsg
	tst.l	d0
	beq	.exit
	move.l	d0,a1
	move.l	(MN+LN_NAME,a1),d0
	beq.s	.A
	movea.l	d0,a0
	clr.l	(dp_Res1,a0)
	move.l	#ERROR_ACTION_NOT_KNOWN,(dp_Res2,a0)
	clr.l	(dp_Port,a0)
	sys	ReplyMsg
	bra.s	.A

.exit	moveq	#DOSTRUE,d0
	move.l	d0,(dp_Res1,a4)
	move.l	MyDeviceNode(a5),a0
	clr.l	dn_Task(a0)
	clr.l	(MyMsgPort,a5)
exit7:	movea.l	(ExecBase,a5),a6
	move.l	RootLock(a5),a1
	bsr	FreeLock
exit6:
	IFND	rom
	tst.l	StupNocopy(a5)
	bne	exit4
	ENDC

	lea	(MyMsg,a5),a1
	clr.l	(MN_SIZE,a1)		;=QUIT
	movea.l	(DosPort,a5),a0
	sys	PutMsg
	movea.l	(PacketPort,a5),a0
	sys	WaitPort
exit5:	movea.l	(PacketPort,a5),a0
	sys	DeleteMsgPort
exit4:	movea.l	(NotifyPort,a5),a0
	sys	DeleteMsgPort
exit3:	move.l	DosBase(a5),a1
	sys	CloseLibrary
	DBUG3	txend,FileSize(a5),TotalSize(a5),NumLocks(a5)
	move.l	MyMemPool(a5),d0
	beq.s	.A
	move.l	d0,a0
	sys	DeletePool
.A	lea	(MyML,a5),a2
.C	IFEMPTY	a2,exit2
	SUCC	a2,a1
	bsr	_free
	bra	.C

exit2:	movea.l	(MyMsgPort,a5),a3
	move.l	a5,a1
	sys	FreeVec
exit1:	movea.l	(dp_Link,a4),a1
	movea.l	(dp_Port,a4),a0
	move.l	a3,(dp_Port,a4)
	clr.l	(MN,a1)
	clr.l	(MN+LN_PRED,a1)
	sys	PutMsg		;reply startup/die message
	movem.l	(sp)+,d2-d7/a2-a6
	moveq	#0,d0
	rts

*******************************************************

* table for branching from main to subroutine depending on the
* current packet

JumpTable:
	dc.w	ACTION_NIL,NoAction-start
	dc.w	ACTION_SET_PROTECT,ASetProtect-start
	dc.w	ACTION_SET_COMMENT,Unsupported-start
	dc.w	ACTION_SET_DATE,ASetDate-start
	dc.w	ACTION_INHIBIT,Unsupported-start
	dc.w	ACTION_FLUSH,NoAction-start
	dc.w	ACTION_IS_FILESYSTEM,NoAction-start

	dc.w	ACTION_DIE,ADie-start
	dc.w	ACTION_CURRENT_VOLUME,ACurrentVolume-start
	dc.w	ACTION_RENAME_DISK,ARenameDisk-start
	dc.w	ACTION_WRITE_PROTECT,AWriteProtect-start

	dc.w	ACTION_FINDUPDATE,AOpenReadWrite-start
	dc.w	ACTION_FINDINPUT,AOpenOldfile-start
	dc.w	ACTION_FINDOUTPUT,AOpenNewfile-start
	dc.w	ACTION_END,ACloseFile-start
	dc.w	ACTION_SEEK,ASeekPosition-start
	dc.w	ACTION_READ,AReadFile-start
	dc.w	ACTION_WRITE,AWriteFile-start
	dc.w	ACTION_DELETE_OBJECT,ADeleteObject-start
	dc.w	ACTION_RENAME_OBJECT,ARenameObject-start
	dc.w	ACTION_CREATE_DIR,ACreateDir-start

	dc.w	ACTION_LOCATE_OBJECT,ALocateObject-start
	dc.w	ACTION_PARENT,AParentDir-start
	dc.w	ACTION_COPY_DIR,ADuplicateLock-start
	dc.w	ACTION_FREE_LOCK,AFreeLock-start
	dc.w	ACTION_SAME_LOCK,ASameLock-start
	dc.w	ACTION_EXAMINE_OBJECT,AExamineObject-start
	dc.w	ACTION_EXAMINE_ALL,Unsupported-start
	dc.w	ACTION_EXAMINE_FH,AExamineFH-start
	dc.w	ACTION_EXAMINE_ALL_END,Unsupported-start
	dc.w	ACTION_EXAMINE_NEXT,AExamineNext-start
	dc.w	ACTION_DISK_INFO,ADiskInfo1-start
	dc.w	ACTION_INFO,ADiskInfo2-start

	dc.w	ACTION_ADD_NOTIFY,AAddNotify-start
	dc.w	ACTION_REMOVE_NOTIFY,ARemoveNotify-start
	dc.w	ACTION_SET_FILE_SIZE,ASetFileSize-start
	dc.w	ACTION_FH_FROM_LOCK,AFHfromLock-start
	dc.w	ACTION_CHANGE_MODE,AChangeMode-start
	dc.w	ACTION_COPY_DIR_FH,ADupLockFromFH-start
	dc.w	ACTION_PARENT_FH,AParentFromFH-start
	dc.w	-1


* strings

volname1	dc.b	"Env",0
reqtitle	dc.b	"HappyENV warning:",0
procn		dc.b	'HappyENV DOS-process',0
requnknown	dc.b	"Task %s",LF,"uses packet %ld",0
reqbutton	dc.b	"OK",0

	IFND	rom
intuiname	dc.b	"intuition.library",0
dosname		dc.b	"dos.library",0
utilname	dc.b	"utility.library",0
_expn		dc.b	'expansion.library',0
reqreadargs	dc.b	"Bad startup arguments",0
template	dc.b	"VOLNAME/K,ICON/S,NOCOPY/S,NOREQ/S",0
volname2	dc.b	"Environment",0
	ENDC

envarc		dc.b	'ENVARC:',0

		IFD	EXPAND
prefstx		dc.b	"prefs",0
configtx	dc.b	"config",0
		ENDC

		even

*******************************************************

* Init to-upper-conversion table which allows
* quick case independent compares
InitConversion:
	lea	CharTable(a5),a2
	move.w	#255,d2
	move.w	d2,d0
.A	move.b	d0,0(a2,d0.w)
	dbra	d0,.A
	moveq	#"A",d0
.C	move.b	d0,"a"-"A"(a2,d0.w)
	addq.w	#1,d0
	cmp.b	#"Z"+1,d0
	bne.s	.C
	move.w	#192,d0
.F	move.b	d0,224-192(a2,d0.w)
	addq.w	#1,d0
	cmp.b	#222+1,d0
	bne.s	.F

	IFD	rom
	moveq	#OLTAG_UTILITY,d0
	sys	TaggedOpenLibrary
	ELSE
	lea	utilname(pc),a1		;open utility.library
	moveq	#37,d0
	sys	OpenLibrary
	ENDC

	tst.l	d0
	beq.s	.D
	move.l	d0,a6
.E	move.w	d2,d0
	sys	ToUpper
	move.b	d0,0(a2,d2.w)
	dbra	d2,.E
	move.l	a6,a1
	move.l	(ExecBase,a5),a6
	sys	CloseLibrary
.D	rts

* Display requester for unknown packets

	IFND	rom
RequestUnknown:
	tst.l	StupNoreq(a5)
	bne.s	.A
	move.l	IntuiBase(a5),d0
	beq.s	.A
	movea.l	d0,a6
	lea	ReqStruct(a5),a1
	lea	requnknown(pc),a0
	move.l	a0,(es_TextFormat,a1)
	sub.l	a2,a2
	lea	FormatArgs(a5),a3
	move.l	dp_Type(a4),4(a3)
	move.l	dp_Port(a4),a0
	move.l	MP_SIGTASK(a0),a0
	move.l	LN_NAME(a0),(a3)
	sub.l	a0,a0
	sys	EasyRequestArgs
	move.l	(ExecBase,a5),a6
.A	rts
	ENDC

*******************************************************

*******************************************************


* Following there are all subroutines to process the individual packets
* It starts with some general purpose and Filehandle handling ones.

* No action will be performed
NoAction:
	moveq	#DOSTRUE,d0
	move.l	d0,dp_Res1(a4)
	rts

ACurrentVolume:
	move.l	MyVolumeBPTR(a5),dp_Res1(a4)
	bra	NoAction

* For unsupported packets without showing a requester
Unsupported:
	DBUG1	txerr1,dp_Type(a4)
	move.w	#ERROR_ACTION_NOT_KNOWN,(dp_Res2+2,a4)
_ret1	rts


ARenameDisk:
	move.w	#ERROR_DISK_WRITE_PROTECTED,(dp_Res2+2,a4)
	tst.b	WriteProtected(a5)
	bne	_ret1
	move.l	dp_Arg1(a4),a0
	lea	TempName(a5),a1
	moveq	#MAXNAME-2,d0
	bsr	CopyBSTR2Char
	lea	TempName(a5),a0
	lea	VolumeName(a5),a1
	moveq	#MAXNAME-2,d0
	bsr	CopyChar2BSTR
	bra	NoAction

AWriteProtect:
	move.l	dp_Arg1(a4),d0
	move.b	d0,WriteProtected(a5)
	bra	NoAction

AOpenOldfile:
	move.l	dp_Arg3(a4),a0
	move.l	dp_Arg2(a4),d0
	moveq	#ACCESS_READ,d2
	bsr	LocateObject
OpnOld:	move.l	dp_Arg1(a4),a3	;label used by AOpenReadWrite and AFHFromLock
	add.l	a3,a3
	add.l	a3,a3
	clr.l	fh_Interactive(a3)
	move.l	d0,fh_Arg1(a3)
	beq	_ret2
	move.l	d0,a0
	move.l	myl_Key(a0),a1
	move.w	#ERROR_OBJECT_WRONG_TYPE,(dp_Res2+2,a4)
	tst.b	myf_Type(a1)		;do not open dirs
	bmi.s	.A
	move.l	a0,a1
	bsr	FreeLock
	bra	_ret2
.A	move.l	myf_Size(a1),myl_Size(a0)
	moveq	#0,d0
	move.b	myf_DataOffs(a1),d0
	add.l	d0,a1
	move.l	a1,myl_Data(a0)
	move.b	#1,myl_Mode(a0)
	DBUG1	txopeno,fh_Arg1(a3)
	bra	NoAction

AOpenReadWrite:
	move.l	dp_Arg3(a4),a0
	move.l	dp_Arg2(a4),d0
	moveq	#ACCESS_READ,d2
	bsr	LocateObject
	bne.s	OpnOld

AOpenNewfile:
	move.w	#ERROR_DISK_WRITE_PROTECTED,(dp_Res2+2,a4)
	tst.b	WriteProtected(a5)
	bne	_ret2
	move.w	#ERROR_OBJECT_EXISTS,(dp_Res2+2,a4)
	move.l	dp_Arg1(a4),a3
	add.l	a3,a3
	add.l	a3,a3
	clr.l	fh_Interactive(a3)
	move.l	dp_Arg2(a4),d0		;does file already exist ?
	move.l	dp_Arg3(a4),a0
	bsr	SearchObject
	beq.s	.A
	move.l	d0,a1
	tst.b	myf_Type(a1)		;do not overwrite dirs
	bpl	_ret2
	move.w	#ERROR_DELETE_PROTECTED,(dp_Res2+2,a4)
	btst	#FIBB_DELETE,(myf_Attrs,a1)
	bne	_ret2
	move.w	#ERROR_WRITE_PROTECTED,(dp_Res2+2,a4)
	btst	#FIBB_WRITE,(myf_Attrs,a1)
	bne	_ret2
	move.l	(myf_Parent,a1),-(sp)
	bsr	DeleteObject
	movea.l	(sp)+,a1
	beq	_ret2
	bsr	SetCurrentDate		;update parent date
.A	bsr	SplitPath
	move.w	#ERROR_DIR_NOT_FOUND,(dp_Res2+2,a4)
	move.l	dp_Arg2(a4),d0
	bsr	CreatePath
	beq	_ret2
	move.l	a3,-(sp)
	move.l	d0,a3
	lea	TempName(a5),a2
	moveq	#0,d2
	bsr	CreateNewObject
	move.l	(sp)+,a3
	beq.s	_ret2
	move.l	d0,a2
	moveq	#ACCESS_WRITE,d2
	bsr	CreateLock
	move.l	d0,fh_Arg1(a3)
	beq.s	_ret2
	move.l	d0,a0
	moveq	#DOSTRUE,d0
	move.b	d0,myl_Mode(a0)
	move.l	d0,dp_Res1(a4)
	bset	#3,(myf_Type,a2)
	DBUG1	txopenn,fh_Arg1(a3)
	movea.l	(myf_Parent,a2),a1
	bsr	SetCurrentDate		;update parent date
_ret2	rts

ACloseFile:
	move.l	dp_Arg1(a4),a1
	tst.b	myl_Mode(a1)
	bmi.s	.D
	bsr	FreeLock
	bra.s	.ok
.D	move.l	myl_Size(a1),d2
	move.l	myl_Key(a1),a2
	move.l	myf_Parent(a2),a3
	lea	myf_Name(a2),a2
	bsr	CreateNewObject
	beq.s	_ret2
	move.l	d0,d4
	move.l	d0,a3
	add.l	d1,a3
	move.l	dp_Arg1(a4),a2
	bsr	CopyBlocksToMem
	move.l	a2,a1
	move.l	myl_Key(a2),a2
	bsr	FreeLock
	moveq	#3,d0
	btst	d0,(myf_Type,a2)	;file has been modified?
	beq	.unset
	movea.l	d4,a1
	bset	d0,(myf_Type,a1)
	bclr	#FIBB_ARCHIVE,(myf_Type,a1)
	bsr	SetCurrentDate		;update object date
	movea.l	d4,a1
	movea.l	(myl_Key,a1),a0
	bclr	#FIBB_ARCHIVE,(myf_Type,a0)
	movea.l	(myf_Parent,a1),a1
	bsr	SetCurrentDate		;update parent date
.unset	move.l	a2,a1
	bsr	DeleteObj
	beq.s	_ret2
	move.l	d4,a0
	bsr	CheckNotify
.ok	bra	NoAction

* Copy all blocks of a filehandle to memory
* ENTRY: a2=lock, a3=destination memory
* EXIT: nothing (some registers changed)
* USED BY: ACloseFile, ASetFileSize
CopyBlocksToMem:
	move.l	myl_Start(a2),d3
.F	tst.l	d3
	beq	_ret2
	move.l	d3,-(sp)
	move.l	d3,a0
	move.l	(a0)+,d3
	move.l	(a0)+,d2
	move.l	a3,a1
	move.l	d2,d0
	sys	CopyMem
	add.l	d2,a3
	move.l	d2,d0
	addq.l	#8,d0
	move.l	(sp)+,a1
	bsr	FreeMem
	bra.s	.F

AWriteFile:
	moveq	#-1,d0
	move.l	d0,dp_Res1(a4)
	move.w	#ERROR_DISK_WRITE_PROTECTED,(dp_Res2+2,a4)
	tst.b	WriteProtected(a5)
	bne	.fail
	move.l	dp_Arg1(a4),a2		;lock
	movea.l	(myl_Key,a2),a0
	move.w	#ERROR_WRITE_PROTECTED,(dp_Res2+2,a4)
	btst	#FIBB_WRITE,(myf_Attrs,a0)
	bne	.fail
	move.l	dp_Arg2(a4),a3		;buffer
	move.l	dp_Arg3(a4),d3		;length
	bsr	ReadToWriteMode
	beq	.fail
.F	move.l	d3,d2
	move.l	myl_Block(a2),d0
	beq.s	.E
	move.l	d0,a1
	move.l	4(a1),d0
	sub.l	myl_BlockPos(a2),d0
	beq.s	.C
	bmi.s	.C
	MIN	d0,d2,G
.D	addq.l	#8,a1
	add.l	myl_BlockPos(a2),a1
	move.l	a3,a0
	move.l	d2,d0
	sys	CopyMem
	DBUG2	txwins,d2,myl_Pos(a2)
	add.l	d2,a3
	add.l	d2,myl_Pos(a2)
	add.l	d2,myl_BlockPos(a2)
	sub.l	d2,d3
	beq.s	.ok
	bra.s	.F

.C	move.l	(a1),d0
	beq.s	.E
	move.l	d0,myl_Block(a2)
	clr.l	myl_BlockPos(a2)
	bra.s	.F	
	
.E	bsr	AppendNewBlock
	beq.s	.fail
	clr.w	(dp_Res2+2,a4)
	move.l	a3,a0
	move.l	d3,d0
	sys	CopyMem
	add.l	d3,myl_Pos(a2)
	move.l	d3,myl_BlockPos(a2)
.ok	move.l	dp_Arg3(a4),dp_Res1(a4)	;zero length writes are skipped
	beq	.fail
	movea.l	(myl_Key,a2),a1
	bset	#3,(myf_Type,a1)	;notification attention
	DBUG3	txwrite,dp_Arg3(a4),myl_Pos(a2),myl_Size(a2)
.fail	rts

ASetFileSize:
	moveq	#DOSTRUE,d0
	move.l	d0,dp_Res1(a4)
	move.w	#ERROR_DISK_WRITE_PROTECTED,(dp_Res2+2,a4)
	tst.b	WriteProtected(a5)
	bne	.fail
	move.l	dp_Arg1(a4),a2		;a2=lock
	movea.l	(myl_Key,a2),a0
	move.w	#ERROR_WRITE_PROTECTED,(dp_Res2+2,a4)
	btst	#FIBB_WRITE,(myf_Attrs,a0)
	bne	.fail
	move.w	#ERROR_SEEK_ERROR,(dp_Res2+2,a4)
	move.l	dp_Arg2(a4),d3		;d3=position to truncate
	move.l	dp_Arg3(a4),d2		;d2=mode
	move.l	myl_Size(a2),d4		;d4=old size
;	moveq	#OFFSET_BEGINNING,d0
	cmp.l	d0,d2
	beq.s	.E
	moveq	#OFFSET_END,d0
	cmp.l	d0,d2
	bne.s	.C
	add.l	d4,d3
	bra.s	.E

.C	tst.l	d2
	bne	.fail
	add.l	myl_Pos(a2),d3
.E	DBUG2	txsize,myl_Key(a2),d4,d3
	tst.l	d3			;d3=new size
	bmi	.fail
	cmp.l	d4,d3
	beq.s	.ok			;size will not change
	bhi.s	.A			;append some data

	move.l	d3,myl_Size(a2)		;truncate some data
	MIN	d3,myl_Pos(a2),F
	bsr	ReadToWriteMode
	beq.s	.fail
	cmp.b	#1,d0
	beq.s	.ok
	move.l	myl_Pos(a2),myl_BlockPos(a2)
	move.l	d3,d0
	beq.s	.D
	bsr	CreateNewBlock
	beq.s	.fail
.D	move.l	myl_Start(a2),d4
	move.l	d0,myl_Block(a2)
	move.l	d0,myl_Start(a2)
	move.l	a1,a3
.J	tst.l	d4
	beq.s	.ok
	move.l	d4,-(sp)
	move.l	d4,a0
	move.l	(a0)+,d4
	move.l	(a0)+,d2
	cmp.l	d2,d3
	bls.s	.G
	move.l	d2,d0
	sub.l	d2,d3
	bra.s	.H
.G	move.l	d3,d0
	beq.s	.I
	moveq	#0,d3
.H	move.l	a3,a1
	sys	CopyMem
.I	add.l	d2,a3
	move.l	d2,d0
	addq.l	#8,d0
	move.l	(sp)+,a1
	bsr	FreeMem
	bra.s	.J

.A	bsr	ReadToWriteMode		;append some data
	beq.s	.fail
	sub.l	d4,d3
	bsr	AppendNewBlock
	beq.s	.fail
.ok	move.l	myl_Size(a2),dp_Res1(a4)
	beq	.fail
	movea.l	(myl_Key,a2),a0
	bset	#3,(myf_Type,a0)	;notification attention
.fail	rts


* Convert Lock from Read to Write mode
* ENTRY: a2=lock
* EXIT:  d0,Z=success (0=fail, 1=changed, 2=not changed)
* USED BY: AWriteFile, ASetFileSize
ReadToWriteMode:
	moveq	#2,d0
	tst.b	myl_Mode(a2)
	bmi.s	.ok
	move.w	#ERROR_WRITE_PROTECTED,(dp_Res2+2,a4)
	move.l	myl_Key(a2),a0
	move.b	myf_Locks(a0),d0
	cmpi.b	#-1,d0
	beq	.wr
	cmpi.b	#1,d0
	bhi.s	.fail
.wr	move.l	myl_Size(a2),d0
	beq.s	.A
	bsr	CreateNewBlock
	beq.s	.fail
.A	move.l	d0,myl_Block(a2)
	move.l	d0,myl_Start(a2)
	move.l	myl_Pos(a2),myl_BlockPos(a2)
	st	myl_Mode(a2)
	move.l	myl_Key(a2),a0
	st	myf_Locks(a0)
	move.l	myl_Data(a2),a0
	move.l	myl_Size(a2),d0
	beq.s	.C
	sys	CopyMem
.C	DBUG2	txchng,myl_Key(a2),myl_Pos(a2),myl_Size(a2)
	moveq	#1,d0
.ok	rts
.fail	moveq	#0,d0
	rts

* Create and append a new block as last one to block list
* ENTRY: d3=size, a2=lock
* EXIT:  d0,Z=success, a1=newblock+8
* USED BY: AWriteFile, ASetFileSize
AppendNewBlock:
	move.l	d3,d0
	bsr	CreateNewBlock
	beq.s	.fail
	add.l	d3,myl_Size(a2)
	move.l	myl_Block(a2),a0
	move.l	d0,myl_Block(a2)
	move.l	a0,d1
	bne.s	.A
	lea	myl_Start(a2),a0
.A	move.l	(a0),d1			;look for end of blocks
	beq.s	.C			; for SetFileSize
	move.l	d1,a0
	bra.s	.A
.C	move.l	d0,(a0)			;insert new block as last
	moveq	#1,d0
.fail	rts

* Allocates memory for a new block
* ENTRY: d0=size
* EXIT:  d0=block , a1=block+8
* USED BY: AppendNewBlock, ReadToWriteMode, ASetFileSIze
CreateNewBlock:
	move.w	#ERROR_DISK_FULL,(dp_Res2+2,a4)
	move.l	d0,-(sp)
	addq.l	#8,d0
	bsr	AllocMem
	beq.s	.fail
	move.l	d0,a1
	clr.l	(a1)+
	move.l	(sp),(a1)+
.fail	addq.l	#4,sp
	tst.l	d0
	rts

AReadFile:
	move.l	dp_Arg1(a4),a2		;lock
	movea.l	(myl_Key,a2),a0
	move.w	#ERROR_READ_PROTECTED,(dp_Res2+2,a4)
	btst	#FIBB_READ,(myf_Attrs,a0)
	bne	.ok
	move.l	dp_Arg2(a4),a3		;buffer
	move.l	myl_Size(a2),d0
	move.l	myl_Pos(a2),a0
	sub.l	a0,d0
	move.l	dp_Arg3(a4),d3		;length
	MIN	d0,d3,G
;	clr.w	(dp_Res2+2,a4)
	DBUG3	txread,d3,dp_Arg3(a4),myl_Pos(a2)
	add.l	d3,myl_Pos(a2)
	move.l	d3,dp_Res1(a4)
	beq.s	.ok
	tst.b	myl_Mode(a2)
	bpl.s	.E

.F	move.l	d3,d2
	move.l	myl_Block(a2),a0
	move.l	4(a0),d0
	sub.l	myl_BlockPos(a2),d0
	beq.s	.C
	bmi.s	.C
	MIN	d0,d2,D
	addq.l	#8,a0
	add.l	myl_BlockPos(a2),a0
	move.l	a3,a1
	move.l	d2,d0
	sys	CopyMem
	add.l	d2,a3
	add.l	d2,myl_BlockPos(a2)
	sub.l	d2,d3
	beq.s	.ok
	bra.s	.F
.C	move.l	(a1),d0
	beq.s	.ok
	move.l	d0,myl_Block(a2)
	clr.l	myl_BlockPos(a2)
	bra	.F

.E	move.l	myl_Data(a2),d0
	add.l	d0,a0
	move.l	a3,a1
	move.l	d3,d0
	sys	CopyMem
.ok	rts

ASeekPosition
	move.l	dp_Arg1(a4),a2		;a2=lock
	move.l	dp_Arg2(a4),d1		;d1=position
	move.l	dp_Arg3(a4),d2		;d2=mode
;	clr.w	(dp_Res2+2,a4)
	move.l	myl_Pos(a2),d3		;d3=old pos
	move.l	myl_Size(a2),d4		;d4=size

	moveq	#OFFSET_BEGINNING,d0
	cmp.l	d0,d2
	beq.s	.E
	moveq	#OFFSET_END,d0
	cmp.l	d0,d2
	bne.s	.C
	add.l	d4,d1
	bra.s	.E
.C	moveq	#OFFSET_CURRENT,d0
	cmp.l	d0,d2
	bne.s	.fail
	add.l	d3,d1
.E	move.l	d1,myl_Pos(a2)
	bmi.s	.A
	cmp.l	d1,d4
	bhs.s	Seeek
	move.l	d4,myl_Pos(a2)
	bra.s	.fail
.A	clr.l	myl_Pos(a2)
.fail	move.w	#ERROR_SEEK_ERROR,(dp_Res2+2,a4)
	moveq	#-1,d3

Seeek	tst.b	myl_Mode(a2)	;label not used globally
	bpl.s	.A
	move.l	myl_Pos(a2),d2
	move.l	myl_Start(a2),d0
	beq.s	.A
	bra.s	.E
.D	move.l	(a0),d0
	beq.s	.C
.E	move.l	d0,a0
	move.l	4(a0),d1
	sub.l	d1,d2
	beq.s	.F
	bpl.s	.D
.F	add.l	d1,d2
.C	move.l	a0,myl_Block(a2)
	move.l	d2,myl_BlockPos(a2)
.A	move.l	d3,dp_Res1(a4)
	DBUG1	txseek,myl_Pos(a2)
_ret3	rts

ADeleteObject:
	move.w	#ERROR_DISK_WRITE_PROTECTED,(dp_Res2+2,a4)
	tst.b	WriteProtected(a5)
	bne.s	_ret3
	move.w	#ERROR_OBJECT_NOT_FOUND,(dp_Res2+2,a4)
	move.l	dp_Arg1(a4),d0
	move.l	dp_Arg2(a4),a0
	bsr	SearchObject
	beq	_ret3
	movea.l	d0,a0
	move.w	#ERROR_DELETE_PROTECTED,(dp_Res2+2,a4)
	btst	#FIBB_DELETE,(myf_Attrs,a0)
	bne	_ret3
	movem.l	d0-d1,-(sp)
	moveq	#1,d0
	bsr	NotifyObject
	movem.l	(sp)+,d0-d1
	move.l	d0,a1
	move.l	(myf_Parent,a1),-(sp)
	bsr	DeleteObject
	movea.l	(sp)+,a1
	beq.s	_ret3
	bsr	SetCurrentDate
	bra	NoAction

ARenameObject:
	move.w	#ERROR_DISK_WRITE_PROTECTED,(dp_Res2+2,a4)
	tst.b	WriteProtected(a5)
	bne	_ret4
	move.w	#ERROR_OBJECT_NOT_FOUND,(dp_Res2+2,a4)
	move.l	dp_Arg1(a4),d0
	move.l	dp_Arg2(a4),a0
	bsr	SearchObject
	beq	_ret4
	move.l	d0,a3
	move.l	d1,d3
	move.w	#ERROR_OBJECT_IN_USE,(dp_Res2+2,a4)
	tst.b	myf_Locks(a3)
	bne	_ret4
	move.l	a3,a0
	bsr	NotifyObject
	move.w	#ERROR_OBJECT_EXISTS,(dp_Res2+2,a4)
	move.l	dp_Arg3(a4),d0
	move.l	dp_Arg4(a4),a0
	bsr	SearchObject
	bne	_ret4
	bsr	SplitPath
	move.w	#ERROR_DIR_NOT_FOUND,(dp_Res2+2,a4)
	move.l	dp_Arg3(a4),d0
	bsr	SearchPath
	beq	_ret4
	move.l	a3,-(sp)
	moveq	#0,d2
	tst.b	myf_Type(a3)
	bpl.s	.D
	move.l	myf_Size(a3),d2
.D	move.l	d0,a3
	lea	TempName(a5),a2
	bsr	CreateNewObject
	move.l	(sp)+,a3
	beq.s	_ret4
	move.l	d0,a2
	move.b	myf_Type(a3),myf_Type(a2)
	bpl.s	.A
	move.l	a2,a1			;rename file
	add.l	d1,a1
	move.l	a3,a0
	moveq	#0,d0
	move.b	myf_DataOffs(a3),d0
	add.l	d0,a0
	move.l	myf_Size(a3),d0
	sys	CopyMem
	bra.s	.C
.A	move.l	myf_First(a3),d0	;rename dir
	clr.l	myf_First(a3)
	move.l	d0,myf_First(a2)
	beq.s	.C
.E	move.l	d0,a0
	move.l	a2,myf_Parent(a0)
	move.l	myf_Link(a0),d0
	bne.s	.E
.C	move.l	d3,d1			;end rename
	move.l	a3,a1
	bsr	DeleteObject
	beq	_ret4
	move.l	a2,a0
	bsr	CheckNotify
	bra	NoAction

*******************************************************

* Here are subroutines for packets that deal with Locks. 

ALocateObject:
	move.l	dp_Arg1(a4),d0
	move.l	dp_Arg2(a4),a0
	move.l	dp_Arg3(a4),d2
	bsr	LocateObject
	lsr.l	#2,d0
	move.l	d0,dp_Res1(a4)
_ret4	rts

ADupLockFromFH:
	move.l	dp_Arg1(a4),d0
	bra.s	DupLock


* fixed ZERO lock -MM

ADuplicateLock:
	move.l	dp_Arg1(a4),d0
	lsl.l	#2,d0
DupLock	movea.l	d0,a0
	bne	.reg		;regular non-ZERO lock
	movea.l	(RootLock,a5),a0
.reg	move.l	myl_Key(a0),a2
	moveq	#ACCESS_READ,d2
	bsr	CreateLock
	lsr.l	#2,d0
	move.l	d0,dp_Res1(a4)
	rts

AParentFromFH:
	move.l	dp_Arg1(a4),d0
	bra.s	ParentD
AParentDir:
	move.l	dp_Arg1(a4),d0
	lsl.l	#2,d0
ParentD	beq.s	.fail		;label not used globally
	move.l	d0,a0
	move.l	myl_Key(a0),a2
	move.l	myf_Parent(a2),d0
	beq.s	.fail
	move.l	d0,a2
	moveq	#ACCESS_READ,d2
	bsr	CreateLock
	lsr.l	#2,d0
	move.l	d0,dp_Res1(a4)
.fail
_ret5	rts

AFreeLock:
	move.l	dp_Arg1(a4),d0
	beq.s	_ret5
	lsl.l	#2,d0
	move.l	d0,a1
	bsr	FreeLock
	bra	NoAction

ASameLock:
	IFND	rom
	tst.l	StupIcon(a5)
	bne	Unsupported
	ENDC

	move.l	dp_Arg1(a4),a0
	add.l	a0,a0
	add.l	a0,a0
	move.l	dp_Arg2(a4),a1
	add.l	a1,a1
	add.l	a1,a1
	move.l	myl_Key(a0),d0
	cmp.l	myl_Key(a1),d0
	bne	.dif
	moveq	#DOSTRUE,d1
	move.l	d1,dp_Res1(a4)
.dif	rts

AExamineFH:
	move.l	dp_Arg1(a4),a0
	bra.s	Exammi

AExamineObject:
	move.l	dp_Arg1(a4),a0
	add.l	a0,a0
	add.l	a0,a0
Exammi	move.l	myl_Key(a0),a0	;label not used globally
	move.l	dp_Arg2(a4),a1
	add.l	a1,a1
	add.l	a1,a1
	DBUG1	txexam,a0
	move.l	a1,a2
	moveq	#fib_SIZEOF/4-1,d0
.D	clr.l	(a2)+
	dbra	d0,.D
* Fill FileInfoBlock (fib) with infos about the object
* Used by AExamineFH, AExamineObject and AExamineNext
* ENTRY: a0=file, a1=fib
ExamineObject:
	moveq	#1,d0
	tst.l	myf_Parent(a0)
	beq.s	.C			;rootdir ?

;	IFD	DEBUG
;	btst	#6,myf_Type(a0)
;	beq.s	.\@D			;hidden file ?
;	moveq	#-3,d0
;	bra.s	.C
;.\@D	ENDC

	moveq	#-3,d0
	tst.b	myf_Type(a0)
	bmi.s	.C			;file ?
	moveq	#2,d0			;dir
.C	moveq	#0,d1
	move.l	d0,fib_DirEntryType(a1)
	move.l	d0,fib_EntryType(a1)
	bpl.s	.A
	move.l	myf_Size(a0),d1		;no file no size
.A	move.l	a0,fib_DiskKey(a1)
	move.l	d1,fib_Size(a1)		;calc size
	add.l	#1023,d1
	lsr.l	#8,d1
	lsr.l	#2,d1
	move.l	d1,fib_NumBlocks(a1)
	moveq	#0,d0
	move.b	(myf_Attrs,a0),d0
;	move.b	myf_Type(a0),d1
;	btst	#5,d1			;copied ? -> A
;	beq.s	.F
;	bset	#FIBB_ARCHIVE,d0
;.F	btst	#4,d1			;notify ? -> P
;	beq.s	.G
;	bset	#5,d0
;.G
;	IFD	DEBUG
;	btst	#6,d1			;hidden ? -> H
;	beq.s	.\@J
;	bset	#7,d0
;.\@J	tst.b	myf_Locks(a0)
;	beq.s	.\@H
;	bmi.s	.\@I
;	bset	#2,d0			;read-locked ?  -> no W
;	bra.s	.\@H
;.\@I	bset	#3,d0			;write-locked ? -> no R
;.\@H	ENDC

	move.l	d0,fib_Protection(a1)
	move.l	(myf_Date+0,a0),fib_DateStamp+0(a1)
	move.l	(myf_Date+4,a0),fib_DateStamp+4(a1)
	move.l	(myf_Date+8,a0),fib_DateStamp+8(a1)
	lea	myf_Name(a0),a0
	DBUG1	txexan,a0
	lea	fib_FileName(a1),a1
	moveq	#106,d0
	bsr	CopyChar2BSTR
	bra	NoAction

AExamineNext:
	move.l	dp_Arg1(a4),a2
	add.l	a2,a2
	add.l	a2,a2
	move.l	dp_Arg2(a4),a1
	add.l	a1,a1
	add.l	a1,a1
	move.l	fib_DiskKey(a1),a0
	DBUG1	txexnx,a0
	cmp.l	myl_Key(a2),a0
	bne.s	.A
	move.w	#ERROR_OBJECT_WRONG_TYPE,(dp_Res2+2,a4)
	tst.b	myf_Type(a0)
	bmi	_ret6
	move.l	myf_First-myf_Link(a0),a0
	bra.s	.C
.A	move.l	myf_Link(a0),a0
.C	move.w	#ERROR_NO_MORE_ENTRIES,(dp_Res2+2,a4)
	move.l	a0,d0
	beq	_ret6

	IFND	DEBUG
	btst	#6,myf_Type(a0)
	bne.s	.A
	ENDC

	bra	ExamineObject

ADiskInfo1:
	move.l	dp_Arg1(a4),a0
	bra.s	DiskInfo
ADiskInfo2:
	move.l	dp_Arg2(a4),a0
* ENTRY: a0=buffer (BPTR)
DiskInfo:			;label not used globally
	add.l	a0,a0
	add.l	a0,a0
	move.l	a0,a1
	moveq	#id_SIZEOF/4-1,d0
.D	clr.l	(a1)+
	dbra	d0,.D

	IFD	DEBUG
	move.l	NumLocks(a5),id_NumSoftErrors(a0)
	ENDC

	moveq	#ID_VALIDATED,d0
	tst.b	WriteProtected(a5)
	beq.s	.C
	moveq	#ID_WRITE_PROTECTED,d0
.C	move.l	d0,id_DiskState(a0)
	move.l	#1024,d1
	move.l	d1,id_BytesPerBlock(a0)
	lsr.l	#1,d1
	move.l	FileSize(a5),d0
	add.l	d1,d0
	lsr.l	#8,d0
	lsr.l	#2,d0
	move.l	d0,id_NumBlocksUsed(a0)
	move.l	TotalSize(a5),d0
	add.l	d1,d0
	lsr.l	#8,d0
	lsr.l	#2,d0
	bne.s	.A
	moveq	#1,d0
.A	move.l	d0,id_NumBlocks(a0)
	move.l	#"DOS"<<8,id_DiskType(a0)
	move.l	MyVolumeBPTR(a5),id_VolumeNode(a0)
	move.l	RootLock(a5),id_InUse(a0)
	bra	NoAction

ACreateDir:
	move.w	#ERROR_DISK_WRITE_PROTECTED,(dp_Res2+2,a4)
	tst.b	WriteProtected(a5)
	bne.s	.fail
	move.w	#ERROR_OBJECT_EXISTS,(dp_Res2+2,a4)
	move.l	dp_Arg1(a4),d0
	move.l	dp_Arg2(a4),a0
	bsr	SearchObject
	bne.s	.fail
	bsr	SplitPath
	move.w	#ERROR_DIR_NOT_FOUND,(dp_Res2+2,a4)
	move.l	dp_Arg1(a4),d0
	bsr	CreatePath
	beq.s	.fail
	move.l	d0,a3
	lea	TempName(a5),a2
	moveq	#0,d2
	bsr	CreateNewObject
	beq.s	.fail
	move.l	d0,a2
	clr.b	myf_Type(a2)
	moveq	#ACCESS_WRITE,d2
	bsr	CreateLock
	beq.s	.fail
	move.l	d0,a0
	lsr.l	#2,d0
	move.l	d0,dp_Res1(a4)
	move.l	myl_Key(a0),a0
	bsr	CheckNotify
.fail
_ret6	rts

*******************************************************

* Here are subroutines that deal with Locks and Filehandles

AFHfromLock:
	move.w	#ERROR_OBJECT_WRONG_TYPE,(dp_Res2+2,a4)
	move.l	dp_Arg2(a4),d0
	beq.s	_ret7
	lsl.l	#2,d0
	move.l	d0,a0
	move.l	myl_Key(a0),a0
	tst.b	myf_Type(a0)		;do not open dirs
	bmi	OpnOld
_ret7	rts

AChangeMode:
	move.w	#ERROR_OBJECT_IN_USE,(dp_Res2+2,a4)
	move.l	dp_Arg1(a4),d0
	move.l	dp_Arg2(a4),a2
	moveq	#CHANGE_FH,d1
	cmp.l	d0,d1
	bne.s	.A
	move.l	dp_Arg1(a4),a3
	add.l	a3,a3
	add.l	a3,a3
	move.l	fh_Arg1(a3),d0
	beq.s	_ret7
	move.l	d0,a2
	bra.s	.C
.A	moveq	#CHANGE_LOCK,d1
	cmp.l	d0,d1
	bne.s	_ret7
.C	move.l	myl_Key(a2),a0
	move.b	myf_Locks(a0),d0
	move.l	dp_Arg3(a4),d2
	moveq	#ACCESS_WRITE,d1
	cmp.l	d1,d2
	bne.s	.D
	cmp.b	d0,d1
	beq.s	.ok
	cmp.b	#1,d0
	bne.s	_ret7
	move.b	d1,myf_Locks(a0)
	bra.s	.ok
.D	cmp.b	d0,d1
	bne.s	.E
	tst.l	myl_Start(a2)
	bne.s	_ret7
	move.b	#1,myf_Locks(a0)
.E	moveq	#ACCESS_READ,d1
.ok	move.l	d1,myl_Access(a2)
	bra	NoAction

*******************************************************

* Here are subroutines for notification support
;	dc.b	'addn'
AAddNotify:
	move.w	#ERROR_NO_FREE_STORE,(dp_Res2+2,a4)
	move.l	dp_Arg1(a4),a3
	clr.l	(nr_MsgCount,a3)	;only one notify per NR allowed
	moveq	#myn_Name,d0
	move.l	nr_FullName(a3),a0
.C	addq.l	#1,d0
	tst.b	(a0)+
	bne.s	.C
	bsr	AllocMem
	beq	_ret8
	move.l	d0,a2
	lea	NotifyList(a5),a0
	move.l	(a0),myn_Link(a2)
	move.l	a2,(a0)
	move.l	a3,myn_Request(a2)
	move.l	nr_FullName(a3),a0
	lea	myn_Name(a2),a1
.D	move.b	(a0)+,(a1)+
	bne.s	.D
	bsr	UpNoti

	IFD	DEBUG
	lea	myn_Name(a2),a0
	DBUG4	txanoti,a2,a3,a0,d0
	ENDC

	btst	#NRB_NOTIFY_INITIAL,(nr_Flags+3,A3)	;initial notify ?
	beq	.A
	tst.l	d0
	beq.s	.A
;	move.l	d0,a0
;	btst	#6,myf_Type(a0)		;file really exists ?
;	bne.s	.A
	bsr	SendNotify
.A	bra	NoAction

ARemoveNotify:	movea.l	(dp_Arg1,A4),A2
	move.w	#ERROR_OBJECT_NOT_FOUND,(dp_Res2+2,a4)
	lea	NotifyList(a5),a1
.A	move.l	(a1),d0
	beq	_ret8
	move.l	a1,a0
	move.l	d0,a1
	cmp.l	myn_Request(a1),a2
	bne.s	.A
	move.l	myn_Link(a1),myn_Link(a0)
	DBUG2	txrnoti,a1,a2
	move.l	myn_Key(a1),d0
	beq.s	.D
	move.l	d0,a0
	bclr	#4,myf_Type(a0)
.D	moveq	#myn_Name,d0
	lea	myn_Name(a1),a0
.C	addq.l	#1,d0
	tst.b	(a0)+
	bne.s	.C
	bsr	FreeMem
	bra	NoAction

* Update myn_Key of notification list, mainly used by CheckNotify
UpdateNotify:
	move.l	a2,-(sp)
	lea	NotifyList(a5),a2
	bra.s	.A
.C	move.l	d0,a2
	tst.l	myn_Key(a2)
	bne.s	.A
	bsr.s	UpNoti

	IFD	DEBUG
	beq.s	.\@A
	lea	myn_Name(a2),a0
	DBUG2	txunoti,a0,d0
.\@A	ENDC

.A	move.l	(a2),d0
	bne.s	.C
	move.l	(sp)+,a2
_ret8	rts

* Subroutine used by UpdateNotify and AddNotify
UpNoti	lea	myn_Name(a2),a0
	lea	TempPath(a5),a1
	move.l	#MAXPATH-1,d0
	bsr	CopyChar2Char
	moveq	#0,d0
	bsr	SearchPath
	bne.s	.A
	moveq	#0,d0
	bsr	CalcFullName
.A	move.l	d0,myn_Key(a2)
	beq.s	.D
	move.l	d0,a0
	bset	#4,myf_Type(a0)
.D	rts

* Update notification list, check it for file/dir and parent dir
* and notify them.
* ENTRY: a0=file
CheckNotify:
	move.l	a0,-(sp)

	IFD	DEBUG
	lea	myf_Name(a0),a0
	DBUG1	txcnoti,a0
	ENDC

	bsr	UpdateNotify
	move.l	(sp)+,a0

* Check notification list for file/dir and parent dir and notify them.
* ENTRY: a0=file
NotifyObject:
	cmpi.l	#ACTION_END,(dp_Type,a4)
	bne	\ok
	moveq	#3,d0
	btst	d0,(myf_Type,a0)
	beq	.E
	bclr	d0,(myf_Type,a0)
	bsr	NotifyObj
	movea.l	myf_Parent(a0),a0
\ok	bsr	NotifyObj
.E	rts

* Notify object in a0, only used by NotifyObject
NotifyObj:
	movem.l	a2/a3,-(sp)
	lea	NotifyList(a5),a2
	bra.s	.A

.C	move.l	d0,a2
	cmp.l	myn_Key(a2),a0
	bne.s	.A
	move.l	a0,-(sp)
	move.l	myn_Request(a2),a3
	bsr	SendNotify
	move.l	(sp)+,a0
.A	move.l	(a2),d0
	bne.s	.C
	movem.l	(sp)+,a2/a3
	rts

* Send a Notify message or set a Notify signal
* ENTRY: a3=NotifyRequest
SendNotify:
	moveq	#NRB_SEND_SIGNAL,d0	;moveq	#1,d0
	btst	d0,nr_Flags+3(a3)
	beq.s	.C
	move.l	nr_Task(a3),a1

	IFD	DEBUG
	move.l	LN_NAME(a1),d0
	lea	myn_Name(a2),a0
	DBUG2	txsnoti,d0,a0
	ENDC

	move.b	nr_SignalNum(a3),d1
	asl.l	d1,d0
	sys	Signal
	rts

.C	btst	#NRB_SEND_MESSAGE,nr_Flags+3(a3)
	beq	.D
	moveq	#NotifyMessage_SIZEOF,d0
	bsr	AllocMem
	beq	.D
	move.l	d0,a0
	move.l	d0,a1
	moveq	#NotifyMessage_SIZEOF/2-1,d0
.A	clr.w	(a0)+
	dbra	d0,.A
	move.b	#NT_MESSAGE,LN_TYPE(a1)
	movea.l	NotifyPort(a5),a0
	move.l	a0,MN_REPLYPORT(a1)
	move.w	#NotifyMessage_SIZEOF,MN_LENGTH(a1)
	move.l	#$40000000,nm_Class(a1)
	move.w	#$1234,nm_Code(a1)
	move.l	a3,nm_NReq(a1)

	IFD	DEBUG
	move.l	a2,-(sp)
	move.l	nr_Port(a3),a0
	move.l	MP_SIGTASK(a0),a0
	move.l	LN_NAME(a0),a0
	lea	myn_Name(a2),a2
	DBUG3	txmnoti,a1,a0,a2
	move.l	(sp)+,a2
	ENDC

	move.l	nr_Port(a3),a0
	sys	PutMsg
	addq.l	#1,nr_MsgCount(a3)
;	btst	#NRB_WAIT_REPLY,nr_Flags+3(a3)
;	beq.s	.D
;	lea	NotifyPort(a5),a0
;	sys	WaitPort
;	bsr	GetNotify
.D	rts

*******************************************************

*******************************************************

* Following there are support subroutines for creating and deleting
* objects and Locks.

* Create a new file/dir
* ENTRY: a2=name, a3=parent dir, d2=data size, a5=global data ptr, a6=exec
* EXIT:  d1=offset of data, d0=new file, Z-flag for success
CreateNewObject:
	move.w	#ERROR_DISK_FULL,(dp_Res2+2,a4)
CreateNewObj:		;label used by SearchPath and CalcFullName
	movem.l	d3/a2-a3,-(sp)
	move.l	a2,a0
	moveq	#myf_Name,d3
.D	addq.l	#1,d3
	tst.b	(a0)+
	bne.s	.D
	addq.l	#1,d3
	bclr	#0,d3
	move.l	d3,d0
	add.l	d2,d0
	bsr	AllocMem
	beq	_ret9
	move.l	d0,a1
	lea	myf_First(a3),a0
.C	move.l	(a0),d0
	beq.s	.A
	move.l	d0,a0
	bra.s	.C
.A	move.l	a1,(a0)
	clr.l	myf_Link(a1)
	move.l	a3,myf_Parent(a1)
	move.l	d2,myf_Size(a1)
	add.l	d2,FileSize(a5)
	bsr	SetCurrentDate
	move.b	#$80,myf_Type(a1)
	move.b	d3,myf_DataOffs(a1)
	clr.b	myf_Locks(a1)
	clr.b	(myf_Attrs,a1)
	lea	myf_Name(a1),a0
.E	move.b	(a2)+,(a0)+
	bne.s	.E
	move.l	d3,d1
	move.l	a1,d0
.fail
	IFD	DEBUG
	move.l	d0,-(sp)
	lea	myf_Name(a1),a2
	move.l	myf_Size(a1),d0
	DBUG3	txcreat,a2,a1,d0
	move.l	(sp)+,d0
	ENDC

	movem.l	(sp)+,d3/a2-a3
_ret9	rts

ASetProtect:	moveq	#0,d2
	bra	_set
ASetDate:	moveq	#1,d2
_set	move.w	#ERROR_DISK_WRITE_PROTECTED,(dp_Res2+2,a4)
	tst.b	(WriteProtected,a5)
	bne	_ret9
	move.w	#ERROR_OBJECT_NOT_FOUND,(dp_Res2+2,a4)
	move.l	dp_Arg2(a4),d0
	move.l	dp_Arg3(a4),a0
	bsr	SearchObject
	beq.s	_ret9
	movea.l	d0,a0		;File
	movea.l	(dp_Arg4,a4),a2
	tst.l	d2
	bne	.date
	move.l	a2,d1
	move.b	d1,(myf_Attrs,a0)
	bra	.done
.date	lea	(myf_Date,a1),a1
	move.l	(a2)+,(a1)+
	move.l	(a2)+,(a1)+
	move.l	(a2)+,(a1)+
.done	bsr	CheckNotify
	bra	NoAction


* Get curent DateStamp
* ENTRY: a1=file or ZERO

SetCurrentDate:
.regs	reg	a0/a1/a6
	movem.l	.regs,-(sp)
	move.l	a1,d1
	bne	.is
	lea	(RootDir,a5),a1
.is	lea	(myf_Date,a1),a1
	move.l	a1,d1
	movea.l	(DosBase,a5),a6
	sys	DateStamp
	movem.l	(sp)+,.regs
	rts

* Delete file/dir in a1, no pred. known
* ENTRY: a1=file a4=packet
* EXIT:  d0=success, Z-flag
DeleteObj:
	move.w	#ERROR_OBJECT_NOT_FOUND,(dp_Res2+2,a4)
	move.l	myf_Parent(a1),a0
	move.l	myf_First(a0),d0
	sub.l	a0,a0
.C	move.l	a0,d1
	move.l	d0,a0
	tst.l	d0
	beq	DelErr
	move.l	myf_Link(a0),d0
	cmp.l	a1,a0
	bne.s	.C

* Delete file/dir in a1 with pred. known
* ENTRY: a1=file d1=pred a4=packet
DeleteObject:
	move.l	d2,-(sp)
	moveq	#0,d0
	move.w	#ERROR_OBJECT_IN_USE,(dp_Res2+2,a4)
	tst.b	myf_Locks(a1)
	bne.s	.fail
	tst.b	myf_Type(a1)
	bmi.s	.C
	move.w	#ERROR_DIRECTORY_NOT_EMPTY,(dp_Res2+2,a4)
	tst.l	myf_First(a1)		;check if dir is empty
	bne.s	.fail
.C	move.l	myf_Link(a1),d2		;rearrange links
	move.l	d1,a0
	tst.l	d1
	bne.s	.A
	move.w	#ERROR_OBJECT_WRONG_TYPE,(dp_Res2+2,a4)
	move.l	myf_Parent(a1),d1
	beq.s	.fail			;do not delete root
	move.l	d1,a0
	lea	myf_First-myf_Link(a0),a0
.A	move.l	d2,myf_Link(a0)
	DBUG1	txdel,a1
	lea	NotifyList(a5),a0
	bra.s	.D
.E	move.l	d0,a0
	cmp.l	myn_Key(a0),a1
	bne.s	.D
	clr.l	myn_Key(a0)		;check notifications
.D	move.l	(a0),d0
	bne.s	.E
;	moveq	#0,d0			;free memory
	move.b	myf_DataOffs(a1),d0
	move.l	myf_Size(a1),d1
	add.l	d1,d0
	sub.l	d1,FileSize(a5)
	bsr	FreeMem
	moveq	#1,d0
.fail	move.l	(sp)+,d2
DelErr	tst.l	d0		;label not used globally
	rts

* Create lock on file/dir given by name
* Must preserve a3
* ENTRY: d0=BPTR parent lock, a0=name (BSTR), d2=access mode
* EXIT:  d0=APTR lock, Z-flag
LocateObject:
	move.w	#ERROR_OBJECT_NOT_FOUND,(dp_Res2+2,a4)
	move.l	d0,d3
	bsr	SearchObject
	bne.s	.A
	moveq	#ACCESS_WRITE,d0
	sub.l	d2,d0
	beq	LOfail2
	move.l	d3,d0
	bsr	CalcFullName
	beq	LOfail2
.A	movea.l	d0,a2

* Create lock on file/dir given by key
* ENTRY: a2=file, d2=access mode
* EXIT:  d0=success, Z-flag
CreateLock:
	moveq	#0,d0
	move.w	#ERROR_OBJECT_IN_USE,(dp_Res2+2,a4)
	lea	myf_Locks(a2),a0
	moveq	#ACCESS_WRITE,d1
	cmp.l	d1,d2
	bne.s	.A
	tst.b	(a0)
	bne.s	LOfail
	move.b	d1,(a0)
	bra.s	.C
.A	tst.b	(a0)
	bmi.s	LOfail
	addq.b	#1,(a0)
.C	move.w	#ERROR_NO_FREE_STORE,(dp_Res2+2,a4)
	move.l	#myl_SIZEOF,d0
	bsr	AllocMem
	beq.s	LOfail
	move.l	d0,a0
	clr.l	(a0)+		;clr.l	myl_Link(a0)
	move.l	a2,(a0)+	;myl_Key(a0)
	move.l	d2,(a0)+	;myl_Access(a0)
	move.l	MyMsgPort(a5),(a0)+	;myl_Task(a0)
	move.l	MyVolumeBPTR(a5),(a0)+	;myl_Volume(a0)
	clr.l	(a0)+		;myl_Pos(a0)
	clr.l	(a0)+		;myl_Data(a0)
	clr.l	(a0)+		;myl_Start(a0)
	clr.l	(a0)+		;myl_Block(a0)
	clr.l	(a0)+		;myl_BlockPos(a0)
	clr.l	(a0)+		;myl_Size(a0)
	clr.b	(a0)		;myl_Mode(a0)
	addq.l	#1,NumLocks(a5)
LOfail
	IFD	DEBUG
	move.l	d0,d1
	lsr.l	#2,d1
	lea	myf_Name(a2),a0
	DBUG3	txlock,a0,d1,d2
	ENDC

LOfail2	tst.l	d0
	rts

* Free lock in a1
* ENTRY: a1=lock (APTR)
FreeLock:
;	move.w	#ERROR_UNLOCK_ERROR,(dp_Res2+2,a4)

	IFD	DEBUG
	move.l	a1,d0
	lsr.l	#2,d0
	DBUG1	txulock,d0
	ENDC

	move.l	myl_Key(a1),a0
	tst.b	myf_Locks(a0)
	beq.s	.A
	bmi.s	.D
	subq.b	#1,myf_Locks(a0)
	bra.s	.A
.D	clr.b	myf_Locks(a0)
.A	move.l	#myl_SIZEOF,d0
	bsr	FreeMem
	subq.l	#1,NumLocks(a5)
	rts

*******************************************************

*******************************************************

* Support routines for lower level memory management

* ENTRY: d0=size
* EXIT:  d0=memory/Z flag
AllocMem:
	add.l	d0,TotalSize(a5)
	move.l	MyMemPool(a5),d1
	beq.s	.A
	move.l	d1,a0
	sys	AllocPooled
	tst.l	d0
	rts
.A	moveq	#MEMF_PUBLIC,d1
	add.l	#MN_SIZE,d0
	sys	AllocVec
	tst.l	d0
	beq	.ret
	lea	(MyML,a5),a0	;add alloc to our list
	movea.l	d0,a1
	move.l  (a0),d1
	move.l  a1,(a0)
	movem.l d1/a0,(a1)
	move.l  d1,a0
	move.l  a1,(LN_PRED,a0)
	add.l	#MN_SIZE,d0
.ret	rts

* ENTRY: d0=size, a1=address
FreeMem:
	sub.l	d0,TotalSize(a5)
	move.l	MyMemPool(a5),d1
	beq.s	.A
	move.l	d1,a0
	sys	FreePooled
	rts
.A	suba.w	#MN_SIZE,a1
_free	move.l	a1,d1
	REMOVE		;remove alloc from our list
	movea.l	d1,a1
	sys	FreeVec
	rts

* Copy BSTR in a0 to string in a1, d0=maxlength without 0
CopyBSTR2Char:
	add.l	a0,a0
	add.l	a0,a0
	moveq	#0,d1
	move.b	(a0)+,d1
	MIN	d0,d1,D
	bra.s	.C
.A	move.b	(a0)+,(a1)+
.C	dbra	d1,.A
	clr.b	(a1)
	rts

* Copy string in a0 to BSTR in a1, d0=maxlength without 0
* EXIT:  d0=BPTR
CopyChar2BSTR:
	moveq	#-1,d1
	addq.l	#1,a1
.A	addq.l	#1,d1
	cmp.l	d0,d1
	bhs.s	.C
	move.b	(a0)+,(a1)+
	bne.s	.A
	subq.l	#1,a1
.C	clr.b	(a1)
	sub.l	d1,a1
	subq.l	#1,a1
	move.b	d1,(a1)
	move.l	a1,d0
	lsr.l	#2,d0
	rts

CopyChar2Char:
* Copy string in a0 to string in a1, d0=maxlength without 0
.C	move.b	(a0)+,(a1)+
	beq.s	.A
	subq.l	#1,d0
	bne.s	.C
.A	rts


*******************************************************

*******************************************************

* Subroutines for path string handling following

* Same as SearchObject, but deletes hidden files
* ENTRY: a0=filename (BSTR), d0=lock on dir
* EXIT:  d0=ptr to file, Z-flag, d1=previous file, TempPath=name
;DelSearchObject
;	bsr	SearchObject
;	beq.s	.A
;	move.l	d0,a1
;	btst	#6,myf_Type(a1)
;	beq.s	.A
;	bsr	DeleteObject		;delete hidden file
;	moveq	#0,d0
;.A	tst.l	d0
;	rts

* Same as SearchPath, but creates the path if not existent
* ENTRY: TempPath=dirname, d0=lock on dir
* EXIT:  d0=ptr to file, Z-flag, d1=previous file
CreatePath:
	moveq	#1,d1
	bra.s	SearchP

* Look for object with path
* ENTRY: a0=filename (BSTR), d0=lock on dir
* EXIT:  d0=ptr to file, Z-flag, d1=previous file, TempPath=name
SearchObject:
	move.l	d0,-(sp)
	lea	TempPath(a5),a1
	move.l	#MAXPATH-1,d0
	bsr	CopyBSTR2Char
	move.l	(sp)+,d0

* Same as SearchObject, but name is already copied to TempPath
* ENTRY: TempPath=dirname, d0=lock on dir
* EXIT:  d0=ptr to file, Z-flag, d1=previous file
SearchPath:
	moveq	#0,d1
SearchP	movem.l	d2-d7/a2-a4,-(sp)	;label not used globally
	move.l	d1,d7
	lea	TempPath(a5),a0
.A	move.b	(a0)+,d1
	beq	.B
	cmp.b	#":",d1			;skip volume name
	bne	.A
.B	tst.l	d0
	bne	.C
	move.l	(RootLock,a5),d0
	lsr.l	#2,d0
.C	tst.b	d1
	bne	.D
	lea	TempPath(a5),a0
.D	lsl.l	#2,d0			;use root or locked dir
	movea.l	d0,a2
	move.l	(myl_Key,a2),a1		;dir represented by lock

* label not used
expand

* label not used
* ENTRY: a0=Name, a1=parent dir; mind SP !
* EXIT:  d0=ptr to file, Z-flag, d1=previous file
SearchName:
.A	moveq	#0,d0
.D	cmp.b	#"/",(a0)		;parent dir when starting with /
	bne.s	.H
	move.l	myf_Parent(a1),d2
	beq.s	.fail
	move.l	d2,a1
	addq.l	#1,a0
	bra.s	.D
.H	tst.b	(a0)			;check for empty string
	beq.s	.G
	move.l	a0,a4
	moveq	#0,d3
.C	addq.l	#1,d3			;look for / or end
	tst.b	(a4)
	beq.s	.E
	cmp.b	#":",(a4)
	beq.s	.fail
	cmp.b	#"/",(a4)+
	bne.s	.C
.E	tst.b	myf_Type(a1)
	bmi.s	.fail
	move.l	a1,d2
	bsr	SearchEntry		;search in dir
	bne.s	.F
	tst.l	d7			;check for CreatePath
	beq.s	.G
	movem.l	d1-d3/a0/a2-a4,-(sp)
	move.l	d2,a3
	move.l	a0,a2
	move.l	a0,a4
	subq.l	#1,d3
	add.l	d3,a4
	move.b	(a4),d3
	clr.b	(a4)
	moveq	#0,d2
	bsr	CreateNewObj		;create new dir
	move.b	d3,(a4)
;	move.l	d0,a1
	movem.l	(sp)+,d1-d3/a0/a2-a4
	tst.l	d0
	beq.s	.I
	clr.b	myf_Type(a1)
.F	move.l	a4,a0
	bra.s	.A			;continue looking for / or end
.G	move.l	a1,d0
.fail
	IFD	DEBUG
	lea	TempPath(a5),a0
	moveq	#0,d2
	tst.l	d0
	beq.s	.\@I
	move.b	myf_Type(a1),d2
.\@I	DBUG3	txfnd,a0,d0,d2
	ENDC

.I	movem.l	(sp)+,d2-d7/a2-a4
	tst.l	d0
	rts

* Look for object in dir
* ENTRY: a0=file/dir name, a1=ptr to dir, d3=length of name
* EXIT:  a1=ptr to file or 0, Z-flag
SearchEntry:
	moveq	#0,d5
	moveq	#0,d6
	move.l	myf_First(a1),d0
	sub.l	a1,a1
.C	move.l	a1,d1
	move.l	d0,a1
	tst.l	d0
	beq.s	.fail
	move.l	myf_Link(a1),d0
	lea	myf_Name(a1),a2
	move.l	a0,a3
	move.l	d3,d4
	bra.s	.E
.A	move.b	(a2)+,d5
	move.b	CharTable(a5,d5.l),d5
	move.b	(a3)+,d6
	move.b	CharTable(a5,d6.l),d6
	cmp.b	d5,d6
	bne.s	.C
.E	subq.l	#1,d4
	bne.s	.A
	tst.b	(a2)
	bne.s	.C

	IFD	DEBUG
	lea	myf_Name(a1),a3
	DBUG1	txentry,a3
	ENDC

.fail	move.l	a1,d0
	rts

* Splits path with file into path=TempPath and file=TempName
* ENRTY: TempPath=file with path
* EXIT:  TempPath=path, TempName=file
SplitPath
	move.l	a2,-(sp)
	lea	TempPath(a5),a0
	move.l	a0,a1
.A	tst.b	(a0)+
	bne.s	.A
.D	cmp.b	#"/",-(a0)
	beq.s	.C
	cmp.b	#":",(a0)
	beq.s	.C
	cmp.l	a0,a1
	bne.s	.D
	move.l	a0,a1
	bra.s	.G
.C	move.l	a0,a1
	addq.l	#1,a1
.G	lea	TempName(a5),a2
.E	move.b	(a1)+,(a2)+
	bne.s	.E
	cmp.b	#":",(a0)
	bne.s	.F
	addq.l	#1,a0
.F	clr.b	(a0)
	move.l	(sp)+,a2

	IFD	DEBUG
	lea	TempPath(a5),a0
	lea	TempName(a5),a1
	DBUG2	txsplit,a0,a1
	ENDC

	rts

* Evaluate full path name of object
* Only used by CalcFullName
* ENTRY: a1=object
* EXIT:  a0=full name (root name not included) in TempPath
GetFullName:
	move.l	d2,-(sp)
	lea	TempPath+MAXPATH+MAXNAME-1(a5),a0
	clr.b	(a0)
	move.w	#MAXPATH+MAXNAME-1,d2
.A	move.l	myf_Parent(a1),d0
	beq.s	.ok
	lea	myf_Name(a1),a1
	moveq	#-1,d1
.C	addq.l	#1,d1
	tst.b	(a1)+
	bne.s	.C
	subq.l	#1,a1
	sub.w	d1,d2
	bmi.s	.ok
	tst.b	(a0)
	beq.s	.D
	move.b	#"/",-(a0)
	subq.w	#1,d2
	bra.s	.D
.E	move.b	-(a1),-(a0)
.D	dbra	d1,.E
	move.l	d0,a1
	bra.s	.A
.ok	move.l	(sp)+,d2
	rts

* Create dir and copy file from ENVARC: if possible
* Only used by LocateObject and UpNoti
* ENTRY: TempPath=name, d0=BPTR parent lock
* EXIT: d0=file or ZERO
CalcFullName:
.regs	reg	d2-d7/a2-a4
	movem.l	.regs,-(sp)
	moveq	#0,d7			;d7=return code
	tst.b	WriteProtected(a5)
	bne	.fail

	IFND	rom
	tst.l	StupNocopy(a5)
	bne	.fail
	ENDC

	move.l	d0,d2		;BPTR to parent lock
	bsr	SearchPath
	bne	.found
	move.l	d2,d0
	bsr	CreatePath
	beq	.fail
.found	move.l	d0,a1
	move.l	d0,a2
	bsr	GetFullName
	DBUG1	txfull,a0
	lea	TempPath(a5),a1
	move.l	#MAXPATH-2,d0
	bsr	CopyChar2BSTR
	move.l	d0,d5			;d5=BSTR full name
	lea	(MyMsg,a5),a1
	movem.l	d5/a2,(MN_SIZE,a1)
	movea.l	(DosPort,a5),a0
	sys	PutMsg
	movea.l	(PacketPort,a5),a0
	sys	WaitPort	
	movea.l	(PacketPort,a5),a0
	sys	GetMsg
	movea.l	d0,a0
	move.l	(MN_SIZE,a0),d7
	beq	.fail1
	move.l	(MN_SIZE+4,a0),(dp_Res2,a4)
	movea.l	d7,a1
	lea	(MyFIB,a5),a0
	move.b	(fib_Protection+3,a0),(myf_Attrs,a1)
	lea	(fib_DateStamp,a0),a0
	lea	(myf_Date,a1),a1
	move.l	(a0)+,(a1)+
	move.l	(a0)+,(a1)+
	move.l	(a0)+,(a1)+
	cmpa.l	d7,a2			;if dir they're equal
	beq	.ok
.fail1	move.l	(dp_Res2,a4),d2
	move.l	a2,a1
	bsr	DeleteObj
	move.l	d2,(dp_Res2,a4)
	tst.l	d7
	beq	.fail
.ok	bsr	UpdateNotify
.fail	move.l	d7,d0
	movem.l	(sp)+,.regs
_perr	rts

* code of special sub-process for synchronous dos calls follows -MM

procc:	movea.l	(4).w,a6
	sys	CreateMsgPort
	move.l	d0,d7
	beq	_perr
	move.l	#1<<15,d0		;wait for CTRL-F
	sys	Wait
	suba.l	a1,a1
	sys	FindTask
	movea.l	d0,a4
	movea.l	(TC_Userdata,a4),a5
	lea	(pr_MsgPort,a4),a4
	move.l	d7,(DosPort,a5)
	movea.l	(PacketPort,a5),a0
	moveq	#1,d0
	move.b	(MP_SIGBIT,a0),d1
	movea.l	(MP_SIGTASK,a0),a1
	lsl.l	d1,d0
	sys	Signal		;signal parent we're ready

.mloop	movea.l	d7,a0
	sys	WaitPort
	movea.l	d7,a0
	sys	GetMsg
	movea.l	d0,a3
	lea	(MN_SIZE,a3),a0
	move.l	(a0)+,d4	;BSTR of full object's name
	beq	.quit
	movea.l	(a0),a2		;object

*-----------------------------------------------------
* perform synchronous DOS IO
* in: d4=BSTR of full object's name, a2 - object
* out: d0=new object or NULL, d5=NULL if read successfull, dp_Res2 otherwise

.regs	reg	d7/a3/a6
	movem.l	.regs,-(sp)
	movea.l	(DosBase,a5),a6
	moveq	#0,d6
	moveq	#0,d7

.loop	lea	(envarc,pc),a0
	move.l	a0,d1
	move.l	d6,d2
	sys	GetDeviceProc
	move.l	d0,d6
	beq	.fail
	move.l	d6,a4
	move.l	(dvp_Port,a4),d1		;port
	moveq	#ACTION_LOCATE_OBJECT,d2	;action
	move.l	(dvp_Lock,a4),d3		;arg1
	moveq	#ACCESS_READ,d5			;arg3
	sys	DoPkt
	move.l	d0,d3				;d3=lock on object
	beq	.loop				;not found

	move.l	d3,d1
	lea	(MyFIB,a5),a3
	move.l	a3,d2
	sys	Examine
	move.l	d0,-(sp)
	move.l	d3,d1
	sys	UnLock
	move.l	(sp)+,d0
	beq	.fail1
	moveq	#0,d5
	tst.l	(fib_DirEntryType,a3)
	bpl	.ok

	move.l	(dvp_Port,a4),d1		;port
	move.l	#ACTION_FINDINPUT,d2		;action
	move.l	d4,d5				;arg3
	move.l	(dvp_Lock,a4),d4		;arg2
	lea	(MyFH,a5),a0
	move.l	a0,d3
	lsr.l	#2,d3				;arg1
	sys	DoPkt
	tst.l	d0
	beq	.fail1
	movem.l	a2/a3/a6,-(sp)
	move.l	(fib_Size,a3),d2
	movea.l	(myf_Parent,a2),a3
	lea	(myf_Name,a2),a2
	movea.l	(ExecBase,a5),a6
	bsr	CreateNewObj
	movem.l	(sp)+,a2/a3/a6
	beq.s	.fail2
	move.l	d0,d4
	add.l	d1,d4			;arg2 (buffer)
	movea.l	d0,a2
	move.b	#$a0,(myf_Type,a2)
	move.l	(fib_Size,a3),d5	;arg3 (size to read)
	moveq	#ACTION_READ,d2		;action
	lea	(MyFH,a5),a0
	move.l	(fh_Arg1,a0),d3		;arg1
	move.l	(dvp_Port,a4),d1	;port
	sys	DoPkt
	sub.l	d0,d5
	beq	.fail2
	move.l	d1,d5			;secondary result
.fail2	move.l	(dvp_Port,a4),d1	;port
	move.l	#ACTION_END,d2		;action
	sys	DoPkt
.ok	move.l	a2,d7
.fail1	move.l	d6,d1
	sys	FreeDeviceProc
.fail	move.l	d7,d0
	movem.l	(sp)+,.regs
*-----------------------------------------------------

	move.l	d0,(MN_SIZE,a3)		;primary result
	move.l	d5,(MN_SIZE+4,a3)	;secondary result
	movea.l	a3,a1
	sys	ReplyMsg
	bra	.mloop
	
.quit	movea.l	d7,a0
	sys	DeleteMsgPort
	movea.l	a3,a1
	sysj	ReplyMsg
	
*******************************************************

_romtag	dc.w	$4AFC
	dc.l	_romtag	;rt_MatchTag
	dc.l	_end	;rt_EndSkip
	dc.b	RTF_AFTERDOS	;rt_Flags
	dc.b	37	;rt_Version
	dc.b	0	;rt_Type
	dc.b	-124	;rt_Pri
	dc.l	_name	;rt_Name
	dc.l	_id	;rt_IdString
	dc.l	_init	;rt_Init

_name	dc.b	'env-handler'
_ns=*-_name
	dc.b	0
	dc.b	'$VER: '
_id	dc.b	'HappyENV-Handler '

	ifnd	test
	$VER
	elseif
	dc.b	'TEST'
	endc

	dc.b	' ('
	ifnd	test
	fdate
	else
	mydate
	endc
	dc.b	')',10,13,0


_init	movem.l	a2/d6/d7,-(sp)
	moveq	#0,d7
	moveq	#DeviceNode_SIZEOF+8+13,D0
	move.l	#MEMF_PUBLIC|MEMF_CLEAR,D1
	sys	AllocMem
	tst.l	D0
	beq	.skip
	movea.l	D0,A2
	lea	(DeviceNode_SIZEOF,a2),a1	create BCPL 'ENV' string (5 bytes)
	moveq	#3,d0
	lea	(envarc,pc),a0
	bsr	CopyChar2BSTR
	move.l	d0,(dn_Name,a2)
	lea	(DeviceNode_SIZEOF+8,a2),a1	create BCPL handler string (11 bytes)
	lea	(_name,pc),a0
	moveq	#_ns,d0
	bsr	CopyChar2BSTR
	move.l	d0,(dn_Handler,a2)

	IFD	rom
	moveq	#OLTAG_EXPANSION,d0
	sys	TaggedOpenLibrary
	ELSE
	lea	(_expn,pc),a1
	moveq	#37,d0
	sys	OpenLibrary
	ENDC

	move.l	d0,d6
	beq.b	.fail
	exg	d6,a6
	lea	(start,pc),a0
	subq.l	#4,a0
	move.l	a0,d0		;init

	IFND	rom
	move.l	(a0),d1
	beq	.ns
	addq.l	#1,d1
	move.l	d1,(dn_Startup,a2)
	ENDC

.ns	asr.l	#2,d0
	move.l	d0,(dn_SegList,a2)
	move.w	#2000,(dn_StackSize+2,A2)
	not.l	(dn_GlobalVec,A2)	;globvec -1
	moveq	#5,D0
	move.l	D0,(dn_Priority,a2)
	moveq	#-128,D0	;bootpri = do not boot at all
	moveq	#0,D1
	suba.l	a1,a1
	movea.l	a2,a0
	sys	AddBootNode
	move.l	d0,d7
.fn	movea.l	a6,a1
	movea.l	d6,A6
	sys	CloseLibrary
	tst.l	d7
	bne	.skip
.fail	movea.l	a2,a1
	moveq	#DeviceNode_SIZEOF+8+13,d0
	sys	FreeMem
.skip	move.l	d7,d0
	movem.l	(sp)+,a2/d6/d7
	rts
_end

*******************************************************

*******************************************************

* Debug output support subroutines and strings following

	IFD	DEBUG

* Output one character
RawHook	move.l	a6,-(sp)
	move.l	4.w,a6
	jsr	_LVORawPutChar(a6)
	move.l	(sp)+,a6
	rts

* Wait for CTRL-C signal
wait_c:
	movem.l	d0-d1/a0-a1,-(sp)
	moveq	#0,d0
	moveq	#0,d1
	bset	#12,d1
	jsr	_LVOSetSignal(a6)
	moveq	#0,d0
	bset	#12,d0
	jsr	_LVOWait(a6)	;wait for CTRL-C
	movem.l	(sp)+,d0-d1/a0-a1
	rts

	IFD	VERBOSE
txstart	dc.b	"Handler started, A5=$%lx.",10,0
txend	dc.b	"Handler ended, FileSize=%ld, TotalSize=%ld, Locks=%ld..",10,0
txcopy	dc.b	"Copyfrom=$%lx.",10,0
txpack	dc.b	"Type=%ld Arg1=$%lx Arg2=$%lx Arg3=$%lx, Task=%s.",10,0
txres	dc.b	"Result1=$%lx, Result2=%ld.",10,0
txpkt	dc.b	"  Packet=$%lx, type=%ld, res=$%lx, err=%ld.",10,0
txerr1	dc.b	"  %ld: Unknown Action.",10,0

txopeno	dc.b	"  Oldopen lock=$%lx.",10,0
txopenn	dc.b	"  Newopen lock=$%lx.",10,0
txcreat	dc.b	"  Created file %s=$%lx size=%ld.",10,0
txread	dc.b	"  Read %ld of %ld bytes, pos=%ld.",10,0
txwrite	dc.b	"  Wrote %ld bytes, pos=%ld, size=%ld.",10,0
txwins	dc.b	"  Inserted %ld bytes at %ld.",10,0
txchng	dc.b	"  Changed mode, pos=%ld, size=%ld.",10,0
txseek	dc.b	"  Seeked, filepos=%ld.",10,0
txdel	dc.b	"  Deleted $%lx.",10,0

txlock	dc.b	"  Locked %s=$%lx mode %ld.",10,0
txulock	dc.b	"  Unlocked $%lx.",10,0
txexam	dc.b	"  Examined file=$%lx.",10,0
txexnx	dc.b	"  Examined next file=$%lx.",10,0
txfnd	dc.b	"    Found %s=$%lx type=$%lx.",10,0
txentry	dc.b	"    Found entry %s.",10,0
txsplit	dc.b	"    Splitted ->%s,%s.",10,0
txfull	dc.b	"    Full name: %s.",10,0
txexp	dc.b	"    Expanded path to %s.",10,0

txanoti	dc.b	"  Added noti $%lx->$%lx on %s key=$%lx.",10,0
txrnoti	dc.b	"  Removed noti $%lx->$%lx.",10,0
txcnoti	dc.b	"  Checking noti on %s.",10,0
txunoti	dc.b	"    Updated noti on %s key=$%lx.",10,0
txsnoti	dc.b	"    Sent signal to %s for %s.",10,0
txmnoti	dc.b	"    Sent message $%lx to %s for %s.",10,0
txgnoti	dc.b	"    Got message $%lx.",10,0
	ENDC

	IFND	VERBOSE
txstart	dc.b	"Handler started, A5=$%lx.",10,0
txend	dc.b	"Handler ended, FileSize=%ld, TotalSize=%ld, Locks=%ld.",10,0
txcopy	dc.b	"Copyfrom=$%lx.",10,0
txpack	dc.b	"Type=%ld Arg1=$%lx Arg2=$%lx Arg3=$%lx, Task=%s.",10,0
txres	dc.b	"Result1=$%lx, Result2=%ld.",10,0
txpkt	dc.b	0;"  Packet=$%lx, type=%ld, res=$%lx, err=%ld.",10,0
txerr1	dc.b	"  %ld: Unknown Action.",10,0
txargs	dc.b	"Args: $%lx, $%lx, $%lx, $%lx.",10,0

txopeno	dc.b	"  Oldopen lock=$%lx.",10,0
txopenn	dc.b	"  Newopen lock=$%lx.",10,0
txcreat	dc.b	"  Created file %s=$%lx size=%ld.",10,0
txread	dc.b	0;"  Read %ld of %ld bytes, pos=%ld.",10,0
txwrite	dc.b	0;"  Wrote %ld bytes, pos=%ld, size=%ld.",10,0
txwins	dc.b	0;"  Inserted %ld bytes at %ld.",10,0
txchng	dc.b	0;"  Changed mode, file=$%lx, pos=%ld, size=%ld.",10,0
txsize	dc.b	0;"  Changed size, file=$%lx, oldsize=%ld, newsize=%ld.",10,0
txseek	dc.b	0;"  Seeked, filepos=%ld.",10,0
txdel	dc.b	"  Deleted $%lx.",10,0

txlock	dc.b	0;"  Locked %s=$%lx mode %ld.",10,0
txulock	dc.b	0;"  Unlocked $%lx.",10,0
txexam	dc.b	0;"  Examined file=$%lx.",10,0
txexan	dc.b	0;"    Examined %s.",10,0
txexnx	dc.b	0;"  Examined next file=$%lx.",10,0
txfnd	dc.b	0;"    Found %s=$%lx type=$%lx.",10,0
txentry	dc.b	0;"    Found entry %s.",10,0
txsplit	dc.b	0;"    Splitted ->%s,%s.",10,0
txfull	dc.b	0;"    Full name: %s.",10,0
txexp	dc.b	"    Expanded path to %s.",10,0

txanoti	dc.b	"  Added noti $%lx->$%lx on %s key=$%lx.",10,0
txrnoti	dc.b	"  Removed noti $%lx->$%lx.",10,0
txcnoti	dc.b	0;"  Checking noti on %s.",10,0
txunoti	dc.b	0;"    Updated noti on %s key=$%lx.",10,0
txsnoti	dc.b	0;"    Sent signal to %s for %s.",10,0
txmnoti	dc.b	0;"    Sent message $%lx to %s for %s.",10,0
txgnoti	dc.b	0;"    Got message $%lx.",10,0
	ENDC

	ENDC
