

* KILLDIR 2.1 - Olly koenders 13-06-97.
* (1.0 = Two hour code), (2.0 = 2 hour update), (2.1 = 5 min bugfix!)

**** The "*" in the comments is the bugfix (forgot some instructs - DUHH!) 

* Also added a "Bytes saved" output - True calculation including file-
* headers and directory headers (512 bytes each) + filesize.

* Right.  This little bugger requires a filename as a parameter, and the
* parameter file should contain pathnames like this in plain ASCII:

* "BBS:files/amiga/utils/"[CR]
* "BBS:files/amiga/comms/"[CR]

* Do not include the quotes (""), and the [CR] is actually where you press
* [RETURN].  You may/may not have a [CR] at the end of the last entry but
* it's not essential as I anticipated and coded for this in either case.

* The proggy will load the ENTIRE parameter file into mem and grab the
* path strings from there byte by byte.  It'll then check for entries
* within these paths and if a directory entry is found, it'll kill it.

* The results are printed in the CLI window.  If you want a log-file then
* just redirect the output: "Killdir >RAM:logfile BBS:pathfile".

**** If a directory entry to kill contains a file it'll DELETE it as well
* as any other files in there, then continue on deleting directories.

* I've tested this thing and have full confidence, but remember to make
* full tests in RAM: before actually doing the nasty.

	section	killdir.code,code,public	;type of mem we'll run in

execbase=4	;just some equates for readablility AND so I don't need
allocmem=-198	;to use bullshit INCLUDES!
freemem=-210
openlib=-408
output=-60
open=-30
mode_old=1005
read=-42
close=-36
write=-48
delete=-72
lock=-84
unlock=-90
examine=-102
exnext=-108

	*executable code starts here

	move.l	execbase,a6	;ALWAYS need this for lib functions
	cmp.b	#1,d0		;check parameter length (filename) 
	ble.w	runout		;if less/equal to 1 byte (CR only) then quit
	subq.l	#1,d0		;take 1 from # of bytes in filename param
	clr.b	(a0,d0.w)	;clear it (null-terminated filenames here!)
	move.l	a0,a5		;store address of parameter filename
	lea	dosname(pc),a1	;get dos.lib name
	jsr	openlib(a6)	;open the lib
	move.l	d0,dosbase	;store its base address (handle) for later
	move.l	d0,a6		;put in a6 as well so we can use dos func's.
	move.l	a5,d1		;address of stored param filename
	moveq	#-2,d2		;"-2" = shared read
	jsr	lock(a6)	;get a lock on it
	move.l	d0,d5		;store the lock
	beq.w	runout		;if we didn't get a lock - quit
	move.l	d0,d1		;lock in d1 for next call
	move.l	#info,d2	;address of infoblock struct to fill
	jsr	examine(a6)	;examine the entry (to get filesize)
	move.l	d5,d1		;lock we have in d1 again
	jsr	unlock(a6)	;set it free
	move.l	execbase,a6	;exec function coming up
	move.l	info+124,d0	;get filesize from infoblock struct
	beq.w	runout		;if null size file quit (idiot user!)
	addq.l	#8,d0		;add some more bytes of clear mem
	move.l	d0,memsize	;store amount for freecall
	move.l	#$10001,d1	;ask for MEMF_CLEAR + MEMF_PUBLIC
	jsr	allocmem(a6)	;allocate it
	move.l	d0,mem		;store it in variable address
	move.l	dosbase,a6	;dos calls from here on
	move.l	a5,d1		;filename parameter address
	move.l	#mode_old,d2	;"mode_old" means it should exist
	jsr	open(a6)	;open it
	move.l	d0,d5		;store "handle"
	move.l	d0,d1		;again in d1
	move.l	mem(pc),d2	;memory start address
	move.l	info+124,d3	;size of file
	jsr	read(a6)	;read into memory
	move.l	d5,d1		;file handle again
	jsr	close(a6)	;close it
	jsr	output(a6)	;find output for CLI text
	move.l	d0,filehd	;store handle
	lea	buffer(pc),a4	;address of small buffer to handle names
	move.l	mem(pc),a5	;memory address stored in all-purpose reg
	clr.l	filenum		;we'll clear our counter variable (files)
	clr.l	dirnum		;same for directories
	clr.l	bytenum		;and "bytes saved"

mainloop:
	tst.b	(a5)		;check byte within mem address
	beq.w	out		;if null byte - outta here
	move.l	a4,a0		;put buffer address in a scratch register

