NEW
  AUTO 3,1
				.LIST OFF
*--------------------------------------
X.MAX.RECURSE	.EQ 8
*--------------------------------------
* Uses: 
* (ZP)
* ZPPtr1		.BS 2
* ZPPtr2		.BS 2
* ZPFileName	.BS 2
* ZPFileStat	.BS 2
* (pData)
*  Index		.BS 1
*  hDIRs		.BS X.MAX.RECURSE+1
*  hDIRENTs		.BS X.MAX.RECURSE+1
*  oDIRENTs		.BS X.MAX.RECURSE*2
*  STATBUF		.BS S.STAT
*  hSrcBasePath	.BS 1
*  hDstBasePath	.BS 1				(optional)
*  hInclude		.BS 1
*  hExclude		.BS 1
*--------------------------------------
* Compare a ZPFileName against hInclude (e.g. '*test?.*')
* CC : match
* CS : no match
*--------------------------------------
X.IncludeMatch	>LDA.G index
				dec
				bne X.FilterMatch.CLCRTS	in a subdir, match everything

				clc
				.HS B0					BCS

X.IgnoreMatch	sec
				ldy #hInclude
				bcc .11
				
				iny
				
.11				lda (pData),y
				beq	X.FilterMatch.RTS	No filter....exit with passed carry

				>SYSCALL GetMemPtr
				>STYA ZPPtr2

				ldy #S.STAT.FSID
				lda (ZPFileStat),y		if 0, Regular ProDOS....
				pha						save FSID for comparing later
				bne .10

				>LDYA ZPPtr2			... so CI compare
				>SYSCALL StrUpr

.10				plx						get back FSID

				lda (ZPPtr2)			Get first pattern byte
				beq .8                  Match always if empty

				ldy #0
				bra .2

.1				jsr X.FilterMatch.NextPtr2	get next pattern char
				beq .41					end of pattern...

.2				cmp #'*'
				beq .5

.3				lda (ZPFileName),y		we must match ? or regular char, check if at end of string
				beq .9                  no char left, exit with error

				lda (ZPPtr2)       		get back pattern char
				cmp #'?'
				beq .4                  no need to compare, any char will match

				jsr X.FilterMatch.CmpPtr2CharPtr1Y	Regular Char, compare with string at Y
				bne .9                  no match, exit

.4				iny                     advance to next char to compare
				bra .1					continue if remaining char in pattern

.41				lda (ZPFileName),y 		end of pattern, but end of string ?
				beq .8					yes, string matched entirely
*										no, remaining char in string, no match
.9              sec
				rts

.5				jsr X.FilterMatch.NextPtr2		we have '*', last char of pattern ?
				beq .8					yes, match everything, including empty string

				cmp #'*'				another '*' ?
				beq .5					yes, '**' = '*', go next char

				cmp #'?'				'*?' ? we must match a least one char
				beq .3

.7				lda (ZPFileName),y		we need at least one remaining char in string, check if at end of string
				beq .9					no chance to match ? or regular char

				iny
				jsr X.FilterMatch.CmpPtr2CharPtr1Y	compare with char in string
				bne .7  	            not equal to next non wildcard in pattern

				iny
				bra .1                  go check remaining char in pattern...

.8
X.FilterMatch.CLCRTS
				clc
X.FilterMatch.RTS
				rts
*--------------------------------------
X.FilterMatch.CmpPtr2CharPtr1Y
				lda (ZPFileName),y
				phx
				plx
				bne .1					Foreign FS or ProDOS.FX : CS compare

				jsr X.ToUpper

.1				cmp (ZPPtr2)
				rts
*--------------------------------------
X.FilterMatch.NextPtr2
				inc ZPPtr2				Make PTR2 advance to next char
				bne .1

				inc ZPPtr2+1

.1				lda (ZPPtr2)
				rts
*--------------------------------------
X.InitSrcDirYA	>STYA ZPPtr1

X.InitSrcDirPtr1
				jsr X.InitGetBuf		Get a 256 buffer to store BasePath
				bcs .99

				>STA.G hSrcBasePath

				ldy #1
				lda (ZPPtr2),y
				beq .8					we have '/'

				jsr X.Init.StatBuf
				bcs .1					File/DIR does not exists, go extract pattern

				.DO X.ENTER.SUBDIR=1
				>LDA.G STATBUF+S.STAT.MODE+1
				and #$F0
				cmp /S.STAT.MODE.DIR
				beq .8					TYPE=DIR, do not extract pattern
				.FIN

				bra .4

.1				cmp #MLI.E.INVPATH
				beq .11

				sec
				rts

.11				jsr X.GetPtr2Len		TYPE is not DIR, check if wc

				dey

				ldx #0					Init Wildcard count

.2				lda	(ZPPtr2),y			search backward for a /
				cmp #'/'
				beq .3

				cmp #'?'				Test if some wildcard chars....
				beq .21

				cmp #'*'
				bne .22

