* ------------------------------------------------------------------------- *
* FS2.ASM -- Assembly support routines for PathMaster(tm) File Selector
* Copyright © 1987-1989 by Justin V. McCormick.  All Rights Reserved.
* ------------------------------------------------------------------------- *

;BENCHMARK	EQU	1

	include "exec/types.i"
	include "exec/nodes.i"
	include "exec/lists.i"

SYS	MACRO	*
	IFGT	NARG-2
	FAIL	!!!
	ENDC
	IFEQ	NARG-2
	MOVE.L	\2,a6
	ENDC
	JSR	_LVO\1(a6)
	ENDM

XLVO	MACRO	*
	XREF	_LVO\1
	ENDM


* These three equate control the size of the path, file, and pattern string limits
PATHSTRSIZE	EQU	300
FILESTRSIZE	EQU	32
MATCHSTRSIZE	EQU	32

AUTOKNOB	EQU	$1
FREEVERT	EQU	$4
PROPBORDERLESS	EQU	$8
MEMF_CLEAR 	EQU	$10000
WINDOWSIZING	EQU	$1
WINDOWDRAG	EQU	$2
WINDOWDEPTH	EQU	$4
WINDOWCLOSE	EQU	$8
WINDOWTGADS	EQU	WINDOWDRAG!WINDOWDEPTH!WINDOWCLOSE
FPEN		EQU	1
DPEN		EQU	3

fib_DirEntryType EQU	$4
fib_FileName	EQU	$8
fib_Size	EQU	$7C
fib_DateStamp	EQU	$84
fib_SIZEOF	EQU	$104

gg_Flags	EQU	$C
id_SIZEOF	EQU	$24
it_FrontPen	EQU	$0
it_IText	EQU	$C
rp_AreaPtrn	EQU	$8
rp_AreaPtSz	EQU	$1D
wd_Flags	EQU	$18
wd_RPort	EQU	$32

	STRUCTURE FSRequest,0
	STRUCT	fs_dirname,PATHSTRSIZE
	STRUCT	fs_filename,FILESTRSIZE
	STRUCT	fs_matchpattern,MATCHSTRSIZE
	WORD	fs_leftedge
	WORD	fs_topedge
	WORD	fs_sorttype
	UWORD	fs_flags
	ULONG	fs_windowflags
	APTR	fs_fullname
	APTR	fs_ignorepattern
	APTR	fs_pathgadstr
	APTR	fs_filegadstr
	APTR	fs_titlestr
	APTR	fs_readingstr
	APTR	fs_sortingstr
	APTR	fs_emptydirstr
	APTR	fs_nomatchstr
	APTR	fs_selectfilestr
	APTR	fs_selectstr
	APTR	fs_cancelstr
	APTR	fs_specgadstr
	APTR	fs_userscreen
	APTR	fs_userport
	APTR	fs_specgadfunc
	APTR	fs_dirfunc
	APTR	fs_filefunc
	APTR	fs_userfunc
	APTR	fs_initfunc
	APTR	fs_matchfunc
	LABEL	fs_SIZEOF

	STRUCTURE node_data,0
	STRUCT	nd_alphadata,58
	WORD	nd_filetype
	WORD	nd_showit
	WORD	nd_textcolor
	WORD	nd_namelength
	ULONG	nd_filesize
	ULONG	nd_days
	ULONG	nd_minutes
	ULONG	nd_ticks
	LABEL	nd_SIZEOF

	STRUCTURE file_node,0
	STRUCT	fn_Node,MLN_SIZE
	APTR	fn_info
	WORD	fn_idnum
	LABEL	fn_SIZEOF

	STRUCTURE dev_node,0
	STRUCT	dn_Node,MLN_SIZE
	STRUCT	dn_name,32
	LABEL	dn_SIZEOF

	XREF	_AlphaGad
	XREF	_ColonStr
	XREF	_CurDevNames
	XREF	_DevGads
	XREF	_devList
	XREF	_DevTxts
	XREF	_DOSBase
	XREF	_EmptyPattern
	XREF	_FileUndoBuffer
	XREF	_FileUndoName
	XREF	_fnList
	XREF	_fsbaddatestr
	XREF	_FSCountDown
	XREF	_fscurmicros
	XREF	_fscursecs
	XREF	_fsdatefmtstr
	XREF	_fsdayspermonth
	XREF	_FSDirFmtStr
	XREF	_fsdirlocked
	XREF	_FSDone
	XREF	_FSDownGad
	XREF	_fserrmsgs
	XREF	_FSFib
	XREF	_FSFileGad
	XREF	_FSFileInfo
	XREF	_FSFileNodes
	XREF	_fsflags
	XREF	_FSIgnorePat
	XREF	_FSInfo
	XREF	_FSLock
	XREF	_fsneedshow
	XREF	_FSNextDevs
	XREF	_fsnumentries
	XREF	_fsoldmicros
	XREF	_fsoldsecs
	XREF	_FSPathBuf
	XREF	_FSPathGad
	XREF	_FSPathInfo
	XREF	_FSPatternGad
	XREF	_FSPatternInfo
	XREF	_FSPatternUndoBuffer
	XREF	_FSPrevDevs
	XREF	_fsprevgadid
	XREF	_FSReq
	XREF	_FSRPort
	XREF	_FSSizeFmtStr
	XREF	_FSSlideGad
	XREF	_FSSlideProp
	XREF	_fsstartedstr
	XREF	_fstempdate
	XREF	_fstitlelength
	XREF	_fstitstatus
	XREF	_FSUndoGad
	XREF	_FSUpGad
	XREF	_FSUserGad
	XREF	_fsvheight
	XREF	_fsvirgindir
	XREF	_fsvposition
	XREF	_FSWin
	XREF	_FSWinTitleStr
	XREF	_FSwstr
	XREF	_GfxBase
	XREF	_IntuitionBase
	XREF	_lastdev
	XREF	_NIL2Gad
	XREF	_OldFSPathBuf
	XREF	_RamDirNameStr
	XREF	_SizeGad
	XREF	_SlashStr
	XREF	_TimeGad
	XREF	_topfin

	XREF	_sprintf
	XREF	_FSVBDelay

	IFD	BENCHMARK
	XREF	_StartTime
	XREF	_StopTime
	XREF	_timermsg1
	ENDC
	
	XLVO	ActivateGadget
	XLVO	AddHead
	XLVO	AddTail
	XLVO	AllocMem
	XLVO	ClipBlit
	XLVO	DisplayBeep
	XLVO	DoubleClick
	XLVO	Examine
	XLVO	ExNext
	XLVO	FreeMem
	XLVO	GetMsg
	XLVO	Insert
	XLVO	IoErr
	XLVO	Move
	XLVO	NewModifyProp
	XLVO	ParentDir
	XLVO	RectFill
	XLVO	RefreshGList
	XLVO	RemHead
	XLVO	ReplyMsg
	XLVO	SetAPen
	XLVO	SetDrMd
	XLVO	SetWindowTitles
	XLVO	Text
	XLVO	UnLock

	SECTION	CODE
* ------------------------------------------------------------------------
; LONG MyActivateGad(gadget, window)
;   struct Gadget *gadget;
;   struct Window *window;
;   
; Activate a gadget, wait till it is ready.  Try up to 3 times.  Returns
; TRUE (1) if it succeeded, FALSE (0) if it failed to activate.
* ------------------------------------------------------------------------
	XDEF	_MyActivateGad
_MyActivateGad:
	movem.l	d2/a2/a6,-(sp)
	moveq	#2,d2			;Try 3 times to activate gadget

1$	movem.l	16(sp),a0/a1		;Grab gadget pointer, window pointer
	suba.l	a2,a2			;No requester pointer
	movea.l	_IntuitionBase,a6
	jsr	_LVOActivateGadget(a6)	;Activate!
	tst.l	d0			;Did it catch?
	 bne.s	2$			;Yep, move on

	pea	5
	jsr	_FSVBDelay
	addq.w	#4,sp
	dbf	d2,1$
2$
	movem.l	(sp)+,d2/a2/a6
	rts

* --------------------------------------------------------------------- *
* LONG FSCheckFlagChange(VOID);
* --------------------------------------------------------------------- *
	XDEF	_FSCheckFlagChange
_FSCheckFlagChange:

	movea.l	_FSReq,a0
	move.w	fs_flags(a0),d0
	move.w	_fsflags,d1
	andi.w	#%00011000,d0
	andi.w	#%00011000,d1
	cmp.w	d0,d1			;FS_SHOW_FILES_FIRST or FS_SHOW_DIRS_FIRST changed?
	 beq.s	1$			;Nope
	move.w	#1,_fsvirgindir	;Else we need to re-read the directory
1$
	move.w	fs_flags(a0),d0
	move.w	_fsflags,d1
	andi.w	#%01100000,d0
	andi.w	#%01100000,d1
	cmp.w	d0,d1			;FS_SHOW_FILES_ONLY or FS_SHOW_DIRS_ONLY changed?
	 beq.s	2$			;Nope
	moveq	#1,d0			;Else flag that we need to FSMatchPattern()
	bra.s	3$
2$
	moveq	#0,d0
3$
	move.w	fs_flags(a0),_fsflags ;Reset flags to match FSReq now
	rts

* ------------------------------------------------------------------------
; Assign device name texts to intuitexts in the 4 Device Gadgets
* ------------------------------------------------------------------------
	XDEF	_SetDevGads
_SetDevGads:
	movem.l	d3-d4/a2-a3/a5,-(sp)

;for (i = 0, tnode = lastdev; i < 4;)
	moveq	#0,d4			;d4 = i = 0
	movea.l	_lastdev,a2		;a2 = tnode = lastdev
	move.l	a2,d3			;Copy for comparison
	lea	_CurDevNames,a3	;a3 = &CurDevNames[0]
	lea	_DevTxts,a5		;a5 = &DevTxts[0]

1$
	lea	dn_name(a2),a1
	move.l	a1,0(a3,d4.w)		;CurDevNames[i] = tnode->name

	moveq	#4,d0
	move.l	0(a5,d4.w),a0
	movea.l	it_IText(a0),a0
	jsr	astrncpy		;astrncpy (DevTxts[i]->IText, tnode->name, 4)

	addq.w	#4,d4			;i++

	movea.l	dn_Node+MLN_SUCC(a2),a2	;tnode = tnode->dn_Node.mln_Succ
	tst.l	dn_Node+MLN_SUCC(a2)	;if (tnode->dn_Node.mln_Succ == 0)
	 bne.s	2$			;Nope
	movea.l	_devList,a2
	movea.l	MLH_HEAD(a2),a2		;Else start the listing over at the start
2$
	cmpa.l	d3,a2			;Have we already shown this node?
	 beq.s	3$			;Yep, disable the rest

	cmpi.w	#16,d4			;Set all 4 Device gadgets yet?
	 bcs	1$			;Nope, do next

3$
* Disable any leftover gadgets
;for (; i < 4; i++)
	lea	_DevGads,a0
	bra.s	5$

4$	movea.l	0(a0,d4.w),a1
	bset.b	#0,gg_Flags(a1)		;DevGads[i]->Flags |= GADGDISABLED
	addq.w	#4,d4

5$	cmpi.w	#16,d4
	 bcs	4$

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

* ------------------------------------------------------------------------
; FSScrollDevGads(direction)
;   WORD direction;
* ------------------------------------------------------------------------
	XDEF	_FSScrollDevGads
_FSScrollDevGads:
	movem.l	a2/a6,-(sp)

	movea.l	_lastdev,a0
	tst.w	12+2(sp)		;Grab scroll direction
	 beq.s	1$			;If zero, scroll to next

* Bump list pointer one direction or the other
	move.l	dn_Node+MLN_SUCC(a0),a0	;Get successor
	tst.l	dn_Node+MLN_SUCC(a0)	;EndoList?
	 bne.s	2$			;Nope
	movea.l _devList,a0
	movea.l	MLH_HEAD(a0),a0		;Else lastdev = devList->lh_Head
	bra.s	2$
1$
	movea.l	dn_Node+MLN_PRED(a0),a0 ;Else, lastdev = lastdev->next
	tst.l	dn_Node+MLN_PRED(a0)	;Endolist?
	 bne.s	2$
	movea.l _devList,a0
	movea.l	MLH_TAILPRED(a0),a0	;Else lastdev = devList->lh_TailPred
2$
	move.l	a0,_lastdev
	jsr	_SetDevGads		;Set the gadget text fields

* Refresh gadget texts
;RefreshGList(&NIL2Gad, FSWin, 0L, 4L);
	moveq	#4,d0
	suba.l	a2,a2
	movea.l	_FSWin,a1
	lea	_NIL2Gad,a0
	SYS	RefreshGList,_IntuitionBase

	movem.l	(sp)+,a2/a6
	rts

* ------------------------------------------------------------------------
; CheckFSArrows()
;  See if the Up or Down arrow gadgets are currently activated, scroll
; the file display area or device gadget list if so.
* ------------------------------------------------------------------------
	XDEF	_CheckFSArrows
_CheckFSArrows:
	subq.w	#1,_FSCountDown
	 bge.s	12$

	btst.b	#7,_FSUpGad+gg_Flags+1	;FSUpGad.Flags & SELECTED?
	 beq.s	2$			;Nope, maybe FSDownGad
	pea	1			;Otherwise scroll filegadgets up
	bra.s	3$

2$	btst.b	#7,_FSDownGad+gg_Flags+1 	;FSDownGad.Flags & SELECTED?
	 beq.s	4$			;Nope, maybe slide gadget is selected
	pea	0			;Otherwise scroll filegadgets down

3$	jsr	_FSScrollFileGads
	addq.w	#4,sp
	move.w	#0,_FSCountDown	;Set timer to wake up in .02 secs.
	bra.s	12$

4$	btst.b	#7,_FSSlideGad+gg_Flags+1	;FSSlideGad.Flags & SELECTED?
	 beq.s	5$			;Nope, maybe UpDev
	jsr	_FSDoSlideGadget	;Process slide gadget motion
	move.w	#0,_FSCountDown	;Set timer to wake up in .02 secs.
	bra.s	12$

5$	btst.b	#7,_FSPrevDevs+gg_Flags+1	;FSPrevDevs.Flags & SELECTED?
	 beq.s	7$			;Nope, maybe DownDev
	pea	1			;Otherwise FSScrollDevGads(1);
6$	jsr	_FSScrollDevGads
	addq.w	#4,sp
	move.w	#1,_FSCountDown	;Set timer to wake up in .16 secs.
	bra.s	12$

7$	btst.b	#7,_FSNextDevs+gg_Flags+1	;FSNextDevs.Flags & SELECTED?
	 beq.s	10$			;Nope, nothing of interest, slow down
	pea	0			;Else FSScrollDevGads(0);
	bra	6$

10$
	move.w	#4,_FSCountDown
12$
	rts

* ------------------------------------------------------------------------
; BYTE *FibFileDate(fib_date)
;   struct DateStamp *fib_date;
;
;   Calculate date based on DateStamp structure and return a pointer
; to the formatted date text.
* ------------------------------------------------------------------------
	XDEF	_FibFileDate