find0orcr:
	move.b	(a5)+,(a0)+	;copy a byte from mem to buffer then add 1
	beq.b	found		;if null byte then should be EOF file
	cmp.b	#10,-1(a5)	;if CR then should be EOF line
	bne.b	find0orcr	;if not found - keep looking
	clr.b	-(a0)		;found CR - address 1 back and null term
	move.l	a0,d5		;store this address point for later

found:
	move.l	a4,d1		;buffer (dirname) address
	moveq	#-2,d2		;shared read
	jsr	lock(a6)	;get a lock
	move.l	d0,locker	;store our lock
	beq.b	mainloop	;if no lock - then try next line
	move.l	d0,d1		;put lock in d1 for next call
	move.l	#info,d2	;infoblock address again
	jsr	examine(a6)	;fill with info - required for next call 

next:
	move.l	locker(pc),d1	;stored lock
	move.l	#info,d2	;same deal
	jsr	exnext(a6)	;find next entry in directory chain
	tst.l	d0		;check result
	beq.w	goagain		;if none - then we're done for this dir
	tst.l	info+4		;check entry type
	bmi.b	next		;if file - then leave it alone

killit:
	move.l	#info+8,a1	;get dirname string address
	move.l	d5,a2		;address point from before

copyname:
	move.b	(a1)+,(a2)+	;copy entryname bytes to rest of path$
	bne.b	copyname	;if not null byte - keep copying
	move.b	#10,(a2)+	;put CR at end of path$ (for text output)
	move.l	a2,d7		;store address in case we find a file
	subq.l	#2,d7		;correct it
	move.l	a2,d6		;get current address in this path$
	sub.l	a4,d6		;subtract start address of path$ for later
	move.l	a4,d1		;full path$ name
	jsr	delete(a6)	;as it says
	tst.l	d0		;check result
	beq.b	entry		;if unsuccessful - dir contains files
	moveq	#buffer-del,d3	;setup # of bytes to print to CLI
	add.l	d6,d3		;add path$ # of bytes
	move.l	#del,d2		;address of text to start printing

textout:
	move.l	filehd(pc),d1	;the CLI window "handle"
	jsr	write(a6)	;print "Deleting entry: etc:???/?????/..."
	addq.l	#1,dirnum	;add "quick" one to our counter (dirs)
	bra.b	next		;go back and find the next entry to kill

entry:
	move.l	a4,d1		;buffer (dirname) address
	moveq	#-2,d2		;shared read
	jsr	lock(a6)	;get a lock (on a deeper directory level)
	move.l	d0,locker2	;store our lock in a different variable
	move.l	d0,d1		;* put lock in d1 for next call
	move.l	#info2,d2	;* second infoblock this time
	jsr	examine(a6)	;* fill with info - required for next call 

nextentry:
	move.l	locker2(pc),d1	;stored lock
	move.l	#info2,d2	;second infoblock again
	jsr	exnext(a6)	;find next entry in directory chain
	tst.l	d0		;check result
	beq.b	nomore		;if none - then we're done in here
	move.l	#info2+8,a1	;filename string from this directory lock
	move.l	d7,a2		;our previously stored pointer
	move.b	#"/",(a2)+	;put "slant" in the string for dir level

entryloop:
	move.b	(a1)+,(a2)+	;copy the filename
	bne.b	entryloop	;if byte is not null - then continue copy
	move.l	a2,d6		;get current address in this path$
	sub.l	a4,d6		;subtract start address of path$ for later
	move.l	a4,d1		;full path$ name
	jsr	delete(a6)	;as it says
	move.l	info2+124,d1	;put filesize in scratch register
	add.l	d1,bytenum	;add it to bytecounter
	addq.l	#1,filenum	;add one to our counter for files
	tst.l	d0		;check result of delete operation
	bne.b	nextentry	;if successful - look for another
	subq.w	#1,filenum	;if not - correct counter
	move.l	info2+124,d1	;put filesize in scratch register
	sub.l	d1,bytenum	;and correct bytecounter
	moveq	#nodel-buffer,d3	;setup # of bytes to print to CLI
	add.l	d6,d3		;add # of bytes from path$
	move.l	#buffer,d2	;address of text to start printing
	move.l	filehd(pc),d1	;the CLI window "handle"
	jsr	write(a6)	;print it
	moveq	#-1,d6		;chuck an error code here to test shortly

nomore:
	move.l	locker2(pc),d1	;our "other" lock from deeper level
	jsr	unlock(a6)	;free it
	cmp.l	#-1,d6		;test for our error
	bne.w	killit		;if not - then finally delete prev. dir

goagain:
	move.l	locker(pc),d1	;lock handle from upper level
	jsr	unlock(a6)	;free it
	cmp.l	#-1,d6		;test for our error again
	bne.w	mainloop	;if not - get next line from param file

