;********************************************************************
;
; NAME	: coal 1.0
;
; TEMPLATE	: coal <dir name> <dir name>
;
; DESCRIPTION	: coalesce duplicate files to point to single virtual reference
;
; AUTHOR	: Anselm Hook, from code from Stuart Mitchell's 'DU'
;			(where Stuart got it from he's not saying....)
;
; DATE	: 20-3-91
;
; NOTES 	: 1) this thing requires the ARP shared library (v39.0+)
;
;********************************************************************

ARG_SOURCE	= 0
ARG_DEST	= 4
ARG_MERGE	= 8
ARG_DELETE	= 12
ARG_REAL	= 16


;	STRUCTURE FileInfoBlock,0
;	LONG	fib_DiskKey		; start of file on media
;	LONG	fib_DirEntryType	; < 0 = file, > 0 = directory
;	STRUCT	fib_FileName,108	; name
;	LONG	fib_Protection		; sparwed
;	LONG	fib_EntryType		; ???
;	LONG	fib_Size		; byte size
;	LONG	fib_NumBlocks		; block size
;	STRUCT	fib_DateStamp,ds_SIZEOF	; last date
;	STRUCT	fib_Comment,80		; null terminated comments
;	STRUCT	fib_padding,36		; unused
;	LABEL	fib_SIZEOF



		section du,code

		opt	d+,c- ;o+

		incdir	"include:"

		include "arpbase.i"
		include "libraries/dos_lib.i"
		include "libraries/dos.i"
		include "exec/memory.i"

ap_Flags = $10

_LVOOpenLibrary 	equ	-$228
_LVOCloseLibrary	equ	-$19e

CALL		macro	(name)
		jsr	_LVO\1(a6)
		endm

stash1		macro	(name)
		movem.l	d0-d7/a0-a6,stash10
		endm
stash2		macro	(name)
		movem.l	d0-d7/a0-a6,stash20
		endm
unstash2		macro	(name)
		movem.l	stash20,d0-d7/a0-a6
		endm
unstash1		macro	(name)
		movem.l	stash10,d0-d7/a0-a6
		endm

	move.l	a7,thestack
	bra	start
thestack	dc.l	0
stash10	dcb.l	16,0
stash20	dcb.l	16,0

r_anchor	equr	a2
r_finfo 	equr	a3
r_current_arg	equr	a4
r_fpath	equr	a5
r_flags	equr	d1
r_files 	equr	d2
r_dirs	equr	d3
r_size	equr	d4
r_blocks	equr	d5
r_num_arg	equr	d6
r_zero	equr	d7

; bitdefs for dir scan - should really be picked up from ArpBase.i ...

DIDDIR	equ	3
DODIR	equ	2

;********************************************************************

start:	move.l	d0,d2			; cache command line
	move.l	a0,a2

	moveq.l #0,r_zero

; open ARP library

	move.l	4.w,a6
	lea	arplib(pc),a1
	move.l	r_zero,d0
	CALL	OpenLibrary
	tst.l	d0
	beq	exit
	move.l	d0,a6

; parse command line

	move.l	a2,a0			; string
	move.l	d2,d0			; length
	lea	help(pc),a1
	lea	args(pc),a2             ; pointer to arg array
	lea	tplate(pc),a3
	CALL	GADS
	move.l	d0,r_num_arg		; count

	tst.l	d0
	bmi	closearp2		; tell user about failure etc

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; AH - code to check every file against every other file

	move.l	args+ARG_SOURCE,r_current_arg	; src arg
	bsr	find_first
	beq	closearp
	stash1
0$	move.l	args+ARG_DEST,r_current_arg	; dest arg
	bsr	find_first		; re-loop entire dest
	beq	closearp
	stash2
1$	bsr	issame_coal
	unstash2
	bsr	find_next		; loop till exhausted
	stash2
	bne	1$
	;bsr	print
	unstash1
	bsr	find_next
	stash1
	bne	0$
	bsr	print
	bra	closearp