_FibFileDate:
	link	a5,#0
	movem.l	d3-d7,-(sp)

	movea.l	8(a5),a1		;Grab datestamp pointer
	moveq	#78,d7			;Initial year = 1978
	
	move.l	(a1),d5			;days = fib_date->ds_Days
	 blt	ffdbaddate		;Hey! you can't be negative! Invalid date...

* Determine what year it is
	divu	#1461,d5
	move.l	d5,d0			;Stash it
	ext.l	d5
	lsl.l	#2,d5
	add.l	d5,d7			;year += (days / 1461) * 4

* Count how many months into that year
ffdgetmo:
	swap	d0
	moveq	#0,d5
	move.w	d0,d5			;days %= 1461

1$	tst.w	d5			;Out of days yet?
	 beq.s	3$			;Yep, done here

	move.w	#365,d6			;Else month_days = 365
	move.w	d7,d0			;Grab year
	andi.w	#3,d0			;if (year & 3) == 0 Leap year?
	 bne.s	2$			;Nope
	addq.w	#1,d6			;Otherwise bump month_days

2$	cmp.w	d6,d5			;is day < month_days?
	 bcs.s	3$			;yep, done here
	sub.w	d6,d5			;otherwise day -= month_days

	addq.l	#1,d7			; year++
	bra	1$
3$

* Count how many days into that month of that year
ffdgetday:
;for (i = 0, day++; i < 12; i++)
	moveq	#0,d4			;current month = 0
	moveq	#0,d6			;Zap hinybs
	addq.w	#1,d5
	lea	_fsdayspermonth,a0

1$	move.b	0(a0,d4.w),d6		;month_days = _fsdayspermonth[i]

	cmpi.w	#1,d4			;if (i == 1 && (year & 3) == 0)
	 bne.s	2$
	move.w	d7,d0
	andi.w	#3,d0
	 bne.s	2$
	addq.w	#1,d6			;month_days++

2$	cmp.w	d6,d5			;if (day <= month_days)
	 ble.s	4$			;Break out, found the right month

	sub.w	d6,d5			;Else, day -= month_days

	addq.w	#1,d4			;i++
3$	cmpi.w	#12,d4			;Done all months yet?
	 bcs	1$			;Nope

4$
ffdprint:
1$	cmpi.l	#99,d7			;while (year >= 100)
	 ble.s	2$
	subi.l	#100,d7			;year -= 100
	bra	1$
2$
;sprintf(_fstempdate, "%02ld-%02ld-%02ld %02ld:%02ld:%02ld", year, i + 1, day, hour, min, sec)
	move.l	8(a1),d0		;sec = fib_date->ds_Tick / 50;
	divu.w	#50,d0
	ext.l	d0
	move.l	d0,-(sp)		;Push secs

	moveq	#0,d0			;Zap reg
	move.w	6(a1),d0		;min = fib_date->ds_Minute
	move.w	d0,d1			;Clone it
	divu	#60,d0
	move.w	d0,d3			;hour = min / 60
	ext.l	d3
	mulu	#60,d0
	sub.w	d0,d1			;min -= hour * 60
	ext.l	d1
	move.l	d1,-(sp)		;Push mins

	move.l	d3,-(sp)		;Push hours
	move.l	d5,-(sp)		;Push day of month
	addq.w	#1,d4			;Push month (offset by 1!)
	move.l	d4,-(sp)
	move.l	d7,-(sp)		;Push year
	pea	_fsdatefmtstr	;Push the format pattern
	pea	_fstempdate		;Push destination buffer
	jsr	_sprintf	
	lea	32(sp),sp
	lea	_fstempdate,a0
	move.l	a0,d0			;return((BYTE *)&_fstempdate[0])

ffddone:
	movem.l	(sp)+,d3-d7
	unlk	a5
	rts

ffdbaddate:
	lea	_fsbaddatestr,a0
	move.l	a0,d0			;return (" <Invalid Date> ")
	bra	ffddone

* --------------------------------------------------------------------- *
	XDEF	_FSSetKnobHeight
_FSSetKnobHeight:
;vheight = (ULONG)( 655350L / (long)fsnumentries );
	move.w	_fsnumentries,d1
	cmpi.w	#10,d1
	 bhi.s	1$
	move.w	#$ffff,_fsvheight
	rts
1$
	move.l	#655350,d0	
	divu	d1,d0
	move.w	d0,_fsvheight
3$
	rts

* --------------------------------------------------------------------- *
	XDEF	_FSSetKnobPos
_FSSetKnobPos:
	movea.l	_topfin,a0
	moveq	#0,d0
	move.w	fn_idnum(a0),d0
	swap	d0
	move.w	_fsnumentries,d1
	subi.w	#10,d1
	 bhi.s	1$
	moveq	#0,d0
	bra.s	2$
1$
	divu	d1,d0
	 bvc.s	2$
	move.w	#$ffff,d0
2$
	move.w	d0,_fsvposition
	rts

* --------------------------------------------------------------------- *
	XDEF	_FSResetKnob
_FSResetKnob:
	jsr	_FSSetKnobHeight
	jsr	_FSSetKnob
	rts

* ------------------------------------------------------------------------
; FSSetKnob()
;  Call ModifyProp() to actually update slide image in window.
* ------------------------------------------------------------------------
	XDEF	_FSSetKnob
_FSSetKnob:
	movem.l	d2-d5/a2/a6,-(sp)

;NewModifyProp(&FSSlideGad, FSWin, 0L, AUTOKNOB|FREEVERT|PROPB, 0L, vposition, 0xffffL, vheight, 1L)
	lea	_FSSlideGad,a0
	movea.l	_FSWin,a1
	suba.l	a2,a2
	moveq	#AUTOKNOB!FREEVERT!PROPBORDERLESS,d0
	moveq	#0,d1
	moveq	#0,d2
	move.w	_fsvposition,d2
	move.l	#$ffff,d3
	moveq	#0,d4
	move.w	_fsvheight,d4
	moveq	#1,d5
	SYS	NewModifyProp,_IntuitionBase

	movem.l	(sp)+,d2-d5/a2/a6
	rts

* ------------------------------------------------------------------------
; FSEnableFGad(gadg_num)
;  LONG gadg_num;
;
; Given a file gadget number, refresh the text for that entry.
* ------------------------------------------------------------------------
	XDEF	_FSEnableFGad
_FSEnableFGad:
	movem.l	a2-a3/a6,-(sp)

	movea.l	_FSRPort,a2

	move.w  4+4*3+2(sp),d0		;Grab gadget number
	move.w	d0,d1			;Copy
	lsl.w	#2,d0			;Conver to long offset
	lea	_FSFileNodes,a0
	movea.l	0(a0,d0.w),a0		;tnode = FSFileNodes[gadg_num]
	movea.l	fn_info(a0),a3		;a3 = tnode->info

* Move to the right slot text position
	mulu	#11,d1
	addi.w	#19,d1
	moveq	#9,d0
	movea.l	a2,a1
	SYS	Move,_GfxBase	;Move(FSRPort, 9, gadnum*11+19)

* Set the pen color according to the node
	move.w	nd_textcolor(a3),d0
	movea.l	a2,a1
	SYS	SetAPen			;SetAPen(FSRPort, tinfo->textcolor)

* Blast out the text
	moveq	#56,d0
	lea	nd_alphadata(a3),a0
	movea.l	a2,a1
	SYS	Text			;Text(FSRPort, tinfo->alphadata, 56)

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

* ------------------------------------------------------------------------
; FSDisableFGad(gadg_num)
;  LONG gadg_num;
;
; Given a file gadget number, fill that slot with the EmptyPattern
; and clear the FileNode[] slot for that gadget.
* ------------------------------------------------------------------------
	XDEF	_FSDisableFGad
_FSDisableFGad:
	movem.l	d2-d4/a2/a6,-(sp)

	move.w	4*5+4+2(sp),d4		;Grab gadget number

	movea.l	_FSRPort,a2

	lea	_EmptyPattern,a0
	move.l	a0,rp_AreaPtrn(a2)	;rp->AreaPtrn = EmptyPattern
	move.b	#1,rp_AreaPtSz(a2)	;rp->AreaPtSz = (BYTE)1

	movea.l	a2,a1
	moveq	#2,d0
	SYS	SetAPen,_GfxBase 	;SetAPen(rp, 2)

	move.w	d4,d1
	mulu	#11,d1
	addi.w	#13,d1			;miny = 13L + (gadg_num * 11)

	move.l	d1,d3
	addq.w	#7,d3			;maxy = miny + 7L

	movea.l	a2,a1			;FSWin->RPort
	moveq	#9,d0			;minx
	move.l	#456,d2			;maxx
	SYS	RectFill		;RectFill(rp, minx, miny, maxx, maxy)

	clr.l	rp_AreaPtrn(a2)		;rp->AreaPtrn = 0L
	clr.b	rp_AreaPtSz(a2)		;rp->AreaPtSz = (BYTE)0

	move.w	d4,d0
	lsl.w	#2,d0
	lea	_FSFileNodes,a0
	clr.l	0(a0,d0.w)		;FSFileNodes[gadg_num] = 0L

	movem.l	(sp)+,d2-d4/a2/a6
	rts

* --------------------------------------------------------------------- *
	XDEF	_FSDisableAllFGads
_FSDisableAllFGads:
	move.l	d2,-(sp)

	moveq	#0,d2
	move.w	_fsnumentries,d2
	bra.s	2$
1$	move.l	d2,-(sp)
	jsr	_FSDisableFGad		;FSDisableFGad(i)
	addq.w	#4,sp
	addq.w	#1,d2			;i++
2$	cmpi.w	#10,d2			;Hit 10 entries?
	 bcs	1$			;Nope, disable one
	move.l	(sp)+,d2
	rts

* ------------------------------------------------------------------------
;struct file_node *AllocFileNode()
;
;  Allocate a file_node structure and it's info block, return pointer
; to the file_node or NULL if allocation failed.
* ------------------------------------------------------------------------
	XDEF	_AllocFileNode
_AllocFileNode:
	move.l	a6,-(sp)

	moveq	#fn_SIZEOF+nd_SIZEOF,d0	;sizeof(struct file_node)+sizeof(struct node_data)
	move.l	#MEMF_CLEAR,d1		;MEMF_CLEAR
	SYS	AllocMem,4		;Allocate file node structure
	move.l	d0,d1
	 beq.s	1$
	movea.l	d0,a0
        addi.l	#fn_SIZEOF,d1
	move.l	d1,fn_info(a0)		;Set pointer to tnode->info area
1$
	movea.l	(sp)+,a6
	rts

* ------------------------------------------------------------------------
; FreeFileNode(tnode)
;  struct file_node *tnode;
;
;  Given a pointer to a file_node, Free the memory allocated for that node.
* ------------------------------------------------------------------------
	XDEF	_FreeFileNode
_FreeFileNode:
	move.l	a6,-(sp)

	move.l	4+4(sp),d0		;Grab tnode
	 beq.s	2$			;Nope, get outta here
	movea.l	d0,a1
	moveq	#fn_SIZEOF+nd_SIZEOF,d0
	SYS	FreeMem,4		;FreeMem(tnode, sizeof(struct file_node)+sizeof(struct node_data))

2$	movea.l	(sp)+,a6
	rts
	

* ------------------------------------------------------------------------
; FreeAllFNodes()
;
;  Call FreeFileNode for each node in the linked list of file_nodes,
;then deallocate the head of the list (fnList)
* ------------------------------------------------------------------------
	XDEF	_FreeAllFNodes
_FreeAllFNodes:
	movem.l	a2/a6,-(sp)

	move.l	_fnList,d0		;Is there a list?
	 beq.s	2$			;Nope, get outta here

	movea.l	d0,a2			;Grab head
	movea.l	4,a6			;Set for ExecBase
1$
	movea.l	a2,a0
	SYS	RemHead			;d0 = RemHead(fnList)
	tst.l	d0			;Hit end?
	 beq.s	2$			;yep

	move.l	d0,-(sp)
	jsr	_FreeFileNode		;Free the node
	addq.w	#4,sp
	bra	1$			;Remove next node

2$
* Reset other globals
	clr.w	_fsvposition	;Set slider position to top of area
	move.w	#$ffff,_fsvheight	;Max height
	clr.w	_fsnumentries	;Zero entries

	movem.l	(sp)+,a2/a6
	rts

* ------------------------------------------------------------------------
;struct dev_node *AllocDevNode()
;
;  Allocate a dev_node, return pointer to same
* ------------------------------------------------------------------------
	XDEF	_AllocDevNode
_AllocDevNode:
	move.l	a6,-(sp)

	moveq	#dn_SIZEOF,d0
	move.l	#MEMF_CLEAR,d1
	SYS	AllocMem,4		;AllocMem(sizeof(struct dev_node), MEMF_CLEAR)
	
	movea.l	(sp)+,a6
	rts

* ------------------------------------------------------------------------
;FreeDevNode(tnode)
;  struct dev_node *tnode;
;
;  Given a pointer to a dev_node, free memory allocated for that node.
* ------------------------------------------------------------------------
	XDEF	_FreeDevNode
_FreeDevNode:
	move.l	a6,-(sp)

	move.l	4+4(sp),d0		;Grab dev_node to free
	 beq.s	1$			;Oops, nothing here

	movea.l	d0,a1
	moveq	#dn_SIZEOF,d0
	SYS	FreeMem,4		;FreeMem(tnode, (long)sizeof(struct dev_node))

1$
	movea.l	(sp)+,a6
	rts


* ------------------------------------------------------------------------
;FreeAllDNodes()
;
;  Call FreeDevNode() for each dev_node in the linked list, then free the
; head of the list, devList.
* ------------------------------------------------------------------------
	XDEF	_FreeAllDNodes
_FreeAllDNodes:
	movem.l	a2/a6,-(sp)

	move.l	_devList,d0		;Is there a head node?
	 beq.s	3$			;Nope, don't deallocate

	movea.l	d0,a2			;Grab head of device list
	movea.l	4,a6			;Set for execbase

1$	movea.l	a2,a0
	SYS	RemHead			;d0 = RemHead(devList)
	tst.l	d0			;Got one?
	 beq.s	2$			;Nope, list is empty

	move.l	d0,-(sp)
	jsr	_FreeDevNode		;Free this device node
	addq.w	#4,sp
	bra	1$

2$	movea.l	a2,a1
	moveq	#MLH_SIZE,d0
	SYS	FreeMem			;FreeMem(devList, sizeof(MinList))
	clr.l	_devList

3$	movem.l	(sp)+,a2/a6
	rts

* ------------------------------------------------------------------------
; FreeFileSelect()
;   Deallocate filenames and fileinfoblocks, unlock any locks
* ------------------------------------------------------------------------
	XDEF	_FreeFileSelect
_FreeFileSelect:
	move.l	a6,-(sp)
	move.w	#1,_fsvirgindir	;virgindir = 1
	jsr	_FreeAllFNodes		;Free all allocated filenames
	jsr	_FSClearLock		;Unlock any locks