.21				inx						inc wildcard count
.22				dey
				bne .2

.3				txa
				beq .98					no wc, file not found....

.4				jsr X.InitSplitBuf
				bcs .99

				>STA.G hInclude

.8				>LDYA ZPPtr2
				>SYSCALL OpenDir
				bcs .99

				ldy #hDIRs
				sta (pData),y			set hDIRs[0] = Src Hdir
				ldy #index
				lda #1
				sta (pData),y			index=hDIRs[0]

*				clc

				rts
				
.98				lda #MLI.E.INVPATH
				sec
.99				rts
*--------------------------------------
				.DO X.RESET.SRC.DIR=1
X.ResetSrcDir	jsr X.GethDIR

				>SYSCALL CloseDir

				>LDA.G hSrcBasePath
				>SYSCALL GetMemPtr
				>SYSCALL OpenDir
				bcs .9

				pha

				>LDA.G index
				clc
				adc #hDIRs-1
				tay

				pla
				sta (pData),y

*				clc
.9				rts
				.FIN
*--------------------------------------
				.DO X.COPY.TO.DEST=1
X.InitDstDirYA	>STYA ZPPtr1

X.InitDstDirPtr1
				jsr X.InitGetBuf		Get a 256 buffer to store BasePath
				bcs .99
				
				>STA.G hDstBasePath
				
				jsr X.Init.StatBuf
				bcs .1					File/DIR does not exists, go extract DstFileName

				>LDA.G STATBUF+S.STAT.MODE+1
				and #$F0
				cmp /S.STAT.MODE.DIR
				beq .8					Dst is a directory...no hDstFileName

.1				jsr X.InitSplitBuf
				bcs .99

				>STA.G hDstFileName
				
.8				jmp X.NormalizePtr2

.99				rts

				.FIN
*--------------------------------------
X.InitGetBuf	>LDYAI 256
				>SYSCALL GetMem			Get a 256 buffer to store BasePath
				bcs .99
				
				>STYA ZPPtr2
				phx
				
				>PUSHW ZPPtr1
				>PUSHW ZPPtr2
				>SYSCALL RealPath
				bcs .98
				
				pla						hBuf
				
*				clc

				rts

.98				plx

				pha
				txa
				>SYSCALL FreeMem
				pla
				
				sec

.99				rts
*--------------------------------------
X.Init.StatBuf	>PUSHW ZPPtr2
				>PUSHEA.G STATBUF
				>SYSCALL Stat
				rts
*--------------------------------------
X.InitSplitBuf	jsr X.GetPtr2Len		Search end of string.....

				lda #'/'

.1				dey
				cmp (ZPPtr2),y			Search backward /
				bne .1

				tya
				sec						+1 to skip '/'
				adc ZPPtr2
				sta ZPPtr1

				lda ZPPtr2+1
				adc #0
				sta ZPPtr1+1

				lda (ZPPtr1)
				beq .8					path is /dir/

				>LDYA ZPPtr1
				>SYSCALL StrDup
				bcs .99

				lda #0
				sta (ZPPtr1)

				txa

.8				clc

.99				rts
*--------------------------------------
X.GetNextEntry	jsr X.GetEntry
				bcs .9

				jsr X.GetFilenameLen	Save actual file len for setting up
				pha						new offset later
				lda ZPFileStat
				clc
				adc #S.STAT
				sta ZPFileName

				lda ZPFileStat+1
				adc /S.STAT
				sta ZPFileName+1		Make nDIRENTs[i] point to next DIRENT

				jsr X.GetFilenameLen	are we at end of this buffer ?
				beq .1					yes, go read next one ...

				jsr X.SetFileStatPtr

				jsr X.GetoDIRENT

				pla						Get back previous file len
				sec
				adc (pData),y
				sta (pData),y
				iny

				lda #0
				adc (pData),y
				sta (pData),y
				dey
				lda #S.STAT
				clc
				adc (pData),y
				sta (pData),y
				iny

				lda /S.STAT
				adc (pData),y
				sta (pData),y
				clc
				rts

.1				pla
				jsr X.GetEntry.ReadDir
				bcs .9

				jsr X.GetFilenameLen
				beq .99

				jsr X.SetFileStatPtr

				clc
.9				rts

.99				sec
				rts
*--------------------------------------
X.GetEntry		jsr X.GethDIRENT
				bne .1					we have a buffer to scan

				jsr X.GetEntry.ReadDir	First run, get first block
				bcs .9					ZPFileName = start of buffer

				lda (ZPFileName)		Empty !!!
				bne .8

				sec
				rts

.1				>SYSCALL GetMemPtr
				>STYA ZPFileName
				jsr X.GetoDIRENT

				lda ZPFileName
				clc
				adc (pData),y
				sta ZPFileName

				iny

				lda ZPFileName+1
				adc (pData),y
				sta ZPFileName+1		ZPFileName=ZPFileName+oDIRENT