issame_coal	unstash1
	movem.l	r_finfo/r_fpath,-(a7)
	unstash2
	move.l	r_finfo,r_anchor
	move.l	r_fpath,r_current_arg
	movem.l	(a7)+,r_finfo/r_fpath
					; now if r_anchor == r_finfo we have a match!

					; not actually same file?
	move.l	fib_DiskKey(r_finfo),r_size
	move.l	fib_DiskKey(r_anchor),r_blocks
	cmp.l	r_size,r_blocks
	beq	1$
					; both same size?
	move.l	fib_Size(r_finfo),r_size
	move.l	fib_Size(r_anchor),r_blocks
	cmp.l	r_size,r_blocks	
	bne	1$
					; are both virtual files?
					; (secretively imbedded in FIB)

	;/* fib_Padding == fib_Reserved depending on your includes!! */

	move.l	fib_Comment+72(r_finfo),r_size
	move.l	fib_Comment+72(r_anchor),r_blocks
	cmp.l	r_size,r_blocks
	bne	1$
	cmp.l	#'VIRT',r_blocks
	bne.s	1$
					; checksums match?
					; (secretively imbedded in FIB)
	move.l	fib_Comment+76(r_finfo),r_size
	move.l	fib_Comment+76(r_anchor),r_blocks
	cmp.l	r_size,r_blocks
	bne	1$

	move.l	r_fpath,matchfiles+4	; name of file
	move.l	r_current_arg,matchfiles ; name of other file

					; 1) DELETE DUPLICATE REFERENCE
	tst.l	args+ARG_DELETE
	beq	5$
	lea	matchfiles(pc),a1	; address ptrs to args
	lea	matchfmt100(pc),a0	; format string
	bsr	myprintf
	move.l	r_current_arg,d1	; delete dest ref
	CALL	DeleteFile
	bra.s	7$
					; 2) OR MERGE DUPLICATE REFERENCE
5$	tst.l	args+ARG_MERGE
	beq.s	7$
	lea	matchfiles(pc),a1	; address ptrs to args
	lea	matchfmt150(pc),a0	; format string
	bsr	myprintf
					; 3) DELETE REAL
7$	tst.l	args+ARG_REAL
	beq.s	8$
	lea	matchfiles(pc),a1	; address ptrs to args
	lea	matchfmt200(pc),a0	; format string
	bsr	myprintf
	rts
8$	;tst.l	args+ARG_REAL		; if all 0 just give stats
	;bne.s	1$
	tst.l	args+ARG_MERGE
	bne.s	1$
	tst.l	args+ARG_DELETE
	bne.s	1$
	lea	matchfiles(pc),a1
	lea	matchfmt(pc),a0
	bsr	myprintf
1$	rts

matchfiles	dc.l	0,0
matchfmt	dc.b	"File %s and %s are the same",10,0
matchfmt100	dc.b	"Retaining %s, killing duplicate %s",10,0
matchfmt150	dc.b	"Copying duplicate %s to %s",10,0
matchfmt200	dc.b	"Deleting *REAL* %s",10,0
	even

coalesce
	rts

;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<


;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; allocate memory for AnchorPath structure - note this is freed automatically
find_first	move.l	#ap_SIZEOF+512,d0
	move.l	#MEMF_PUBLIC|MEMF_CLEAR,d1
	CALL	ArpAllocMem
	tst.l	d0
	beq	closearp
	move.l	d0,r_anchor
	move.b	#%00100001,ap_Flags(r_anchor)   ; DOWILD|DODOT
	lea	ap_Info(r_anchor),r_finfo	; the FIB

	; (ap_Flags and ap_Length overlap...I must have old docs!)
	move.w	#512,ap_Length+2(r_anchor)	; 512 bytes for path name
	lea	ap_Buf(r_anchor),r_fpath	; the file path name

init_regs:	move.l	r_zero,r_files		; zero results
	move.l	r_zero,r_dirs
	move.l	r_zero,r_size
	move.l	r_zero,r_blocks
					; find first file/directory
	move.l	r_current_arg,d0
	move.l	r_anchor,a0
	CALL	FindFirst
	tst.l	d0
	bne.s	no_files
					; loop around in directory until all subdirectories/files exhausted
					; first check if CNTL-C pressed
du_loop:	move.l	r_zero,a1
	CALL	CheckAbort
	tst.l	d0
	bne	break_pressed
	move.b	ap_Flags(r_anchor),r_flags
					; if DirEntryType <0 then we have a file
	move.l	fib_DirEntryType(r_finfo),d0
	blt.s	do_file