* Free the head node itself
	move.l	_fnList,d0
	 beq.s	1$
	movea.l	d0,a1
	moveq	#MLH_SIZE,d0
	SYS	FreeMem,4		;FreeMem(fnList, sizeof(MinList))
	clr.l	_fnList		;And clear the pointer
1$
* Free the global FileInfoBlock and InfoData struct
	move.l	_FSFib,d0		;Do we have a FileInfoBlock allocated?
	 beq.s	2$			;Nope
	movea.l	d0,a1
	move.l	#fib_SIZEOF+id_SIZEOF,d0
	SYS	FreeMem,4		;FreeMem(FSFib, 300L)
	clr.l	_FSFib		;Set to NULL so we don't re-free it
	clr.l	_FSInfo
2$
	movea.l	(sp)+,a6
	rts

* --------------------------------------------------------------------- *
* Cleanup function for fileselect
* --------------------------------------------------------------------- *
	XDEF	_ReleaseFileSelect
_ReleaseFileSelect:
	jsr	_FreeFileSelect
	jsr	_FreeAllDNodes
	rts

* ------------------------------------------------------------------------
; LONG FSGetNextFib(VOID)
;
;  Call ExNext() to fill in next FileInfoBlock, set titlebar to reflect
; status of the request.
* ------------------------------------------------------------------------
	XDEF	_FSGetNextFib
_FSGetNextFib:
	movem.l	d2/a6,-(sp)

* Try to get next fileinfoblock from dos
	move.l	_FSLock,d1
	move.l	_FSFib,d2
	SYS	ExNext,_DOSBase
	tst.l	d0			;Did we get an entry?
	 bne.s	gnfgot1			;Yep, got it

* Didn't get one, find out why
;if ( (error = IoErr()) != ERROR_NO_MORE_ENTRIES)
	SYS	IoErr			;Get error number
	cmpi.l	#232,d0			;No more entries?
	 beq.s	gnfnme			;Yep

	move.l	d0,-(sp)
	jsr	_ioerrnum		;Otherwise set window title to error number
	addq.w	#4,sp
	move.w	d0,_fstitstatus
	moveq	#-1,d2			;Return bad get status
	bra.s	gnfsetwintit

gnfnme:
	moveq	#0,d2			;Return DONE!
	tst.w	_fsnumentries	;Got any valid pattern matches?
	 bne.s	gnfselect		;Yep, select a file time

	movea.l	_fnList,a6
	movea.l	MLH_HEAD(a6),a6
	tst.l	fn_Node+MLN_SUCC(a6) 	;Do we have any files?
	 beq.s	gnfnoentry		;Nope, no entries

	move.w	#24,_fstitstatus	;Otherwise no match
	bra.s	gnfsetwintit

gnfnoentry:
	move.w	#22,_fstitstatus	;No Entries...
	bra.s	gnfsetwintit

gnfselect:
	move.w	#29,_fstitstatus	;Select a file...

gnfsetwintit:
	bra.s	gnfdone

gnfgot1:
	moveq	#1,d2

gnfdone:
	move.l	d2,d0
	movem.l	(sp)+,d2/a6
	rts

* ------------------------------------------------------------------------
;AllocFSFib()
;Allocate a fileinfoblock, return status
* ------------------------------------------------------------------------
	XDEF	_AllocFSFib
_AllocFSFib:
	move.l	a6,-(sp)
	tst.l	_FSFib		;Do we already have a fib?
	 bne.s	1$			;Yep, get out of here

;FSFib = AllocMem(sizeof(*FSFib) + sizeof(*FSInfo), MEMF_CLEAR);
	move.l	#fib_SIZEOF+id_SIZEOF,d0
	move.l	#MEMF_CLEAR,d1
	SYS	AllocMem,4
	move.l	d0,_FSFib
	 beq.s	2$			;Oops, allocation failed, return 0

	addi.l	#fib_SIZEOF,d0
	move.l	d0,_FSInfo		;Set pointer to InfoData struct too

1$	moveq	#1,d0			;Return good status

2$	movea.l	(sp)+,a6
	rts

* ------------------------------------------------------------------------
;LONG ioerrnum(num)
;  LONG num;
;
; Given an IoErr number, convert to error text table offset, return
; converted index number.
* ------------------------------------------------------------------------
	XDEF	_ioerrnum
_ioerrnum:
	move.l	4(sp),d0

	cmpi.w	#202,d0			;if (num < 202L || num > 231L)
	 bcs.s	ioetl
	cmpi.w	#231,d0
	 bls.s	ioe3l
ioetl:
	moveq	#27,d0			;return(27)
	rts
ioe3l:
	cmpi.w	#209,d0			;if (num < 209L)
	 bcc.s	ioe4l
	subi.w	#202,d0			;num -= 202L
	rts
ioe4l:
	subi.w	#205,d0			;else num -= 205L
	rts

* ------------------------------------------------------------------------
; BYTE *ioerrmsg(num)
;  long num;
;
;  Given a IoErr number, return pointer to error message text that
; describes that error.
* ------------------------------------------------------------------------
	XDEF	_ioerrmsg
_ioerrmsg:
	move.l	4(sp),-(sp)		;Push error number
	jsr	_ioerrnum
	addq.w	#4,sp
	lsl.w	#2,d0			;Convert return to text table offset
	lea	_fserrmsgs,a0
	move.l	0(a0,d0.w),d0		;Return pointer to fserrmsgs[num]
	rts

* ------------------------------------------------------------------------
; VOID FSPutPath(VOID)
;  Refresh the FSPathGadget imagery.
* ------------------------------------------------------------------------
	XDEF	_FSPutPath
_FSPutPath:
	movem.l	a2/a6,-(sp)

;RefreshGList(&FSPathGad, FSWin, 0L, 1L);
	lea	_FSPathGad,a0
	movea.l	_FSWin,a1
	suba.l	a2,a2
	moveq	#1,d0
	SYS	RefreshGList,_IntuitionBase

	movem.l	(sp)+,a2/a6
	rts

* ------------------------------------------------------------------------
; VOID FSClearLock(VOID)
;
;  If we have a Lock on the current directory, unlock it.
* ------------------------------------------------------------------------
	XDEF	_FSClearLock
_FSClearLock:
	move.l	a6,-(sp)
	tst.w	_fsdirlocked	;Is it locked?
	 beq.s	2$			;Nope, don't dare unlock

	move.l	_FSLock,d1
	 beq.s	1$
	SYS	UnLock,_DOSBase	;UnLock(FSLock)
	clr.l	_FSLock		;FSLock = 0L;
1$	clr.w	_fsdirlocked	;dirlocked = 0

2$	movea.l	(sp)+,a6
	rts

* ------------------------------------------------------------------------
; FSWinTitle()
;   Refresh current window title using contents of windowtitle[].
* ------------------------------------------------------------------------
	XDEF	_FSWinTitle
_FSWinTitle:
	movem.l	a2/a6,-(sp)

	move.w	_fstitlelength,d0
	lea	_FSWinTitleStr,a0
	clr.b	0(a0,d0.w)		;windowtitle[fstitlelength] = '\x0'

	move.w	_fstitstatus,d1
	lsl.w	#2,d1
	lea	_fserrmsgs,a1
	movea.l	0(a1,d1.w),a1
	adda.w	d0,a0
	jsr	astrcpy			;astrcpy(&windowtitle[fstitlelength], fserrmsgs[fstitstatus])

	movea.l	_FSWin,a1
	move.l	wd_Flags(a1),d0		;d0 = FSWin->Flags
	andi.l	#WINDOWTGADS,d0 	;Do we have titlebar gadgets?
	 bne.s	3$			;Yep, refresh is automatic

	lea	_FSWinTitleStr+79,a1
	clr.b	(a1)			;FSWinTitleStr[79] = 0
	move.b	#' ',d0			;for (;ptr != &FSWinTitle[79];ptr++)
2$	cmpa.l	a0,a1
	 beq.s	4$
	move.b	d0,(a0)+		;*ptr = ' '
	bra	2$
3$
	move.b	#' ',(a0)+		;Concat a space and a null onto the end
	clr.b	(a0)
4$
;SetWindowTitles(FSWin, windowtitle, -1L)
	movea.l	_FSWin,a0
	lea	_FSWinTitleStr,a1
	movea.l	#-1,a2
	SYS	SetWindowTitles,_IntuitionBase

	movem.l	(sp)+,a2/a6
	rts

* ------------------------------------------------------------------------
; VOID FSEndString(VOID)
;
; If user started a string gadget entry then clicked in another gadget,
;ignore any changes to the string and restore the previous string.
* ------------------------------------------------------------------------
	XDEF	_FSEndString
_FSEndString:
	movem.l	a2/a6,-(sp)

	move.w	_fsstartedstr,d0	;Grab starttext flag
	cmpi.w	#1,d0			;Did user start at file entry?
	 bne.s	fstnotfile		;Nope

	movea.l	_FSFileInfo,a0	;Grab filename buffer
	lea	_FileUndoName,a1
	lea	_FSFileGad,a2	;Grab filetext gadget
	bra.s	fstrefresh		;Refresh it

fstnotfile:
	cmpi.w	#2,d0			;Did user start a path entry?
	 bne.s	fstnotpath		;Nope, maybe pattern

	movea.l	_FSPathInfo,a0	;Grab pathtext buffer
	lea	_FileUndoBuffer,a1
	lea	_FSPathGad,a2	;Pointer to path gadget
	bra.s	fstrefresh		;Refresh text

fstnotpath:
	cmpi.w	#3,d0			;Started a pattern entry?
	 bne.s	fstdone			;Nope, don't have to do anything

	movea.l	_FSPatternInfo,a0	;Else grab the pattern buffer
	lea	_FSPatternUndoBuffer,a1
	lea	_FSPatternGad,a2	;Pointer to pattern gadget

fstrefresh:
	jsr	astrcpy			;astrcpy(tstr, FSwstr)

	movea.l	a2,a0
	movea.l	_FSWin,a1
	suba.l	a2,a2
	moveq	#1,d0
	SYS	RefreshGList,_IntuitionBase ;RefreshGList(tgad, FSWin, 0L, 1L)

	clr.w	_fsstartedstr	;fsstartedstr = 0

fstdone:
	movem.l	(sp)+,a2/a6
	rts


* ------------------------------------------------------------------------
; FSDoGadget(gadgid)
;  ULONG gadgid;
;
; Given a gadget id number, perform action associated with that gadget.
* ------------------------------------------------------------------------
	XDEF	_FSDoGadget
_FSDoGadget:
	movem.l	d4/a2,-(sp)

	move.w	14(sp),d4		;Grab gadget id number
	 bne.s	dgnotusergad		;Not zero, not Undo gadget

* FSUserGad
	jsr	_ConcatPathString 	;Concat final output text, return
	clr.w	_FSDone

	movea.l	_FSReq,a1
	movea.l	fs_specgadfunc(a1),a0
	pea	_FSUserGad
	move.l	_FSWin,-(sp)
	move.l	a1,-(sp)
	jsr	(a0)
	lea	12(sp),sp
	tst.l	d0
	 bne	dgnewdir
	bra	dogaddone

dgnotusergad:
* FSSelectGad
	cmpi.w	#1,d4			;Is the the FSSelect gadget?
	 bne.s	dgnotselect		;Nope
	jsr	_ConcatPathString 	;Concat final output text, return
	bra	dogaddone

dgnotselect:
* FSCancelGad
	cmpi.w	#2,d4			;Is it the FSCancel Gadget?
	 bne.s	dgnotcancel		;Nope
	move.w	#1,_FSDone		;Global signal that we're done
	movea.l	_FSReq,a0		;a0 = fsreq
	movea.l	fs_fullname(a0),a0	;a0 = fsreq->fullname
	clr.b	(a0)			;Return filename == ""
	bra	dogaddone

dgnotcancel:
* UndoGad
	cmpi.w	#3,d4			;Undo?
	 bne.s	dgnotundo
	movea.l	_FSPathInfo,a0	;Copy OldFSPathBuf to FSPathInfo.Buffer
	movea.l	_OldFSPathBuf,a1
	jsr	astrcpy			;astrcpy(FSPathInfo.Buffer, OldFSPathBuf)
	bra	dgnewdir		;Process new directory

dgnotundo:
* Dev1Gad
	cmpi.w	#20,d4			;Is it the first Device Gadget?
	 bne.s	dgnotram		;Nope
	movea.l	_CurDevNames+12,a1	;Push the name text
dgdevs:
	movea.l	_FSPathInfo,a0	;Destination text
	jsr	astrcpy			;Copy new path
	bra	dgnewdir		;Now set new directory

dgnotram:
	cmpi.w	#21,d4			;Is it the second Device Gadget?
	 bne.s	dgnotdf0		;Nope
	movea.l	_CurDevNames+8,a1	;Push the name text
	bra	dgdevs			;Change path

dgnotdf0:
	cmpi.w	#22,d4			;Is it the third Device Gadget?
	 bne.s	dgnotnil1		;Nope
	movea.l	_CurDevNames+4,a1	;Push the name text
	bra	dgdevs			;Set the path

dgnotnil1:
	cmpi.w	#23,d4			;Is it the fourth Device Gadget?
	 bne.s	dgnotnil2		;Nope
	movea.l	_CurDevNames,a1	;Push the name text
	bra	dgdevs			;And set the new path

dgnotnil2:
* RootGad
	cmpi.w	#24,d4			;Is it the ROOT gadget?
	 bne.s	dgnotroot		;Nope
	jsr	_SetRootDir		;Set root directory!
	bra	dogaddone		;Done

dgnotroot:
* ParentGad
	cmpi.w	#25,d4			;Is it the Parent gadget?
	 bne.s	dgnotpar		;Nope
	jsr	_SetParentDir		;Change to parent directory
	bra	dogaddone		;Done!

dgnotpar:
* FSPatternGad
	cmpi.w	#30,d4			;Maybe its the pattern text gadget?
	 bne.s	dgnotpattern
	jsr	_FSMatchPattern		;Otherwise attempt to match new pattern
	bra	dogaddone		;We're done!

dgnotpattern:
* FSFileGad
	cmpi.w	#31,d4			;Is it the file text gadget?
	 bne.s	dgnotfileg		;Nope
	jsr	_FSFileFunc
	jsr	_ConcatPathString	;Form final path text
	btst.b	#1,_fsflags+1	;FS_NO_STRING_EXIT?
	 beq	dogaddone
	clr.w	_FSDone
	bra	dogaddone
1$
	movea.l	_FSFileInfo,a0
	tst.b	(a0)			;Really a filename here?
	 bne.s	2$			;yep
	btst.b	#2,_fsflags+1	;FS_NO_STRING_OKAY?
	 beq.s  2$			;Nope
 	bra	dogaddone		;let user exit with no filename
2$
	clr.w	_FSDone		;Else we ain't done yet!
	bra	dogaddone