out:
	move.l	filenum(pc),d0	;number of files
	move.l	#512,d1		;512 bytes per fileheader
	bsr.w	multiply	;multiply them together (custom routine)
	move.l	d0,d4		;hang onto it for later
	move.l	filenum(pc),d0	;one of our stored counters
	lea	filenum(pc),a1	;use this address for the numerical text
	bsr.b	convert		;convert the binary number to text
	move.l	dirnum(pc),d0	;number of directories
	move.l	#512,d1		;512 bytes per directory header
	bsr.w	multiply	;multiply them together (custom routine)
	move.l	d0,d5		;store result
	move.l	dirnum(pc),d0	;the other counter
	lea	dirnum(pc),a1	;use this address to
	bsr.b	convert		;convert this one too
	move.l	bytenum(pc),d0	;the bytes saved counter
	add.l	d4,d0		;add the "file header" total
	add.l	d5,d0		;add the "directory header" total
	lea	table2(pc),a0	;for bigger numbers
	lea	bytenum(pc),a1	;use this address for conversion
	bsr.b	pad		;convert this one with bigger table
	move.l	filehd(pc),d1	;the CLI window "handle"
	move.l	#filesdel,d2	;address for the text to print
	move.l	#locker-filesdel,d3	;number of characters to print
	jsr	write(a6)	;print text - numbers and all
	move.l	execbase,a6	;exec function coming up
	move.l	mem(pc),a1	;address of mem we got before
	move.l	memsize(pc),d0	;size of the bugger
	jsr	freemem(a6)	;give it back to the system

runout:
	moveq	#0,d0		;clear result so you don't need "Failat"
	rts			;quit this proggy

convert:			;our binary to text (decimal) routine
	cmpi.l	#10,d0
	bge.b	convert2
	add.b	#$30,d0
	move.l	#"    ",(a1)+
	move.b	d0,(a1)
	rts

convert2:
	lea	table(pc),a0

pad:
	move.l	(a0)+,d1
	move.b	#" ",(a1)+
	cmp.l	d1,d0
	bcs.B	pad

nextdigit:
	moveq	#10,d2

getdigit:
	subq.l	#1,d2
	sub.l	d1,d0
	bcc.B	getdigit
	add.l	d1,d0
	move.b	#"9",d1
	sub.b	d2,d1
	move.b	d1,(a1)+
	move.l	(a0)+,d1
	bne.B	nextdigit
	add.b	#$30,d0
	move.b	d0,(a1)
	rts

multiply:		;custom multiplication routine (d0.L = d0.L * d1.L)
	move.w d1,d2	;reason for this is that the normal 680x0 "mulu"
	mulu d0,d2	;command only does it wordwise (16 bits), so the
	move.l d1,d3	;maximum number you could receive as a result is
	swap d3		;only 65,535 or less.  This way (32 bits), maximum
	mulu d0,d3	;result is over 4 GIG.
	swap d3
	clr.w d3
	add.l d3,d2
	swap d0
	mulu d1,d0
	swap d0
	clr.w d0
	add.l d2,d0
	rts

dosname:
	dc.b	"dos.library",0		;library name$
	even

dosbase:
	dc.l	0		;somewhere to store its "handle" (4 bytes)

mem:
	dc.l	0		;same for memory allocation (address)

memsize:
	dc.l	0		;and for the size of mem

filehd:
	dc.l	0		;storage for "handles"

del:
	dc.b	"Deleting entry: "	;our text

buffer:
	ds.b	100		;the buffer for path$+entryname$

nodel:
	dc.b	" not deleted - protected!",10

filesdel:
	dc.b	10,"Files deleted: "	;the text we quit with

filenum:
	dc.b	"     ",10	;one of the buffers for our numbers
	dc.b	"Directories deleted: ",0

dirnum:
	dc.b	"     ",10
	dc.b	"Bytes saved: ",0

bytenum:
	dc.b	"        ",10,10
	
locker:
	dc.l	0		;place to store file/dir locks

locker2:
	dc.l	0		;same for the deeper levels

table2:
	dc.l	1000000	;conversion table for binary to text (max = 9999999)
	dc.l	100000
	dc.l	10000

table:
	dc.l	1000	;max = 9999
	dc.l	100
	dc.l	10
	dc.l	0

	section	killdir.data,data,public	;different proggy hunk

info:
	ds.b	260	;260 byte block of preallocated space for info

info2:
	ds.b	260	;the second one for deeper levels
	end		;stop assembling when you reach here