do_dir: 	;tst.l	args+ARG_ALL		; Do Sub Dirs?
	;beq.s	exit_dir

	btst	#DIDDIR,r_flags		; DIDDIR bit set
	beq.s	enter_dir		; no ... enter dir
					; have just popped out of a directory, mark as finished & increment count
exit_dir:	bclr	#DIDDIR,r_flags
	addq.l	#1,r_dirs
	bra.s	find_next
					; found a new directory, so enter it at next FindNext
enter_dir:	bset	#DODIR,r_flags
	bra.s	find_next
					; this ones a file, so increment count & size
do_file:	addq.l	#1,r_files
	add.l	fib_Size(r_finfo),r_size
	add.l	fib_NumBlocks(r_finfo),r_blocks
	moveq	#1,d0			; found a file! AH
	rts
					; find next file
find_next:	move.b	r_flags,ap_Flags(r_anchor)   ; set directory handling
	move.l	r_anchor,a0
	CALL	FindNext
	tst.l	d0
	beq.s	du_loop 		; if zero then got something
					; directory exhausted
	bsr.s	free_anchor		; free mem
	moveq	#0,d0			; failure AH
	rts

;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<


; print out results

print:	movem.l r_files/r_dirs/r_size/r_blocks,files	 ; flush results

	move.l	r_current_arg,a0     ; print directory name
	bsr	myprintf
	lea	fmt(pc),a0		; format string
	lea	files(pc),a1		; address ptrs to args
	bsr	myprintf

	rts

;; and do it all again;;  don't bother AH
;
;end_loop:	subq.l	#1,r_num_arg		; decrement arg count
;	bra.s	main_loop		; and do next arg



;********************************************************************
; handle case when directory/file doesn't exist...

no_files:	move.l	r_current_arg,a0
	bsr	myprintf
	lea	not_exist(pc),a1
	CALL	Puts
	bsr.s	free_anchor		; free mem

	moveq	#0,d0			; failure AH
	rts

	;bra.s   end_loop

;********************************************************************

myprintf	;tst.l	args+ARG_QUIET
	;bne.s	1$
	jmp	_LVOPrintf(a6)
;1$	rts

;********************************************************************
; called if CNTL-C detected
; NB: 1	- call to CheckAbort sets up a1 to point to break string
;     2 - drop through to end of program

break_pressed:	; a1 = 0
	CALL	Puts			; output "***BREAK"
	bsr.s	free_anchor		; free mem
	;bra.s	closearp

;*******************************************************************
; exit program - close ARP & return to DOS - call frees resources

closearp:	move.l	a6,a1
	move.l	4.w,a6
	CALL	CloseLibrary
	move.l	thestack(pc),a7

	; system should exit before hitting this line...but

exit:	move.l	r_zero,d0
	rts


closearp2	move.l	args(pc),a1
	CALL	PUTS
	bra	closearp

;********************************************************************
; free anchorpath

free_anchor:	move.l	r_anchor,a0		; free anchor
	jmp	_LVOFreeAnchorChain(a6)

;********************************************************************
; data

files	dc.l	0
dirs	dc.l	0
size	dc.l	0
blocks	dc.l	0
	even
fmt	dc.b	" -- %ld files, %ld directories : %ld bytes (%ld blocks)",$0a,0
	even
arplib	dc.b	"arp.library",0
	even
args	dc.l	0,0,0,0,0		; was char *args[] now is x args
	even
help	dc.b	"FROM TO [MERGE/KILLREF/KILLREAL]",0
	even
tplate	dc.b	'FROM,TO,MERGE/S,KILLREF/S,KILLREAL/S'
	even
not_exist	dc.b	" -- directory does not exist",0
	end


;*	- should not detect itself (using diskkey?) (vir fixed too)
;*	- should use checksum (in comment field?) to determine duplc.
;	- should print out full path name (lock file using fib?)
;	- should do one of these things if duplicate found:
;		connect reference to same dest
;		delete duplicate reference
;		delete real copy, and connect reference to same dest
;		delete real copy, delete reference

;		copy "file of name a" to "file of name b"
;		delete "file of name b"
;		extract real destination, delete that....


;	lea	comm2(pc),a0
;	move.l	a0,d1
;	clr.l	d2
;	clr.l	d3
;	jmp	LVOExecute(a6)
;comm2:	dc.b	'copy >nil: :dpaint :kodak',0