dgnotfileg:
* FSPathGad
	cmpi.w	#32,d4			;Is it the path text gadget?
	 bne.s	dgnotpath		;Nope
	movea.l	_FSPathInfo,a2	;Grab pointer to path text
	movea.l	a2,a0
	jsr	astrlen			;Determine length
	subq.w	#1,d0			;Move back to last BYTE
	 blt.s	dgnotdamnsla		;Oops, null text!

	cmpi.b	#'/',0(a2,d0.w)		;Is the last char a slash?
	 bne.s	dgnotdamnsla		;Nope, don't have to tromp it

1$      cmpi.b	#'/',0(a2,d0.w)		;Check for a trailing slash
         bne.s	2$			;Didn't find another
	clr.b	0(a2,d0.w)		;Clobber this slash
	subq.w	#1,d0			;Dec character pointer
	 bne.s	1$			;till we run out of slashes or chars

2$	jsr	_FSPutPath		;Refresh path text
dgnotdamnsla:
	jsr	_FSDirFunc		;Do fsreq->dirfunc, if there is one
	tst.w	d0			;Ignore new dir?
	 beq.s	1$			;Nope
	jsr	_SetParentDir		;Else chop off the new path
	jsr	_FSPutPath		;Update string gad
	clr.w	_fsvirgindir	;Virgin again
	bra.s	dogaddone		;All done
1$
	move.l	_FSWin,-(sp)
	pea	_FSFileGad
	jsr	_MyActivateGad		;Activate file text gadget
	addq.w	#8,sp
	bra.s	dgnewdir		;Set new path, read new dir

dgnotpath:
* Slide Gadget
	cmpi.w	#33,d4			;Is it the SlideGadget?
	 bne.s	dgnotslide		;nope
	jsr	_FSDoSlideGadget	;Else it's been released, update position
	bra.s	dgresetarrows		;Done here, reset timer

dgnotslide:
* Arrow gadgets
	cmpi.w	#40,d4			;Is it one of the four arrows gadgets?
	 blt.s	dogaddone
	cmpi.w	#43,d4
	 bgt.s	dogaddone
dgresetarrows:
	clr.w	_FSCountDown	;Kill any timer requests in progress
	bra.s	dogaddone

dgnewdir:
	move.w	#1,_fsvirgindir	;Set flag to read new directory!

dogaddone:
	movem.l	(sp)+,d4/a2
	rts

* --------------------------------------------------------------------- *
* VOID FillFileNode(tnode)
*   struct file_node *tnode;
* --------------------------------------------------------------------- *
ffnnode	EQU	4+4*4

	XDEF	_FillFileNode
_FillFileNode:
	movem.l	d2/a2-a3/a6,-(sp)

	movea.l	ffnnode(sp),a0		;a0 = tnode passed
	movea.l	fn_info(a0),a2		;a2 = tinfo = tnode->info
	movea.l	_FSFib,a3		;a3 = FSFib

	move.l	fib_Size(a3),nd_filesize(a2) ;tinfo->filesize = FSFib->fib_Size
	move.l	fib_DateStamp+0(a3),nd_days(a2) ;tinfo->day = FSFib->fib_DateStamp.ds_Days
	move.l	fib_DateStamp+4(a3),nd_minutes(a2)
	move.l	fib_DateStamp+8(a3),nd_ticks(a2)

	pea	fib_DateStamp(a3)
	jsr	_FibFileDate
	addq.w	#4,sp

	movea.l	d0,a1
	lea	nd_alphadata(a2),a0	;a0 = tinfo->alphadata
	jsr	astrcpy			;a0 = astrcpy(&tinfo->alphadata[0], FibFileDate(&FSFib->DateStamp))

	tst.l	fib_DirEntryType(a3)	;If < 0 it is a file
	 bge.s	ffnisdir		;>= 0, it is a dir

ffnisfile:
	move.w	#1,nd_filetype(a2)	;tinfo->filetype = 1
	move.w	#FPEN,nd_textcolor(a2)	;tinfo->textcolor = FPEN

	move.l	a0,-(sp)
	move.l	fib_Size(a3),-(sp)
	pea	_FSSizeFmtStr
	move.l	a0,-(sp)
	jsr	_sprintf		;sprintf(a0, "%8ld ", FSFib->fib_Size)
	lea	12(sp),sp
	movea.l	(sp)+,a0
	adda.w	#9,a0			;Skip filesize part of message
	bra.s	ffnsetname

ffnisdir:
	move.w	#2,nd_filetype(a2)	;tinfo->filetype = 2
	move.w	#DPEN,nd_textcolor(a2)	;tinfo->textcolor = DPEN

	lea	_FSDirFmtStr,a1
	jsr	astrcpy			;astrcpy(dest, FSDirFmtStr)

ffnsetname:
	moveq	#0,d0
	lea	fib_FileName(a3),a1	;a1 = FSFib->fib_FileName
1$	move.b	(a1)+,d1
	 beq.s	2$			;Hit null, see if we need to pad
	move.b	d1,(a0)+		;copy a char, till null
	addq.w	#1,d0			;i++
	cmpi.w	#30,d0			;Max chars yet?
	 bcs	1$

2$	move.w	d0,nd_namelength(a2)	;tinfo->namelength = i
	move.b	#' ',d1
	bra.s	4$			;Append spaces till 30 chars
3$	move.b	d1,(a0)+
	addq.w	#1,d0
4$	cmpi.w	#30,d0
	 bcs	3$

	clr.b	(a0)			;Null terminate at 31st char

ffnsetshow:
	movea.l	ffnnode(sp),a2
	moveq	#1,d0
	movea.l	a2,a0			;a0 = tnode
	jsr	_FSValidateEntry	;FSValidateEntry(tnode, 1)

* Now insert the node into list according to sort type
ffninsert:
	movea.l	_fnList,a3
	movea.l	MLH_HEAD(a3),a3		;a3 = fnList->lh_Head

	movea.l	_FSReq,a0
	move.w	fs_sorttype(a0),d2	;d0 = FSReq->sorttype

1$
	tst.l	fn_Node+MLN_SUCC(a3)	;Is this the end of the list?
	 beq.s	2$			;Nope, insert it here

	move.l	fn_info(a3),a1		;Push existing node->info
	move.l	fn_info(a2),a0		;Push tnode->info
	move.w	d2,d0			;Push sort type
	jsr	_CompareNodes		;Compare info's according to sort type
	tst.w	d0			;Is a0 > a1?
	 beq.s	2$			;nope, found insertion point
	movea.l	fn_Node+MLN_SUCC(a3),a3	;oldnode = oldnode->next
	bra	1$			;Nope, keep looking

2$
	movea.l	_fnList,a0
	movea.l	a2,a1
	move.l	fn_Node+MLN_PRED(a3),a2
	SYS	Insert,4

ffndone:
	movem.l	(sp)+,d2/a2-a3/a6
	rts

* --------------------------------------------------------------------- *
* VOID FSValidateEntry(tnode, mode)
*                       a0     d0
* --------------------------------------------------------------------- *
	XDEF	_FSValidateEntry
_FSValidateEntry:
	movem.l	d2-d3/a2,-(sp)

* See if the Acceptor likes this node name
	move.l	d0,d3			;d3 = mode
	movea.l	a0,a2			;a2 = tnode
	move.l	a2,-(sp)
	jsr	_FSAcceptEntry		;if (FSAcceptEntry(tnode) == 0)
	addq.w	#4,sp
	tst.w	d0
	 bne.s	1$

* Don't display this node
	movea.l	fn_info(a2),a0
	clr.w	nd_showit(a0)		;tnode->info->showit = 0;
	move.w	#-1,fn_idnum(a2)	;tnode->idnum = -1
	bra.s	4$			;And return

* Accept this node for display
1$	movea.l	fn_info(a2),a0
	move.w	#1,nd_showit(a0)	;else tnode->info->showit = 1;

	moveq	#0,d2
	move.w	_fsnumentries,d2	;Grab entry number for this node
	 bne.s	2$			;Not first entry
	move.l	a2,_topfin		;topnode = tnode, first visible node
2$
	move.w	d2,fn_idnum(a2)		;tnode->idnum = fsnumentries
	addq.w	#1,_fsnumentries	;fsnumentries++, one more visible entry
	clr.w	_fsneedshow 	;fsneedshow = 0, needs sorting

* If we haven't filled display area, show this entry
	cmpi.w	#10,d2			;> 10 entries already?
	 bcc.s	3$			;Yep, just set the knob

	lea	_FSFileNodes,a0
	move.w	d2,d0			;Grab entry number
	lsl.w	#2,d0			;Convert to long table offset
	move.l	a2,0(a0,d0.w)		;FSFileNodes[fsnumentries] = tnode

	move.l	d2,-(sp)
	jsr	_FSEnableFGad		;FSEnableFGad(fsnumentries)
	addq.w	#4,sp
	bra.s	4$			;Skip setting knob, it is still full size

3$
	tst.w	d3			;Want slide update?
	 beq.s	4$			;Nope

;	btst.b	#7,_FSSlideGad+gg_Flags+1 ;User has slide gadget in use?
;	 bne.s	4$			;Yes, don't refresh it while in use

* Calc new knob size and refresh
	btst.l	#0,d2			;(idnum % 2) == 0?
	 beq.s	4$			;Yep, don't reset on even values
	jsr	_FSResetKnob

4$
	movem.l	(sp)+,d2-d3/a2
	rts

* --------------------------------------------------------------------- *
* Update FileTexts list, redisplay
* Renumber list, find new topfin
* --------------------------------------------------------------------- *
	XDEF	_FSUpdateSort
_FSUpdateSort:
	move.w	_fsneedshow,d0
	 blt.s	3$
	move.w	_fsnumentries,d1
	 beq.s	2$
	subi.w	#10,d1			;Less max number of visible files
	move.w	_FSSlideProp+4,d0	;Grab current vpot
	mulu	d1,d0			;Times position percentage
	addi.l	#32768,d0
	clr.w	d0
	swap	d0
	jsr	_FSResetNumEntries
	jsr	_FSSetFileGads
2$
	move.w	#-1,_fsneedshow
	moveq	#0,d0

3$	tst.w	d0
	rts

* --------------------------------------------------------------------- *
* VOID FSResetNumEntrys(idnum)
*                       d0:16
* --------------------------------------------------------------------- *
	XDEF	_FSResetNumEntries
_FSResetNumEntries:
	move.l	a2,-(sp)

	movea.l	_fnList,a2		;Grab List
	movea.l	MLH_HEAD(a2),a2		;Head of list


	moveq	#0,d1			;i = 0

1$	tst.l	fn_Node+MLN_SUCC(a2)	;End of list?
	 beq.s	5$
	movea.l	fn_info(a2),a0		;a0 = tnode->info
	tst.w	nd_showit(a0)		;Visible?
	 bne.s	2$			;Yep
	move.w	#-1,fn_idnum(a2)	;Else tnode->idnum = -1
	bra.s	4$			;Next node
2$	move.w	d1,fn_idnum(a2)		;tnode->idnum = i
	cmp.w	d1,d0			;if (idnum == oldidnum)
	 bne.s	3$	
	move.l	a2,_topfin		; topfin = tnode;
3$
	addq.w	#1,d1			;i++
4$
	movea.l	fn_Node+MLN_SUCC(a2),a2	;tnode = tnode->fn_Node.mln_Succ
	bra	1$			; Check next node

5$
	move.w	d1,_fsnumentries	;Update number of entries
	
	movea.l	(sp)+,a2
	rts

* --------------------------------------------------------------------- *
* LONG FSAcceptEntry(struct file_node *tnode);
* Decide whether given tnode is displayable.
* Return 0L for ignore this node, 1L for valid node.
* --------------------------------------------------------------------- *
aftnode	EQU	8
aftname	EQU	-48

	XDEF	_FSAcceptEntry
_FSAcceptEntry:
	link	a5,#aftname
	movem.l	a2-a3,-(sp)

	movea.l	aftnode(a5),a2		;Grab tnode
	movea.l	fn_info(a2),a3		;Grab tnode->info

* Copy tnode->info->alphadata name section to stack, null terminate
	move.w	nd_namelength(a3),d0	;Push tinfo->namelength
	lea	nd_alphadata+26(a3),a1	;Push tinfo->alphadata + 26
	lea	aftname(a5),a0		;Push destination
	jsr	astrncpy		;Copy and null terminate