.8				jsr X.GetFilenameLen

				jsr X.SetFileStatPtr

				clc
.9				rts
*--------------------------------------
X.GetEntry.ReadDir
				jsr X.GethDIRENT
				beq .1

				pha
				lda #0
				sta (pData),y			reset hDIRENT
				pla
				>SYSCALL FreeMem		discard previous hDIRENT

.1				jsr X.GethDIR
				>SYSCALL ReadDir
				bcs .9

				>STYA ZPFileName
				ldy #index
				lda (pData),y
				clc
				adc #hDIRENTs-1
				tay
				txa						get hDIRENT in A
				sta (pData),y

				jsr X.GetoDIRENT

				lda #0					and reset offset for this buffer
				sta (pData),y
				iny
				sta (pData),y
				clc
.9				rts
*--------------------------------------
X.EnterSubDirYA	>STYA ZPPtr1			save SUBDIR for StrCat

				>LDA.G index
				cmp #X.MAX.RECURSE
				beq .9

				ldy #hSrcBasePath
				jsr X.EnterSubDirY
				>LDYA ZPPtr2
				>SYSCALL OpenDir
				bcs .9

				pha
				>INC.G index
				tax
*				clc						from BCS
				adc #hDIRs-1
				tay
				pla
				sta (pData),y
				txa						make sure hDIRENTs[i] is closed
				adc #hDIRENTs-1
				tay
				lda #0
				sta (pData),y

				.DO X.COPY.TO.DEST=1
				ldy #hDstBasePath
				jsr X.EnterSubDirY
				.FIN

				clc
.9				rts
*--------------------------------------
X.EnterSubDirY	lda (pData),y
				>SYSCALL GetMemPtr
				>STYA ZPPtr2			save full path

				>PUSHW ZPPtr2
				>PUSHW ZPPtr1
				>SYSCALL StrCat

X.NormalizePtr2	jsr X.GetPtr2Len

				dey
				lda #'/'
				cmp (ZPPtr2),y
				beq .8

				iny
				sta (ZPPtr2),y

				iny
				lda #0
				sta (ZPPtr2),y

.8				clc
				rts
*--------------------------------------
X.LeaveSubDir	>LDA.G index
				beq .7

				jsr X.GethDIRENT.A
				jsr X.LeaveSubDir.free

				jsr X.GethDIR
				beq .1

				pha
				lda #0
				sta (pData),y
				pla
				>SYSCALL CloseDir

.1				>DEC.G index
				bne .8

.7				>LDA.G hSrcBasePath
				jsr X.LeaveSubDir.free

				.DO X.COPY.TO.DEST=1
				>LDA.G hDstBasePath
				jsr X.LeaveSubDir.free
				.FIN

				sec
				rts

.8				clc
				rts
*--------------------------------------				
X.LeaveSubDir.free
				tax
				beq .8

				lda #0
				sta (pData),y
				txa
				>SYSCALL FreeMem
				
.8				rts
*--------------------------------------
X.BasePath..	.DO X.COPY.TO.DEST=1

				ldy #hDstBasePath
				jsr X.BasePath..1
				.FIN
				ldy #hSrcBasePath

X.BasePath..1	lda (pData),y
				>SYSCALL GetMemPtr
				>STYA ZPPtr2			save full path

				jsr X.GetPtr2Len		get len

				dey 					discard ending /

.2				dey
				lda (ZPPtr2),y
				cmp #'/'
				bne .2

				iny
				lda #0
				sta (ZPPtr2),y			cut DIR2/ from /dir1/DIR2/

				rts
*--------------------------------------
X.ToUpper		cmp #'a'
				bcc .8

				cmp #'z'+1
				bcs .8

				eor #$20

.8				rts
*--------------------------------------
X.GetPtr2Len	ldy #$ff

.1				iny

				lda (ZPPtr2),y			get len
				bne .1

				rts		
*--------------------------------------
X.GetFilenameLen
				ldy #$ff

.1				iny
				lda (ZPFileName),y
				bne .1

				tya
				rts
*--------------------------------------
X.SetFileStatPtr
				sec
				adc ZPFileName
				sta ZPFileStat
				lda ZPFileName+1
				adc #0
				sta ZPFileStat+1
				rts
*--------------------------------------
X.GethDIR		>LDA.G index

				clc
				adc #hDIRs-1
				tay
				lda (pData),y
				rts
*--------------------------------------
X.GethDIRENT	>LDA.G index

X.GethDIRENT.A	clc
				adc #hDIRENTs-1
				tay
				lda (pData),y
				rts
*--------------------------------------
X.GetoDIRENT	>LDA.G index
				asl
				clc
				adc #oDIRENTs-1
				tay
				rts
*--------------------------------------
MAN
SAVE usr/src/shared/x.fileenum.s
LOAD usr/src/bin/ls.s
ASM