* If there is a special match function defined, use that...
	movea.l	_FSReq,a0
	tst.l	fs_matchfunc(a0)	;If FSReq->matchfunc != 0
	 beq.s	1$
	move.l	aftnode(a5),-(sp)
	pea	aftname(a5)
	move.l	_FSReq,-(sp)
	move.l	fs_matchfunc(a0),a0
	jsr	(a0)			;return((FSReq->matchfunc)(FSReq, name, tnode)
	lea	12(sp),sp
	bra.s	aftdone

* Otherwise use built-in acceptor routine
1$	cmpi.w	#2,nd_filetype(a3)	;Is tinfo->filetype == 2? (a directory)
	 beq.s	aftisdir		;Yep, we need it in list

* Check file name against patterns
	btst.b	#6,_fsflags+1	;Want dirs only?
	 bne.s  aftwrongpat		;Yep, ignore all files

	pea	_FSIgnorePat	;See if it matches the ignore text
	pea	aftname(a5)
	jsr	_wildmatch
	addq.w	#8,sp
	tst.w	d0			;Did it match the ignore text?
	 bne.s	aftwrongpat		;Yep, ignore this file

	move.l	_FSPatternInfo,-(sp) ;See if it matches desired pattern
	pea	aftname(a5)
	jsr	_wildmatch
	addq.w	#8,sp
	tst.w	d0			;Did it match?
	 beq.s	aftwrongpat		;Nope, skip it
	bra.s	aftgoodpat		;Else accept this file

aftisdir:
	btst.b	#5,_fsflags+1	;This is a dir, want files only?
	 bne.s	aftwrongpat		;Yep, ignore this dir

aftgoodpat:
	moveq	#1,d0			;Accept this entry
	bra.s	aftdone

aftwrongpat:
	moveq	#0,d0			;Ignore this entry

aftdone:
	movem.l	(sp)+,a2-a3
	unlk	a5
	rts

* --------------------------------------------------------------------- *
* VOID FSMatchPattern(VOID);
* Called when path or pattern has changed - rematch all entries
* against the new pattern, display entries that match.
* --------------------------------------------------------------------- *
	XDEF	_FSMatchPattern
_FSMatchPattern:
	movem.l	d4/a2-a3/a6,-(sp)

* Pattern match list against new pattern, flag unwanted names 
	movea.l	_FSPatternInfo,a0 	;Grab pattern
	tst.b	(a0)			;Null pattern text?
	 bne.s	mnpnotnop		;Nope, process it

	move.b	#'*',(a0)+		;Otherwise put back wildstar
	clr.b	(a0)			;Null terminate
	lea	_FSPatternInfo,a0	;Grab stringinfo
	move.w	#1,8(a0)		;FSPatternInfo.BufferPos = 1;

	lea	_FSPatternGad,a0
	movea.l	_FSWin,a1
	suba.l	a2,a2
	moveq	#1,d0
	SYS	RefreshGList,_IntuitionBase ;Refresh pattern text gadget

	move.l	_FSWin,-(sp)
	pea	_FSPatternGad
	jsr	_MyActivateGad		;MyActivateGad(&FSPatternGad, FSWin)
	addq.w	#8,sp
	bra.s	mnpdone			;Make the user type something else in

mnpnotnop:
	movea.l	_fnList,a0
	move.l	MLH_HEAD(a0),a0
	tst.l	fn_Node+MLN_SUCC(a0)	;Do we have any files to match?
	 beq.s	mnpdone			;Nope, nothing to do

* Reset knob to full height, zero position
	clr.w	_fsnumentries	;Clear fsnumentries count
	clr.w	_fsvposition
	move.w	#$ffff,_fsvheight
	jsr	_FSSetKnob		;Show new knob

* Disable all file "gadgets"
	jsr	_FSDisableAllFGads

* Scan list, enable/flag entries that match new pattern
	movea.l	_fnList,a2		;tnode = fnList
	movea.l	MLH_HEAD(a2),a2		;tnode = head node
	clr.l	_topfin		;Clear out top/bot display pointers
1$
	tst.l	fn_Node+MLN_SUCC(a2)	;End of list?
	 beq.s	2$

	moveq	#0,d0
	movea.l	a2,a0
	jsr	_FSValidateEntry	;FSValidateEntry(tnode, 0)
	movea.l	fn_Node+MLN_SUCC(a2),a2	;tnode = tnode->fn_Node.mln_Succ
	bra	1$			;Nope, process this one too
2$
	move.w	#-1,_fsneedshow	;Clear reshow/sort flag, list valid
	cmpi.w	#10,_fsnumentries	;Found some entries?
	 bls.s	mnpsettitle		;Yep, skip setting knob again
	jsr	_FSResetKnob		;Show new knob size

mnpsettitle:
	tst.w	_fsnumentries	;Got any matches to pattern?
	 bne.s	mnpmatchedsome		;Yep

	move.w	#24,_fstitstatus	;"Nothing matched PATTERN"
	bra.s	mnpnewtit

mnpmatchedsome:
	move.w	#29,_fstitstatus	;"Select a file..."

mnpnewtit:
	jsr	_FSWinTitle		;Show final title status

mnpdone:
	movem.l	(sp)+,d4/a2-a3/a6
	rts

* ----------------------------------------------------------------------
; VOID ConcatPathString(VOID)
;
;  If the file text is non-NULL, concat path text and file text,
; set global exit flag to show user selected a filename.
* ----------------------------------------------------------------------
	XDEF	_ConcatPathString
_ConcatPathString:
* Try to add path prefix
	move.l	_FSFileInfo,-(sp)
	move.l	_FSPathInfo,-(sp)
	movea.l	_FSReq,a0		;Grab final pathtext
	movea.l	fs_fullname(a0),a0
	move.l	a0,-(sp)
	jsr	_ConcatDirFile		;ConcatDirFile(FSReq->fullname, FSPathInfo->Buffer, FSFileInfo->Buffer)
	lea	12(sp),sp

* Did the user select a filename yet?
	movea.l	_FSFileInfo,a0
	tst.b	(a0)			;First byte of name null?
	 bne.s	1$			;Nope, must be okay
	btst.b	#2,_fsflags+1	;(FSFlags & FS_NO_STRING_OKAY) == 0?
	 beq.s	2$			;Yep, user can't exit till something is entered
1$
* Flag main loop to exit
	move.w	#1,_FSDone
2$
	rts

* --------------------------------------------------------------------- *
* VOID __stdargs ConcatDirFile(BYTE *, BYTE *, BYTE *)
* --------------------------------------------------------------------- *
	XDEF	_ConcatDirFile
_ConcatDirFile:
	movem.l	4(sp),a0-a1		;grab dest, dirname
	tst.b	(a1)			;Is there a dirname here?
	 beq.s	1$			;Nope
	jsr	astrcpy			;astrcpy(dest, dirname)
        movea.l	12(sp),a1		;grab filename
	tst.b	(a1)			;Is there a filename?
	 beq.s	2$			;Nope, don't concat a slash
	move.b	-1(a0),d0		;d0 = dest[strlen(dest - 1)]
	cmpi.b	#':',d0			;Already has a colon?
	 beq.s	1$			;Yep
	cmpi.b	#'/',d0			;Already has a slash?
	 beq.s	1$			;Yep
	move.b	#'/',(a0)+		;Append a slash
	clr.b	(a0)			;Terminate
1$
	movea.l	12(sp),a1
	jsr	astrcpy			;astrcpy(dest, name)
2$
	rts

* ------------------------------------------------------------------------
* VOID FSDoSlideGadget(VOID)
*
*  Process the current slide gadget position, scroll file entries
* to match current position.
* ------------------------------------------------------------------------
	XDEF	_FSDoSlideGadget
_FSDoSlideGadget:
	movem.l	d4-d5,-(sp)

	jsr	_FSUpdateSort
	 beq	dsgdone

	cmpi.w	#10,_fsnumentries	;Do we have less than 10 entries?
	 blt	dsgdone			;Yep, can't move anyhow

	moveq	#0,d0
	move.w	_FSSlideProp+4,d0	;Grab new vpot
	cmp.w	_fsvposition,d0 	;Has it moved?
	 beq	dsgdone			;Nope

	move.w d0,_fsvposition 	;Otherwise update position

	move.w	_fsnumentries,d4	;Grab number of entries
	subi.w	#10,d4			;Less max number of visible files
	mulu	d0,d4			;Times position percentage
	addi.l	#32768,d4
	clr.w	d4
	swap	d4

	movea.l	_topfin,a0		;Grab top node
	cmp.w	fn_idnum(a0),d4		;Is node->idnum the same as newpos?
	 beq.s	dsgdone			;Yep, don't have to move it
	 bcc.s	dsgscrup		;newpos is lower, scroll up

**************
* Scroll Down
**************
	move.w	fn_idnum(a0),d0		;Grab topfin->idnum
	sub.w	d4,d0			;i = idnum - newpos
	cmpi.w	#8,d0			;Need to scroll more than 8?
	 ble.s	dsgssdwn		;Nope, use slow scroll

dsgdwn1:
	cmp.w	fn_idnum(a0),d4		;Is topfin->idnum == newpos yet?
	 beq.s	dsgredo			;Yep, we are done

dsgpretop:
	movea.l	fn_Node+MLN_PRED(a0),a0	;topfin = topfin->fn_Node.mln_Pred
	tst.w	fn_idnum(a0)		;Is this idnum >= 0?
	 blt.s	dsgpretop		;Nope, keep looking
	bra.s	dsgdwn1			;Check this new position

dsgssdwn:
	move.l	d0,-(sp)
	jsr	_FSScrollDownGads	;Scroll entries down d5 times
	addq.w	#4,sp
	bra.s	dsgdone

************
* Scroll Up
************
dsgscrup:
	move.w	d4,d5			;Grab newpos
	sub.w	fn_idnum(a0),d5		;i = newpos - node->idnum
	cmpi.w	#8,d5			;i > 8?
	 ble.s	dsgssup			;Nope, use slow scroll

dsgmvup1:
	cmp.w	fn_idnum(a0),d4		;Is topfin->idnum == newpos yet?
	 beq.s	dsgredo			;Yep, we are done

dsgnxttop:
	movea.l	fn_Node+MLN_SUCC(a0),a0	;topfin = topfin->fn_Node.mln_Succ
	tst.w	fn_idnum(a0)		;Is this a valid entry?
	 blt.s	dsgnxttop		;Nope, move on
	bra.s	dsgmvup1

dsgredo:
	move.l	a0,_topfin		;Set new top and bottom
	jsr	_FSSetFileGads		;Redraw all the file entries
	bra.s	dsgdone			;We're done

dsgssup:
	move.l	d5,-(sp)
	jsr	_FSScrollUpGads		;Scroll the entries up d5 times
	addq.w	#4,sp

dsgdone:
	movem.l	(sp)+,d4-d5
	rts

* --------------------------------------------------------------------
* VOID FSStartScrollGad(gadcode)
*   ULONG gadcode;
* Given Gadget ID code, respond to first user click on a arrow gadget.
* Reset the timer.device so that .25 seconds delay occurs before
* the autorepeat kicks in.
* --------------------------------------------------------------------
	XDEF	_FSStartScrollGad
_FSStartScrollGad:
	move.w	6(sp),d0	;Grab gadget code
	subi.w	#33,d0		;Slide gad
	 bne.s	1$
	jsr	_FSDoSlideGadget
	clr.w	_FSCountDown
	rts

1$
	subi.w	#7,d0		;33+7=40, UpGad
	 bne.s	2$
	pea	1
11$	jsr	_FSScrollFileGads
	addq.w	#4,sp
	bra.s	ssgdone
2$
	subq.w	#1,d0		;33+7+1=41, DownGad
	 bne.s	3$
	pea	0
	bra	11$
3$
	subq.w	#1,d0		;33+7+2=42, UpDev
	 bne.s	4$
	pea	1
33$	jsr	_FSScrollDevGads
	addq.w	#4,sp
	bra.s	ssgdone
4$
	subq.w	#1,d0		;33+7+3=43, DownDev
	 bne.s	ssgrts
	pea	0
	bra	33$

ssgdone:
	move.w	#4,_FSCountDown
ssgrts:
	rts

* -----------------------------------------------------------------------
* FSScrollFileGads(direction)
*   BOOL direction;
*
* Scroll the file entries up or down by 1
* if (direction == 0)
*   scrollup;
* else
*   scrolldown;
* -----------------------------------------------------------------------
	XDEF	_FSScrollFileGads
_FSScrollFileGads:

;if (fsnumentries > 10)
	cmpi.w	#10,_fsnumentries	;Less than 10 entries?
	 ble.s	sfgdone			;Yep, can't scroll anyhow

;if (direction)
;  FSScrollDownGads(1);
	tst.w	6(sp)			;ScrollDown?
	 beq.s	sfgup			;Nope, scroll up
	pea	1
	jsr	_FSScrollDownGads
	addq.w	#4,sp
	bra.s	sfgpos

sfgup:
;FSScrollUpGads(1)
	pea	1
	jsr	_FSScrollUpGads
	addq.w	#4,sp

sfgpos:
* Reset the slide knob position the display movement
	jsr	_FSSetKnobPos
	jsr	_FSSetKnob		;Set new knob position

sfgdone:
	rts

* -------------------------------------------------------------------------
; FSScrollUpGads(count)
;  LONG count;
;
;  Single-step scroll the file entries up count times.
* -------------------------------------------------------------------------
	XDEF	_FSScrollUpGads
_FSScrollUpGads:
	link	a5,#0
	movem.l	d2-d7/a2/a6,-(sp)

	jsr	_FSUpdateSort
	 beq.s	sugdone

	movea.l	_FSRPort,a2

;for (i = 0; i < count; i++)
	moveq	#0,d7
	bra.s	sugchk1

sugloop1:
* If the bottom entry is not the last entry, scroll it up
	movea.l	_topfin,a0		;Grab top visible node
	move.w	fn_idnum(a0),d0		;Grab id number
	addi.w	#11,d0			;Plus 11
	cmp.w	_fsnumentries,d0	;Compare to last known entry
	 bgt.s	sugdone			;Yep, can't scroll up any more

* Bump the top pointer down the list to the next visible entry
	movea.l	_topfin,a0
1$	move.l	fn_Node+MLN_SUCC(a0),a0	;topfin = topfin->fn_Node.mln_Succ
	tst.w	fn_idnum(a0)		;while (topfin->idnum < 0)
	 blt	1$			;Not a valid entry
	move.l	a0,_topfin		;Here's our new top entry node

* Now reassign the visible file gadget array to the new node list
	jsr	_FSAssignNodes

* Display new file entries and redraw borders
;ClipBlit(rp, 9L, 24L, rp, 9L, 13L, 447L, 96L, 0xc0L);
	movea.l	a2,a0
	moveq	#9,d0
	moveq	#24,d1
	movea.l	a2,a1
	moveq	#9,d2
	moveq	#13,d3
	move.l	#447,d4
	moveq	#96,d5
	move.l	#$0c0,d6
	SYS	ClipBlit,_GfxBase

;FSEnableFGad(9);
	move.l	#9,-(sp)
	jsr	_FSEnableFGad		;Now draw the new gadget at bottom
	addq.w	#4,sp

	addq.w	#1,d7			;Bump scroll count

sugchk1:
	cmp.w	10(a5),d7		;Have we scrolled enough?
	 blt	sugloop1		;Nope, do again

sugdone:
	movem.l	(sp)+,d2-d7/a2/a6
	unlk	a5
	rts

* -------------------------------------------------------------------------
; FSScrollDownGads(count)
;   LONG count;
;
;  Single-step scroll the file entries down count times.
* -------------------------------------------------------------------------
	XDEF	_FSScrollDownGads
_FSScrollDownGads:
	link	a5,#0
	movem.l	d2-d7/a2/a6,-(sp)

	jsr	_FSUpdateSort
	 beq.s	sdgdone

	movea.l	_FSRPort,a2

;for (i = 0; i < count; i++)
	moveq	#0,d7
	bra.s	sdgchk1

sdgloop1:
;If the top entry is not the first entry, scroll it down
	movea.l	_topfin,a0		;Grab top node
	tst.w	fn_idnum(a0)		;idnum == 0?
	 beq.s	sdgdone			;Yep, can't scroll down any more

* Bump the top and bottom entry pointers down the list
1$	movea.l	fn_Node+MLN_PRED(a0),a0	;topfin = topfin->fn_Node.mln_Pred
	tst.w	fn_idnum(a0)		;while (topfin->idnum < 0)
	 blt	1$
	move.l	a0,_topfin		;New topfin

* Now reassign the visible file gadget array to the new node list
	jsr	_FSAssignNodes

* Display new file entries and redraw borders
;ClipBlit(rp, 9L, 13L, rp, 9L, 24L, 447L, 96L, 0xc0L)
	movea.l	a2,a0
	moveq	#9,d0
	moveq	#13,d1
	movea.l	a2,a1
	moveq	#9,d2
	moveq	#24,d3
	move.l	#447,d4
	moveq	#96,d5
	move.l	#$0c0,d6
	SYS	ClipBlit,_GfxBase

;FSEnableFGad(0);
	pea	0
	jsr	_FSEnableFGad		;Display the only new "gadget"
	addq.w	#4,sp

	addq.w	#1,d7			;Bump scroll count

sdgchk1:
	cmp.w	10(a5),d7		;Have we scrolled enough?
	 blt	sdgloop1		;Nope, do again

sdgdone:
	movem.l	(sp)+,d2-d7/a2/a6
	unlk	a5
	rts

* -------------------------------------------------------------------
* VOID FSAssignNodes(VOID)
* -------------------------------------------------------------------
	XDEF	_FSAssignNodes
_FSAssignNodes:
	movem.l	a2-a3,-(sp)

;for (tnode = topfin, i = 0; ;tnode = tnode->next)
	lea	_FSFileNodes,a3	;Grab base address of array of filegadgets
	movea.l	_topfin,a2		;Grab our top visible filenode
	moveq	#10,d0			;Fill 10 gadgets max
ran1:
	tst.l	fn_Node+MLN_SUCC(a2)	;Succ == 0?
	 beq.s	ragdone			;Ran out of list entries

;if (tnode->idnum >= 0)
	tst.w	fn_idnum(a2)		;Is this name "visible"?
	 blt.s	ragchkbot		;Nope, skip it
	move.l	a2,(a3)+		;FSFileNodes[i] = tnode
	subq.w	#1,d0			;One less slot available
	 beq.s	ragdone			;Filled all the slots, exit loop

ragchkbot:
	movea.l	fn_Node+MLN_SUCC(a2),a2
	bra	ran1			;check next node

ragdone:
	movem.l	(sp)+,a2-a3
	rts

* -----------------------------------------------------------------------
; VOID __stdargs FSDoFileGad(Ypos)
;   LONG Ypos;
;
;  Based on the mouse's Ypos passed, select file entry that user clicked.
* -----------------------------------------------------------------------
	XDEF	_FSDoFileGad
_FSDoFileGad:
	link	a5,#0
	movem.l	d2-d4/a2-a3/a6,-(sp)

	moveq	#0,d4
	move.w	10(a5),d4		;Grab gadget y position
	subi.w	#12,d4			;Less 12 (top edge of first entry)
	divu	#11,d4			; divided by 11 (height of each entry)
	ext.l	d4			;Blow remainder, this is our entry number

	move.w	d4,d0
	lsl.w	#2,d0			;Convert to long offset
	lea	_FSFileNodes,a2	;Grab base address of visible node_datas
	movea.l	0(a2,d0.w),a3		;Grab FSFileNodes[gadg_num]
	cmpa.l	#0,a3
	 beq	dfgdone			;If zero, not a active "gadget" slot

;strncpy(FSwstr, tinfo->alphadata + 26L, tinfo->namelength)
	movea.l	fn_info(a3),a3		;tinfo = tnode->info
	lea	_FSwstr,a2		;Grab address of worktext
	move.w	nd_namelength(a3),d0	;Push tinfo->namelength
	lea	nd_alphadata+26(a3),a1	;Push tinfo->alphadata + 26
	movea.l	a2,a0			;Push dest text
	jsr	astrncpy		;strncpy(FSwstr, filename, namelength)

;if (tinfo->filetype == 1)
	cmpi.w	#1,nd_filetype(a3)	;Did user click on a filename?
	 bne.s	dfgnotfile		;Nope, must be a dir

;if ( strcmp(FSFileInfo.Buffer, FSwstr) )
	movea.l	a2,a1			;Push FSwstr
	movea.l	_FSFileInfo,a0	;Push address of filebuffer
	jsr	astrcmp
	tst.w	d0			;Is this the same as what we already have?
	 beq.s	dfgnsname		;Yep, see if it was double-clicked

;strcpy(FSFileInfo.Buffer, FSwstr)
	movea.l	a2,a1
	movea.l	_FSFileInfo,a0
	jsr	astrcpy			;Otherwise copy new filename to filebuffer

	lea	_FSFileGad,a0 	;And refresh filename text gadget
	movea.l	_FSWin,a1
	suba.l	a2,a2
	moveq	#1,d0
	SYS	RefreshGList,_IntuitionBase
	bra	dfgfiledone

dfgnsname:
;if ( DoubleClick(fsoldsecs, fsoldmicros, fscursecs, fscurmicros &&
; (gadg_num == fsprevgadid) )
;  ConcatPathString();
	btst.b	#0,_fsflags+1	;FS_NO_DCLICK_EXIT?
	 bne	dfgfiledone		;Yep, programmer doesn't allow that

	cmp.w	_fsprevgadid,d4 	;Is it the same gadget?
	 bne	dfgfiledone		;Nope, can't be a double click

	move.l	_fsoldsecs,d0
	move.l	_fsoldmicros,d1
	move.l	_fscursecs,d2
	move.l	_fscurmicros,d3
	SYS	DoubleClick,_IntuitionBase
	tst.w	d0			;Is it within click speed parameters?
	 beq.s	dfgfiledone		;Nope

	jsr	_ConcatPathString 	;Construct full path text
	bra.s	dfgdone

;Clicked on a DIR
dfgnotfile:
	cmpi.w	#1,_fsvirgindir	;if (virgindir != 1)
	 beq.s	dfgdone 		;Don't concat if we haven't locked dir yet

;i = strlen(FSPathInfo.Buffer);
	move.l	_FSPathInfo,a0
	jsr	astrlen
	move.w	d0,d2			;Length of stuff already in path text

;if ( (strlen(FSwstr) + i) < PATHSTRSIZE)
	movea.l	a2,a0
	jsr	astrlen
	add.w	d2,d0
	cmpi.w	#PATHSTRSIZE-1,d0
	 bge.s	dfgtoodeep 		;Don't append path if no room!

	move.l	a2,-(sp)
	move.l	_FSPathInfo,-(sp)
	move.l	_FSPathInfo,-(sp)
	jsr	_ConcatDirFile		;ConcatDirFile(FSPathInfo->Buffer, FSPathInfo->Buffer, FSwstr)
	lea	12(sp),sp

	jsr	_FSDirFunc		;Do FSReq->dirfunc, if there is one
	tst.w	d0			;Avoid reading this dir?
	 beq.s	1$			;Nope, process it
	move.l	_FSPathInfo,a0
	clr.b	0(a0,d2.w)		;Else FSPathInfo->Buffer[oldstrlen] = 0
	bra.s	dfgdone
1$
	move.w	#1,_fsvirgindir	;virgindir = 1, read the new directory
	bra.s	dfgdone

dfgtoodeep:
	move.w	#12,_fstitstatus	;fstitstatus = 12
	jsr	_FSWinTitle
	suba.l	a0,a0
	SYS	DisplayBeep,_IntuitionBase
	bra.s	dfgdone

dfgfiledone:
	jsr	_FSFileFunc		;Call FSReq->filefunc, if there is one

dfgdone:
	move.w	d4,_fsprevgadid 	;fsprevgadid = gadg_num
	movem.l	(sp)+,d2-d4/a2-a3/a6
	unlk	a5
	rts

* --------------------------------------------------------------------- *
* LONG FSDirFunc(VOID);
*  If there is a fsrequest->dirfunc, this routine handles the interface.
* --------------------------------------------------------------------- *
	XDEF	_FSDirFunc
_FSDirFunc:
	move.l	d2,-(sp)

	moveq	#0,d0
	movea.l	_FSReq,a0		;Grab our FSRequest struct
	move.l	fs_dirfunc(a0),d2	;Is there a user dir function to call?
	 beq.s	1$			;Nope, that's all we have to do this click!

	jsr	_ConcatPathString 	;Combine dir/file as fullname for return
	clr.w	_FSDone		;Not done though, undo return status

	move.l	_FSWin,-(sp)
	move.l	_FSReq,-(sp)
	movea.l	d2,a0
	jsr	(a0)			;d0 = (FSReq->dirfunc)(FSReq, FSWin)
	addq.w	#8,sp
1$
	move.l	(sp)+,d2
	rts

* --------------------------------------------------------------------- *
* VOID FSFileFunc(VOID);
*  If there is a fsrequest->filefunc, this routine handles the interface.
* --------------------------------------------------------------------- *
	XDEF	_FSFileFunc
_FSFileFunc:
	move.l	d2,-(sp)

	movea.l	_FSReq,a0		;Grab our FSRequest struct
	move.l	fs_filefunc(a0),d2	;Is there a user file function to call?
	 beq.s	1$			;Nope, that's all we have to do this click!

	jsr	_ConcatPathString 	;Combine dir/file as fullname for return
	clr.w	_FSDone		;Not done though, undo return status

	move.l	_FSWin,-(sp)
	move.l	_FSReq,-(sp)
	movea.l	d2,a0
	jsr	(a0)			;d0 = (FSReq->filefunc)(FSReq, FSWin)
	addq.w	#8,sp
	tst.l	d0			;Return 0?
	 beq.s	1$			;Yep
	move.w	#1,_fsvirgindir	;Else re-read directory
1$
	move.l	(sp)+,d2
	rts

* -----------------------------------------------------------------------
* VOID FSSetFileGads(VOID)
*   Assign and render currently active file texts, disable remainder
* -----------------------------------------------------------------------
	XDEF	_FSSetFileGads
_FSSetFileGads:
	movem.l	d4-d5,-(sp)

	move.w	_fsnumentries,d5	;Grab number of entries
	 beq.s	3$			;No entries, nothing to set!

* Update visible list based on topfin point in linked list
	jsr	_FSAssignNodes

* Show all used gadgets
	moveq	#0,d4			;i = 0
	bra.s	2$
1$
	move.l	d4,-(sp)
	jsr	_FSEnableFGad
	addq.w	#4,sp
	addq.w	#1,d4			;Bump gadget count
2$
	cmpi.w	#10,d4			;Hit highest gadget?
	 bcc.s	3$			;Yep, done
	cmp.w	d5,d4			;Else, Hit last gadget?
	 bcs	1$			;Nope enable another
3$
	movem.l	(sp)+,d4-d5
	rts

* ------------------------------------------------------------------------
* SetRootDir()
* Chop off all subdirectory names contained in FSPathInfo, moving up to
* root name if possible.  Flag to read new directory in any case.
* ------------------------------------------------------------------------
	XDEF	_SetRootDir
_SetRootDir:
;if ( (tstr = strchr(FSPathInfo.Buffer, ':')) != 0L)
	moveq	#':',d0
	movea.l	_FSPathInfo,a0
	jsr	aindex
	tst.l	d0
	 beq.s	1$			;Nope

	clr.b	1(a0)			;Otherwise clobber the char after ':'
	move.w	#1,_fsvirgindir	;virgindir = 1, read directory

1$	rts

* ------------------------------------------------------------------------
; SetParentDir()
;  Chop off last directory name contained in FSPathInfo, moving up one level
; if possible.
* ------------------------------------------------------------------------
	XDEF	_SetParentDir
_SetParentDir:
	movem.l	a2-a3,-(sp)

;if (FSPathInfo.Buffer[0])
	movea.l	_FSPathInfo,a3	;Grab pointer to path text
	tst.b	(a3)			;Is it of zero length?
	 beq.s	spddone			;Yep, at root already

;tstr = rindex(FSPathInfo.Buffer, '/')
	moveq	#'/',d0
	movea.l	a3,a0
	jsr	arindex
	tst.l	d0			;Grab index to last occurance of slash
	 bne.s	spdnotsub		;If we got one that is

; if ( tstr = rindex(FSPathInfo.Buffer, ':') )
;        tstr++;
	moveq	#':',d0
	movea.l	a3,a0
	jsr	arindex			;Grab index to last occurance of colon
	tst.l	d0
	 beq.s	spdnotroot		;Oops, no colon either, at top of path?
	addq.w	#1,a0			;Move past the ':'
	bra.s	spdnotsub		;Terminate the text
;else
spdnotroot:
	movea.l	a3,a0			;tstr = (BYTE *)FSPathInfo.Buffer

spdnotsub:
	clr.b	(a0)			;*tstr = '\x0'
	move.w	#1,_fsvirgindir	;virgindir = 1, read the new directory

spddone:
	movem.l	(sp)+,a2-a3
	rts

* -----------------------------------------------------------------
;HCompGad(rp, g2)
;  struct RastPort *rp;
;  struct Gadget *g2;
;
;  Given a rastport and a gadget pointer, highlight or de-highlight
; a gadget using RectFill in COMPLEMENT mode.
* -----------------------------------------------------------------
hrp	EQU	16
hgad	EQU	20
	XDEF	_HCompGad
_HCompGad:
	movem.l	d2-d3/a6,-(sp)

	movea.l	hrp(sp),a1
	moveq	#2,d0
	SYS	SetDrMd,_GfxBase 	;SetDrMd(rp, COMPLEMENT)

	movea.l	hgad(sp),a0		;Grab gadget pointer
	moveq	#0,d0
	move.w	4(a0),d0		;x0 = (ULONG)g2->LeftEdge

	move.l	d0,d2
	add.w	8(a0),d2
	subq.w	#1,d2			;x1 = x0 + g2->Width - 1L

	moveq	#0,d1
	move.w	6(a0),d1		;y0 = (ULONG)g2->TopEdge

	move.l	d1,d3
	add.w	10(a0),d3
	subq.w	#1,d3			;y1 = y0 + g2->Height - 1L

	movea.l	hrp(sp),a1
	SYS	RectFill		;RectFill(rp, x0, y0, x1, y1)

	movea.l	hrp(sp),a1
	moveq	#1,d0
	SYS	SetDrMd			;SetDrMd(rp, JAM2)

	movem.l	(sp)+,d2-d3/a6
	rts

* -----------------------------------------------------------------
;HCompEntry(num)
;  LONG num;
;
;   Highlight or de-highlight a slot in the filename area, given its
; entry number
* -----------------------------------------------------------------
	XDEF	_HCompEntry
_HCompEntry:
	movem.l	d2-d3/a2/a6,-(sp)

	movea.l	_FSRPort,a2		;Grab window

	movea.l	a2,a1
	moveq	#2,d0
	SYS	SetDrMd,_GfxBase 	;SetDrMd(rp, COMPLEMENT)

	moveq	#5,d0			;x0 = (ULONG)g2->LeftEdge
	move.l	#460,d2			;x1 = x0 + g2->Width - 1L

	move.w	16+4+2(sp),d1
	mulu	#11,d1
	add.w	#12,d1			;y0 = (ULONG)g2->TopEdge

	move.l	d1,d3
	add.w	#9,d3			;y1 = y0 + g2->Height - 1L

	movea.l	a2,a1
	SYS	RectFill		;RectFill(rp, x0, y0, x1, y1)

	movea.l	a2,a1
	moveq	#1,d0
	SYS	SetDrMd			;SetDrMd(rp, JAM2)

	movem.l	(sp)+,d2-d3/a2/a6
	rts

* --------------------------------------------------------------------- *
* VOID FSDoSortGadget(type)
*   LONG type;
* --------------------------------------------------------------------- *
dosgtype	EQU	4+4*3

	XDEF	_FSDoSortGadget
_FSDoSortGadget:
	movem.l	d2/a2/a6,-(sp)

	movea.l	_FSReq,a0
	moveq	#0,d0
	move.w	fs_sorttype(a0),d0
	cmp.w	dosgtype+2(sp),d0
	 beq	dosgsame

* Unhighlight previous setting
	tst.w	d0			;Alphabetize?
	 bne.s	1$
	lea	_AlphaGad,a0
	bra.s	3$
1$
	subq.w	#1,d0			;FileSize?
	 bne.s	2$
	lea	_SizeGad,a0
	bra.s	3$
2$
	subq.w	#1,d0			;Time?
	 bne.s	4$			;Nope, nosort previously
	lea	_TimeGad,a0
3$
	bclr.b	#7,gg_Flags+1(a0)	;gad->Flags &= ~SELECTED

	movea.l	_FSWin,a1
	suba.l	a2,a2
	moveq	#1,d0
	SYS	RefreshGList,_IntuitionBase ;RefreshGList(gad, FSWin, 0L, 1)
4$
* Set new sort type
	movea.l	_FSReq,a0
	move.l	dosgtype(sp),d1
	move.w	d1,fs_sorttype(a0)

	movea.l	_fnList,a0
	movea.l	MLH_HEAD(a0),a0
	tst.l	fn_Node+MLN_SUCC(a0)
	 beq.s	dosgdone		;No list, don't bother sorting
	tst.l	_topfin		;Grab current topfin
	 beq.s	5$			;No currently visible files
	clr.w	_fsneedshow 	;fsneedshow = 0, needs sorting
	bra.s	6$
5$
	move.w	#-1,_fsneedshow	;No topfin, fsneedshow = -1
6$
dossort:
* Sort the list using the new sort type
	move.l	d1,-(sp)
	jsr	_SortNodes		;SortNodes(type)
	addq.w	#4,sp

	jsr	_FSUpdateSort

	jsr	_FSWinTitle		;Reset title to previous
	bra.s	dosgdone

dosgsame:
	move.w	#-1,fs_sorttype(a0)

dosgdone:
	movem.l	(sp)+,d2/a2/a6
	rts

* -----------------------------------------------------------------------
; SortNodes(key)
;   LONG key;
; Selection sort list of filenames using one of three key types
* -----------------------------------------------------------------------
	XDEF	_SortNodes
_SortNodes:
	movem.l	d4/a2-a3/a6,-(sp)

	IFD	BENCHMARK
	jsr	_StartTime
	ENDC

	move.l	4+4*4(sp),d4		;Grab our key type
	 blt	snsortdone		;No sort wanted
	cmpi.w	#1,_fsnumentries	;More than one entry?
	 ble	snsortdone		;Nope, don't bother sorting

	move.w	_fstitstatus,-(sp)	;Save old titstatus
	move.w	#28,_fstitstatus
	jsr	_FSWinTitle		;FSWinTitle("Sorting...")
	move.w	(sp)+,_fstitstatus	;Restore previous titlemsg

* Convert Exec list to a simple head->next->NULL type
	movea.l	_fnList,a3		;Grab old list
	movea.l	MLH_TAILPRED(a3),a1	;a0 = last entry in list
	movea.l	MLH_HEAD(a3),a0
	clr.l	fn_Node+MLN_SUCC(a1)	;Zap last nodes next field

	NEWLIST	a3			;New the old list

	move.l	d4,d0			;d0 = sorttype, a0 = headnode
	jsr	_FSListSort		;list_sort(headnode, key) (a0/d0)
	movea.l	d0,a1			;Grab new head of sorted list

* Now add the new nodes back into the list
	movea.l	4,a6			;Execbase calls

1$	cmpa.l	#0,a1			;Grab next node in new list
	 beq.s	2$			;End of list
	movea.l	fn_Node+MLN_SUCC(a1),a2	;node = node->fn_Node.mln_Succ

	movea.l	a3,a0
	SYS	AddTail			;AddTail(fnList, newnode)

	movea.l	a2,a1			;a2 = next node
	bra	1$			;Add the next node
2$
snsortdone:
	IFD	BENCHMARK
	pea	_timermsg1
	jsr	_StopTime
	addq.w	#4,sp
	ENDC

	movem.l	(sp)+,d4/a2-a3/a6
	rts

* --------------------------------------------------------------------
* struct file_node *FSListSort (list, type)
*   struct file_node *list;
*   long type;
*
*   Assumes a list of structures, with a pointer to "next" as the first
*   field.  It reorders the list into ascending order, and returns the
*   new first node's address.  It is order N log(N).
*
*   The compare routine should return 0 if the items are in order, and 1
*   if they are not.  If the compare routine returns 0 in case of
*   equality, the sort will be stable.
*
*   This routine depends upon compiler-dependant struct layout, but this
*   assumption is likely to be fairly commonly valid.
*
*   The basic notion of this sort is to make sorted sublists longer and
*   longer by merging.  On the Nth pass through the list, sorted
*   sublists of length 2^(N-1) are produced.  Eventually, the entire
*   list is sorted, in log2(N)+1 passes through the list.  There is
*   extra bookkeeping overhead, but minimal extra storage space needed.
*   Counts and clever pointer management substitute for extra "glue"
*   nodes.
*
*   while more than one list
*      while not at end of composite lists
*            for each merge_length(m) block
*                  merge first items in lists onto current output list
*            toggle current output list
*
* Register parameters:
* struct file_node *FSListSort __ARGS((struct file_node *, LONG));
*          d0                                a0             d0
* --------------------------------------------------------------------- *
flsold1	equ	-4
flsold0	equ	-8
flsnew1	equ	-12
flsnew0	equ	-16
flscnt1	equ	-20
flscnt0	equ	-24
flsffn1	equ	flscnt0-16
flsffn0	equ	flsffn1-16
flssend	equ	flsffn0-4

* --------------------------------------------------------------------- *
	XDEF	_FSListSort
_FSListSort:
	link	a5,#flssend
	movem.l	d2-d7/a2-a3,-(sp)

	move.l	d0,d3			;d3 = sorttype
	clr.l	flsffn0+LN_SUCC(a5)	;front[0].next = 0
	move.l	a0,flsffn1+LN_SUCC(a5)	;front[0].next = list

	moveq	#1,d7			;m = 1
	moveq	#0,d6			;hm = 0

flsLoop1:
	tst.l	flsffn1+LN_SUCC(a5)	;while (front[1].next)
	 beq	flsdone

	lea	flsffn0(a5),a0
	move.l	a0,flsnew0(a5)		;new[0] = &front[0]
	lea	flsffn1(a5),a0
	move.l	a0,flsnew1(a5)		;new[1] = &front[1]
	move.l	flsffn0+LN_SUCC(a5),flsold0(a5)	;old[0] = front[0].next
	move.l	flsffn1+LN_SUCC(a5),flsold1(a5)	;old[1] = front[1].next
	clr.l	flscnt0(a5)		;count[0] = 0
	clr.l	flscnt1(a5)		;count[1] = 0
	moveq	#0,d4			;n = 0

	moveq	#0,d5			;items_merged = 0
flsLoop2:
	tst.l	flsold0(a5)		;old[0]?
	 bne.s	1$			;Yep, get an item from this list
	tst.l	flsold1(a5)		;old[1]?
	 beq	flsNext2		;Nope, both lists exhausted

1$	cmp.l	d7,d5			;if (items_merged >= m)
	 bcs.s	2$			;Nope, m > items_merged
	moveq	#0,d5			;items_merged = 0
	bchg.l	#0,d4			;n = 1 - n
	clr.l	flscnt0(a5)		;count[0] = 0
	clr.l	flscnt1(a5)		;count[1] = 0

2$	tst.l	flsold0(a5)		;old[0]?
	 beq.s	3$
	tst.l	flsold1(a5)		;old[1]?
	 beq.s	3$
	cmp.l	flscnt0(a5),d6		;count[0] < hm?
	 bls.s	3$			;Nope, hm >= count[0]
	cmp.l	flscnt1(a5),d6		;count[1] < hm?
	 bls.s	3$			;Nope

	movea.l	flsold1(a5),a1
	movea.l	fn_info(a1),a1
	movea.l	flsold0(a5),a0
	movea.l	fn_info(a0),a0
	move.l	d3,d0
	jsr	_CompareNodes		;CompareNodes(type, old[0]->info, old[1]->info)
	lsl.w	#2,d0			;d0 = result of compare (0 or 1)

	move.w	d4,d1
	lsl.w	#2,d1			;d1 = n<<2
	movea.l	flsnew0(a5,d1.w),a1	;a1 = new[n]
	movea.l	flsold0(a5,d0.w),a0	;a0 = old[o]
	move.l	a0,LN_SUCC(a1)		;new[n]->next = old[o]
	move.l	LN_SUCC(a0),flsold0(a5,d0.w)  ;old[o] = old[o]->next
	addq.l	#1,flscnt0(a5,d0.w)	;count[o]++
	bra.s	flsNext1
3$
flselse1:
	tst.l	flsold0(a5)		;old[0]?
	 beq.s	2$			;Nope, use old[1]
	cmp.l	flscnt0(a5),d6		;count[0] < hm?
	 bls.s	2$			;Nope, count[0] >= hm, use old[1]
1$	lea	flsold0(a5),a0		;else a0 = &old[0]
	bra.s	3$
2$	lea	flsold1(a5),a0		;a0 = &old[1]
3$
	move.w	d4,d1
	lsl.w	#2,d1
	movea.l	flsnew0(a5,d1.w),a1	;a1 = new[n]
	move.l	(a0),LN_SUCC(a1)	;new[n]->next = old[o]
	movea.l	(a0),a1
	move.l	LN_SUCC(a1),(a0)	;old[o] = old[o]->next

flsNext1:
	move.w	d4,d0
	lsl.w	#2,d0
	movea.l	flsnew0(a5,d0.w),a0	;a0 = new[n]
	move.l	LN_SUCC(a0),flsnew0(a5,d0.w) ;new[n] = new[n]->next
	addq.l	#1,d5			;items_merged++
	bra	flsLoop2

flsNext2:
	movea.l	flsnew0(a5),a0
	clr.l	LN_SUCC(a0)		;new[0]->next = 0
	movea.l	flsnew1(a5),a0
	clr.l	LN_SUCC(a0)		;new[1]->next = 0
	move.l	d7,d6			;hm = m
	lsl.l	#1,d7			;m *= 2
	bra	flsLoop1
	
flsdone:
	move.l	flsffn0+LN_SUCC(a5),d0	;return(front[0].next)

	movem.l	(sp)+,d2-d7/a2-a3
	unlk	a5
	rts

* -------------------------------------------------------------------------
; d0 = WORD CompareNodes(d0, a0, a1)
;   WORD key = d0;
;   struct node_data *a = a0, *b = a1;
;   
; Compare data in two nodes, based upon key type.  Return boolean result
; of the comparison in d0. Note that this is a register based function.
* -------------------------------------------------------------------------
	XDEF	_CompareNodes
_CompareNodes:
	move.w	nd_filetype(a0),d1
	cmp.w	nd_filetype(a1),d1	;Comparing same type of entries?
	 beq.s	3$			;Yep, use sort for compare

	btst.b	#3,_fsflags+1	;Want files first?
	 bne.s	2$			;Yep
	btst.b	#4,_fsflags+1	;Want dirs first?
	 beq.s	3$			;Don't care, see if there is a sort

	cmp.w	nd_filetype(a1),d1	;Recompare
	 bcs.s	cnmatch			;a0->filetype = 2, a1->filetype = 1
	bra.s	cnnomatch		;a0->filetype = 1, a1->filetype = 2
2$
	cmp.w	nd_filetype(a1),d1	;Recompare
	 bhi.s	cnmatch			;a0->filetype = 2, a1->filetype = 1
	bra.s	cnnomatch		;a0->filetype = 1, a1->filetype = 2
3$
	tst.w	d0
	 blt.s	cnmatch			;No sort, append to end
	 bne.s	cnnotalpha		;Not zero, not alphabetize

cnnalpha:
	pea	nd_alphadata+26(a1)
	pea	nd_alphadata+26(a0)
	jsr	_lstrcmp		;return(lstrcmp(a->alphadata + 26L, b->alphadata + 26L) > 0)
	addq.w	#8,sp
	tst.w	d0
	 ble.s	cnnomatch
	bra.s	cnmatch
	
cnnotalpha:
	cmpi.w	#1,d0			;Sort by size?
	 bne.s	cnnotsize		;Nope, must be by date

	move.l	nd_filesize(a0),d0
	cmp.l	nd_filesize(a1),d0	;return(a->filesize > b->filesize)
	 beq	cnnalpha		;Same size, sort alpha
	 bcs.s	cnnomatch		;a1 > a0
	bra.s	cnmatch

cnnotsize:
	move.l	nd_days(a0),d0
	cmp.l	nd_days(a1),d0		;if (a->days > b->days)
	 beq.s	cntstmins		;Nope, a->days == b->days, check minutes
	 bls.s	cnnomatch		;Nope, a->days < b->days
	bra.s	cnmatch

cntstmins:
;t1 = (a->minutes - b->minutes) * 3000L + (a->ticks - b->ticks)
	move.l	nd_minutes(a0),d0
	sub.l	nd_minutes(a1),d0
	muls	#3000,d0

	move.l	nd_ticks(a0),d1
	sub.l	nd_ticks(a1),d1
	add.l	d1,d0

	tst.l	d0			;return(t1 > 0L);
	 beq	cnnalpha		;Same date, sort alpha
	 bgt.s	cnmatch

cnnomatch:
	moveq	#0,d0
	bra.s	cndone

cnmatch:
	moveq	#1,d0
cndone:
	rts

* ---------------------------- lstrcmp -------------------------------
* LONG lstrcmp(astr, bstr)
*   BYTE *astr, *bstr;	
*
* Compare two texts for lexigraphic order, ignoring case differences
*---------------------------------------------------------------------
	XDEF	_lstrcmp
_lstrcmp:
	movem.l	4(sp),a0/a1		;Grab astr, bstr

;for(;*a && tolower(*a) == tolower(*b); a++, b++)
lccstart:
	move.b	(a0)+,d0		;Grab a char from astr
	cmpi.b	#$40,d0			;less than @ character?
	 bls.s	1$			;Yep
	cmpi.b	#$5a,d0			;Greater than Z?
	 bhi.s	1$			;Yep
	addi.b	#$20,d0
1$
	move.b	(a1)+,d1		;Grab a char from bstr
	cmpi.b	#$40,d1			;less than @ character?
	 bls.s	2$			;Yep
	cmpi.b	#$5a,d1			;Greater than Z?
	 bhi.s	2$			;Yep
	addi.b	#$20,d1
2$
	tst.b	d0			;End of astr?
	 beq.s	3$
	cmp.b	d1,d0			;Are they the same character?
	 beq	lccstart		;Yep, compare next pair of chars
3$
	sub.b	d1,d0			;return(tolower(*astr) - tolower(*bstr))
	ext.w	d0
	ext.l	d0
	rts

*----------------------------------------------------------------------
* Search a text for wild characters, return 1 if found
*----------------------------------------------------------------------
	XDEF	_iswild
_iswild:
	movea.l	4(sp),a0		;Grab text pointer
	moveq	#0,d0			;Clear out our character register
ischk1:
	move.b	(a0)+,d0		;Grab a char
	 beq.s	iswdone			;Might be end of text?
	cmpi.b	#'*',d0			;Is it *?
	 beq.s	iswdone			;yep, is wild
	cmpi.b	#'?',d0			;Is it a qmark
	 bne	ischk1			;Nope, check next character
	rts				;Otherwise it is wild

iswdone:
	rts


* ------------------------------------------------------------------------
; Compare a wild card name with a normal name
; WORD wildmatch (name, wild)
;   BYTE *name, *wild;
* ------------------------------------------------------------------------
	XDEF	_wildmatch
_wildmatch:
	link	a5,#-64
	movem.l	d3/a2-a3,-(sp)

	movem.l	8(a5),a2-a3		;Grab name/pattern
	lea	-64(a5),a0		;back[0][0]
	lea	-60(a5),a1		;back[0][1]

	moveq	#0,d3			;bi = 0

wmloop1:
	tst.b	(a2)			;End of name?
	 bne.s	wmnoteon
	tst.b	(a3)			;End of pattern?
	 beq	wmmatched		;Yep, we matched

wmnoteon:
	cmpi.b	#'*',(a3)		;Is it a splat?
	 bne.s	wmnotstar		;Nope, maybe '?'

	cmpi.w	#64,d3			;Have we hit max expression depth?
	 beq	wmnomatch		;Yep, ran out of room in recursion table

;back[bi][0] = w
	move.l	a3,0(a0,d3.w)		;Stash pointer to this '*' in table

;back[bi][1] = n
	move.l	a2,0(a1,d3.w)

	addq.w	#8,d3			;++bi
	addq.w	#1,a3			;++w
	bra	wmloop1			;Check next

wmgoback:
	subq.w	#8,d3			;--bi
	move.l	a0,d0
wmback1:
	tst.w	d3			;while (bi >= 0 && *back[bi][1] == '\x0')
	 blt.s	wmbacked
	movea.l	0(a1,d3.l),a0
	tst.b	(a0)
	 bne.s	wmbacked

	subq.w	#8,d3			;--bi
	bra	wmback1

wmbacked:
	tst.w	d3			;if (bi < 0)
	 blt.s	wmnomatch		;return (0)

	movea.l	d0,a0
	movea.l	0(a0,d3.w),a3		;w = back[bi][0] + 1
	addq.w	#1,a3	

	addq.l	#1,0(a1,d3.w)
	movea.l	0(a1,d3.l),a2		;n = ++back[bi][1]

	addq.w	#8,d3			;++bi
	bra	wmloop1

wmnotstar:
	cmpi.b	#'?',(a3)		;Is it '?'
	 bne.s	wmnotqmark

	tst.b	(a2)			;Reached end of text?
	 bne.s	wmincpoint		;Nope, move on to next char

	tst.w	d3			;Are we at top level of expression?
	 beq.s	wmnomatch		;Yep, expression didn't match
	bra	wmgoback		;Otherwise pop a level and try to match

wmnotqmark:
	move.b	(a2),d0			;Grab a char from bstr
	cmpi.b	#$40,d0			;less than @ character?
	 bls.s	1$			;Yep
	cmpi.b	#$5a,d0			;Greater than Z?
	 bhi.s	1$			;Yep
	addi.b	#$20,d0
1$
	move.b	(a3),d1			;Grab a char from bstr
	cmpi.b	#$40,d1			;less than @ character?
	 bls.s	2$			;Yep
	cmpi.b	#$5a,d1			;Greater than Z?
	 bhi.s	2$			;Yep
	addi.b	#$20,d1
2$
	cmp.b	d0,d1			;*n = *w?
	 beq.s	wmincpoint		;Yep, move on past

	tst.w	d3			;Are we at top expression level?
	 beq.s	wmnomatch		;Yep, they didn't match
	bra	wmgoback		;Nope, process next part

wmincpoint:
	tst.b	(a2)			;Done with name?
	 beq.s	wmnamend		;Yep
	addq.w	#1,a2			;Otherwise increment name pointer

wmnamend:
	tst.b	(a3)			;End of pattern?
	 beq.s	wmmatched		;Yep, we matched
	addq.w	#1,a3			;Otherwise inc wild pointer, match next char
	bra	wmloop1

wmmatched:
	moveq	#1,d0
	bra.s	wmdone

wmnomatch:
	moveq	#0,d0

wmdone:
	movem.l	(sp)+,d3/a2-a3
	unlk	a5
	rts


* --------------------------------------------------------------------- *
* LONG MakePathString(lock, dest)
*                      a0     a1
*   struct FileLock *lock;
*   BYTE *dest;
*
* DESCRIPTION:
*   Given text and a filelock, construct entire pathname and
* return in dest.
* --------------------------------------------------------------------- *
	XDEF	_MakePathString
_MakePathString:
	link	a5,#0
	movem.l	d2-d5/d7/a2-a3/a6,-(sp)

* Grab pointer to lock and dest text to fill
	move.l	8(a5),d3		;d3 = lock
	movea.l	12(a5),a2		;a2 = dest
	clr.b	(a2)			;NULL terminate dest
	moveq	#0,d5			;LockFlag = 0

* Allocate a FileInfoBlock for local use
	moveq	#0,d1
	move.l	#fib_SIZEOF,d0
	SYS	AllocMem,4		;AllocMem(sizeof(fib), 0L)
	move.l	d0,d7			;d7 = *fib
	 beq	mpsfailed		;Whoops no mem? return!

	movea.l	_DOSBase(a4),a6		;DOSBase calls from here on

* while (lock != 0)
1$
	tst.l	d3			;Got a lock?
	 beq.s	mpsokay			;Nope, must be at root

* Examine the current lock
	move.l	d3,d1
	move.l	d7,d2
	SYS	Examine			;Examine(lock, fib)
	tst.l	d0			;Okay?
	 beq.s	mpsfailed		;Nope, some sort of dos failure?

	movea.l	d7,a1
	cmpi.b	#' ',fib_FileName(a1)	;if (fib->fib_FileName[0] >= ' ')
	 bcs.s	3$			;Nope, don't bother inserting?

	tst.b	(a2)			;if (dest[0] != 0)
	 beq.s	2$
	lea	_SlashStr(a4),a1
	movea.l	a2,a0
	jsr	_InsertPathString	;InsertPathString(dest, "/");
2$
	movea.l	d7,a1
	lea	fib_FileName(a1),a1
	movea.l	a2,a0
	jsr	_InsertPathString	;InsertPathString(dest, fib->fib_FileName)
3$
* Okay, move up one directory
	move.l	d3,d4			;oldlock = lock

	move.l	d3,d1
	SYS	ParentDir
	move.l	d0,d3			;lock = ParentDir(lock)

	tst.w	d5			;LockFlag set?
	 bne.s	4$			;Yep, unlock
	moveq	#1,d5			;Else LockFlag = 1, unlock next time
	 bra	1$			;Next directory up
4$
	move.l	d4,d1
	SYS	UnLock			;UnLock(oldlock)
	bra	1$			;Examine

mpsokay:
* See if root was RAM:, special case
	movea.l	d7,a1			;a1 = fib
	cmpi.b	#' ',fib_FileName(a1)	;if (fib->fib_FileName[0] >= ' ')
	 bcc.s	1$			;Yep, not 1.1/1.2 RAM:
	lea	_RamDirNameStr(a4),a1	;Else...
	movea.l	a2,a0
	jsr	_InsertPathString	;InsertPathString(dest, "RAM:")
	bra.s	mpsdone
1$
* Find last slash we tacked on, change to a colon, or, add a colon
	moveq	#'/',d0
	movea.l	a2,a0
	jsr	aindex			;d0 = strchr(dest, '/')
	tst.l	d0			;Do we have a slash?
	 beq.s	2$			;Nope, at root....
	movea.l	d0,a0
	move.b	#':',(a0)		;Else change first '/' to a ':'
	bra.s	mpsdone

* No slash, must be locked at the root.  Append a colon to the dest.
2$
	lea	_ColonStr(a4),a1	
	movea.l	a2,a0
	jsr	astrcat			;strcat (dest, ":")
	bra.s	mpsdone

* Come here if an error occured, return empty text to caller
mpsfailed:
	clr.b	(a2)			;dest[0] = (BYTE)0
	moveq	#0,d3			;return (0L)
	bra.s	mpsdeall

* Come here if everything is okay, deallocate FileInfoBlock
mpsdone:
	moveq	#1,d3			;return (1L)

mpsdeall:
	tst.l	d7			;Did we allocate a fib?
	 beq.s	mpsfinis		;nope
	movea.l	d7,a1			;Else free the memory
	move.l	#fib_SIZEOF,d0
	SYS	FreeMem			;FreeMem(fib, sizeof(fib))

mpsfinis:
	move.l	d3,d0			;Put return value in d0
	movem.l	(sp)+,d2-d5/d7/a2-a3/a6
	unlk	a5
	rts

* --------------------------------------------------------------------- *
* VOID InsertPathString(dest, source)
*                        a0     a1
*   BYTE *dest, *source;
*
* DESCRIPTION:
*   Insert source text into dest text.
* Special case for source length == 0, source must be RAM.
* --------------------------------------------------------------------- *
	XDEF	_InsertPathString
_InsertPathString:
	movem.l	d7/a2-a3,-(sp)

	movea.l	a1,a3			;a3 = source
	move.l	a0,a2			;a2 = dest

	movea.l	a3,a0
	jsr	astrlen
	move.l	d0,d7			;d7 = strlen(source)

1$	movea.l	a2,a0
	jsr	astrlen			;d0 = strlen(dest)

	addq.w	#1,d0			;Bump the length to include zero byte at end
	movea.l	a2,a1
	adda.w	d7,a1			;Push dest + slen
	movea.l	a2,a0			;Push dest
	jsr	amovmem			;amovmem(dest, dest + slen, strlen(dest) + 1)

	move.w	d7,d0
	movea.l	a2,a1
	movea.l	a3,a0
	jsr	amovmem			;amovmem(source, dest, slen)

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

* --------------------------------------------------------------------- *
* BYTE *myrindex(text, searchchar)
*   BYTE *text;
*   LONG searchchar;
*
* C stack version of arindex.
* --------------------------------------------------------------------- *
	XDEF	_myrindex
_myrindex:
	movea.l	4(sp),a0
	move.l	8(sp),d0

* --------------------------------------------------------------------- *
* BYTE *aindex(BYTE *, BYTE);
* --------------------------------------------------------------------- *
	XDEF	aindex
aindex:
1$	cmp.b	(a0),d0
	 beq.s	aifound
	tst.b	(a0)+
	 beq.s	ainomatch
	bra	1$

ainomatch:
	moveq	#0,d0
	rts

aifound:
	move.l	a0,d0
	rts

* --------------------------------------------------------------------- *
* BYTE *arindex(BYTE *, BYTE);
* --------------------------------------------------------------------- *
	XDEF	arindex
arindex:
	move.l	a0,d1			;Copy start of string for compares

1$	tst.b	(a0)+			;Find end of text
	 bne	1$

2$	cmp.b	-(a0),d0		;Now work backward comparing along the way
	 beq.s	rifound			;..Until we find a match
	cmpa.l	d1,a0			;...or hit the beginning of the text
	 bgt	2$

rinomatch:
	moveq	#0,d0
	rts

rifound:
	move.l	a0,d0
	rts	

* ------------------------------------------------------------------------- *
*  d0            a0
* LONG astrlen(text)
*   BYTE *text;
*
* Synopsis: Calculate length of null-byte terminated text.
* a0 returns pointing to null char at end of text
* d0 returns number of bytes in the text
* ------------------------------------------------------------------------- *
	XDEF	astrlen
astrlen:
	move.l	a0,d0			;Save initial address
1$	tst.b	(a0)+			;Test a byte for zero-ness
	 bne	1$

	suba.l	d0,a0			;Minus initial address
	subq.w	#1,a0			;Less one for overshoot
	exg	d0,a0			;swap em
	rts

* ------------------------------------------------------------------------- *
* astrcat()
* Takes text in a0, appends text in a1
* a0 returns pointing to null at end of final text
* ------------------------------------------------------------------------- *
	XDEF	astrcat
astrcat:
* Find end of first text
1$	tst.b	(a0)+
	 bne	1$
	subq.w	#1,a0			;Back up to null char

* Append second text
2$	move.b	(a1)+,(a0)+
	 bne	2$
	subq.w	#1,a0			;a0 = the null at end of final text
	rts

* ------------------------------------------------------------------------- *
* astrcpy()
* Takes text in a0, copies text in a1
* a0 returns pointing to null at end of final text
* ------------------------------------------------------------------------- *
	XDEF	astrcpy
astrcpy:
* Copy second text
1$	move.b	(a1)+,(a0)+
	 bne	1$
	subq.w	#1,a0			;Back up to null
	rts

* ------------------------------------------------------------------------- *
* astrcmp()
* Takes text in a0, compares to text in a1
* a0 returns pointing to first difference, d0 contains the difference
* or zero if the two texts are identical
* ------------------------------------------------------------------------- *
	XDEF	astrcmp
astrcmp:
1$	move.b	(a0)+,d0		;Grab a char from A
	move.b	(a1)+,d1		;Grab a char from B
	tst.b	d0			;Hit end of A?
	 beq.s	2$			;Yep, diff em
	cmp.b	d1,d0			;Else the same char?
	 beq	1$			;Compare next pair
2$	sub.b	d1,d0			;A - B
	ext.w	d0
	ext.l	d0
	rts

* -----------------------------------------------------------------------
* safestrcpy(dest, source, length)
*   Copies at most length chars of source to dest, if source < length
* don't pad with nulls like strncpy.  Always null terminates dest.
* -----------------------------------------------------------------------
	XDEF	_safestrcpy
_safestrcpy:
	movem.l	4(sp),a0/a1		;Grab dest and source pointers
	move.w	14(sp),d0		;Grab count

* ------------------------------------------------------------------------- *
* astrncpy()
* Takes text in a1, copies d0 bytes to text in a0.
* a0 returns pointing to null at end of final text.
* Dest text is always null terminated.
* ------------------------------------------------------------------------- *
	XDEF	astrncpy
astrncpy:
1$	move.b	(a1)+,(a0)+
2$	dbeq	d0,1$
	subq.w	#1,a0
	clr.b	(a0)			;Null terminate dest
	move.l	a0,d0
	rts

* ------------------------------------------------------------------------- *
* amovmem()
* Takes text in a0, copies d0 bytes to text in a1. Correctly handles
* overlapping memory.
* ------------------------------------------------------------------------- *
	XDEF	amovmem
amovmem:
	cmpa.l	a0,a1			;Low to high or high to low?
	 bcs.s	2$			;High to low, copy forward
	adda.w	d0,a0			;Else start at end, copy backward
	adda.w	d0,a1

1$	move.b	-(a0),-(a1)
	subq.w	#1,d0
	 bgt	1$
	bra.s	amdone

2$	move.b	(a0)+,(a1)+
	subq.w	#1,d0
	 bgt	2$
amdone:
	rts

* --------------------------------------------------------------------- *
* 			END OF SOURCE
* --------------------------------------------------------------------- *
	END
