;
;	ARCV.ASM
;

NL		equ	10

ARCMARK		equ	26	; special archive marker
ARC_VER_MAX	equ	9	; highest compression code used

INBUF_SIZE	equ	512	; size of input	buffer

MODE_OLDFILE	equ	1005
SHARED_LOCK	equ	-2


	include	"asm68k.init"

	ext_sys	OpenLibrary	; external AmigaDOS system calls
	ext_sys	CloseLibrary
;	ext_sys	Input
	ext_sys	Output
	ext_sys	Open
	ext_sys	Close
	ext_sys	Read
	ext_sys	Write
	ext_sys	Lock
	ext_sys	UnLock
	ext_sys	Examine
	ext_sys	ExNext
	ext_sys	CurrentDir
	ext_sys	DupLock
	ext_sys	Seek

	xref	_AbsExecBase

;====
main:
;====
; Entry:
;	A0 -> CLI command line terminated by a CR ($0D)
;	D0 == length of command line INCLUDING the CR (i.e. Count+1)
;
	lea	_data(pc),a5		; A5 -> data area thru entire program
	move.l	sp,Stack_ptr(a5)	; save stack ptr
	move.b	#0,-1(a0,d0.l)		; null terminate command line @CR

	move.l	a0,-(sp)		; push A0 -> command line
	move.l	_AbsExecBase,a6		; A6 -> exec base
	lea	dos_lib(pc),a1		; A1 -> name of DOS library
	moveq	#0,d0			; D0 = no particular version
	sys	OpenLibrary		; try to open DOS library
	tst.l	d0			; succeess?
	 beq	exit2			;  N: leave this place
	move.l	d0,a6			; A6 -> library base thru entire program
	sys	Output			; grab standard output handle
	move.l	d0,Stdout(a5)
	 beq	exit2			; Output handle zero? Y: exit
	move.l	(sp)+,a1		; pop A1 -> command line

process_next_arg:			; strip	leading	blanks
	move.b	(a1)+,d0
	 beq.s	display_usage
	cmp.b	#' ',d0
	 beq.s	process_next_arg
	subq.l	#1,a1			; backup to first non-space

	moveq	#0,d6			; wild_flag (D6) = FALSE
	lea	arc_name,a3		; A3 -> arc_name storage area
	lea	wild_name,a4		; A4 -> wild_name, in case we find one.
	move.l	a3,a2			; set file_ptr (A2) to start
move_arcname_loop:
	move.b	(a1)+,d0		; grab next char
	 beq	move_done		;  at eol? Y: weese done
	cmp.b	#' ',d0			; end of arg?
	 beq	move_done		;  Y: ditto
	cmp.b	#'/',d0			; path delimiter?
	 beq.s	found_delimiter		;  Y: note position
	cmp.b	#':',d0			; drive delimiter?
	 beq.s	found_delimiter		;  Y: ditto
	cmp.b	#'#',d0			; one of the wild cards?
	 beq.s	found_wild		;  Y: set flag
	cmp.b	#'?',d0
	 beq.s	found_wild
	cmp.b	#'*',d0
	 bne.s	store_arcname_char	;  N: skip flagset
found_wild:
	moveq	#1,d6			; wild_flag (D6) = TRUE
store_arcname_char:
	move.b	d0,(a4)+		; store char in wild_name
store_arcname_char2:
	move.b	d0,(a3)+		; store char in arc_name
	bra.s	move_arcname_loop	; keep loopin

found_delimiter:		; found '/' or ':'
	tst.w	d6			; have we found any wildcards?
	 bne.s	wildpath_error		;  Y: report an error
	move.l	a3,a2
	addq.l	#1,a2			; A2 -> next guess for file_ptr
	lea	wild_name,a4		; restart wild_name ptr
	bra.s	store_arcname_char2	; store delimiter only in arc_name

display_usage:
	subq.l	#1,a1			; set arg_ptr to point to null
	move.l	a1,Arg_ptr(a5)
	tst.b	Got_arg_flag(a5)	; did we process any previous args?
	 bne	exit			;  Y: we're ok -- no message
	leaprt	usage_msg		;  N: tell them how to use us
	bra.s	to_msg_exit
wildpath_error:
	leaprt	wild_path_err		; tell them about wildcard paths
to_msg_exit:
	bra	msg_exit
lock_error:
	tst.b	Wild_flag(a5)		; were we locking path or filename?
	 bne.s	check_path
	leaprt	not_found_err		; tell them filename isn't found
	bra.s	to_arcname_msg_exit
check_path:
	cmp.l	#arc_name,a2		; is the path null?
	 bne.s	check_slash		;  N: check ram: slash kludge
	moveq	#0,d1			;  Y: kludge ""==current_dir for ram:
	sys	CurrentDir		; Get our current lock
	move.l	d0,d1
	move.l	d1,-(sp)
	sys	CurrentDir		; put it back as CurrentDir
	move.l	(sp)+,d1
	sys	DupLock			; try to duplicate it for our use
	move.l	d0,Dir_lock(a5)		; if non-zero we succeeded
	 bne	use_lock		; so retry ramdisk with kludged lock
path_error:
	move.b	Pre_name_char(a5),-1(a2); restore char in case we blasted it
	leaprt	path_err		; if all else fails, path is invalid
	bra.s	to_arcname_msg_exit
check_slash:
	cmp.b	#'/',-1(a2)		; is name after a path delimiter?
	 bne.s	path_error		;  N: we give up
	move.b	#0,-1(a2)		;  Y: zap it for retry of lock
	bra	try_lock
examine_error:
	leaprt	examine_err
	bra.s	to_arcname_msg_exit
no_match:
	tst.b	This_path_flag(a5)	; did we process any previous files?
	 bne	exit			;  Y: we're ok -- no message
	move.b	wild_name,d0		;  N: we never found a name
	move.l	File_ptr(a5),a1		; so put the first byte of the name
	move.b	d0,(a1)			; back in place of null for output
	leaprt	no_match_err		; report no match
to_arcname_msg_exit:
	bra	arcname_msg_exit


move_done:
;---------
;
	move.b	#1,Got_arg_flag(a5)	; flag that we processed something
	subq.l	#1,a1
	move.l	a1,Arg_ptr(a5)		; save pointer to next arg (if any)

	lea	arc_ext(pc),a1		; A1 -> '.arc'
	lea	-4(a3),a0		; A0 -> last 4 bytes of arc_name
	moveq	#4-1,d7
match_arc_ext_loop:			; is the extention already there?
	move.b	(a0)+,d1
	cmp.b	#'Z',d1			; lowercase the arc_name char
	 bhi	mael_cmp
	cmp.b	#'A',d1
	 bcs.s	mael_cmp
	add.b	#$20,d1
mael_cmp:
	cmp.b	(a1)+,d1
	dbne	d7,match_arc_ext_loop	; loop until name <> '.arc'
	 beq.s	add_arc_ext_loop	; if we matched, append only a null
	lea	arc_ext(pc),a1		; A1 -> '.arc',0 again
add_arc_ext_loop:
	move.b	(a1),(a3)+		; append chars to arc_name
	move.b	(a1)+,(a4)+		; and to wild_name
	 bne.s	add_arc_ext_loop	; stop when we moved the null


find_first:		; find first matching file
;==========
;
	move.l	a2,File_ptr(a5)		; save pointer to filename
	move.b	#0,This_path_flag(a5)	; no files yet on this path
	move.b	-1(a2),Pre_name_char(a5); save the char before the name portion
	move.b	d6,Wild_flag(a5)	; were there wildcards?
	 beq.s	try_lock		;  N: lock the whole thing
	move.b	#0,(a2)			; null terminate path @ name
try_lock:
	move.l	#arc_name,d1		; D1 -> path to lock
	moveq	#SHARED_LOCK,d2
	sys	Lock			; attempt a shared file lock
	move.l	d0,Dir_lock(a5)		; save the directory lock or 0
	 beq	lock_error		; did it lock?  N: report error
	move.b	Pre_name_char(a5),-1(a2); restore char in case we blasted it
use_lock:
	move.l	d0,d1			; D1 = directory lock
	move.l	#fib,d2			; D2 -> file info block for examine
	sys	Examine			; get file/directory info
	tst.l	d0			; did we examine it?
	 beq	examine_error		;  N: report error
	tst.b	d6			; are we wild?
	 beq.s	use_match		;  N: use this file
	tst.l	fib_type		; is this a directory?
	 bmi	path_error		;  N: invalid path

find_next:
	move.l	Dir_lock(a5),d1		; D1 = directory lock
	move.l	#fib,d2			; D2 -> file info block for examine
	sys	ExNext			; try to get next file info in dir
	tst.l	d0			; were there any files?
	 beq	no_match		;  N: go wrap things up
process_wild_arcname:
	lea	wild_name,a1		; A1 -> wildcard name
	lea	fib_name,a2		; A2 -> name we found
	bsr	match_wild		; do they match?
	 bne.s	find_next		;  N: try, try again!

use_match:
	lea	fib_name,a1		; A1 -> found name
	move.l	File_ptr(a5),a2		; A2 -> spot for name in path
matched_name_loop:
	move.b	(a1)+,(a2)+		; move name
	 bne.s	matched_name_loop	;...until we move the null


process_archive:
;===============
;
	moveq	#0,d0			; reset	totals counters
	move.w	d0,Total_cnt(a5)
	move.l	d0,Total_sizenow(a5)
	move.l	d0,Total_length(a5)
	move.l	d0,Inbuf_len(a5)	; reset input buffer length

	move.b	#1,This_path_flag(a5)	; flag that this path is valid

	bsr	open_arc		; open arcfile for read. handles error

	print	arc_title		; print title for this entry
	lea	arc_name,a1		; and archive name
	bsr	print_string
	print	verbose_heading		; output the heading

process_arc_entry:
;-----------------
;
	bsr	get_header		; load next header
	 beq	check_totals		;  EOF? Y: this arc's done

	lea	H_name(a5),a2		; copy file name
	lea	v_name(pc),a3
	moveq	#13-1,d2
format_name_loop:
	move.b	(a2)+,(a3)+		; move next name byte
	dbeq	d2,format_name_loop	; until we moved a 0
	 bne.s	increment_count		; if we didn't move a zero, fudge it
	subq.l	#1,a3
pad_name_loop:
	move.b	#' ',(a3)+		; pad rest of name with blanks
	dbra	d2,pad_name_loop
increment_count:
	addq.w	#1,Total_cnt(a5)	; increase item count

	move.l	H_length(a5),d0		; get actual file size
	move.l	H_sizenow(a5),d2	; length of file in archive
	lea	v_save_rate(pc),a1	; format savings rate
	bsr	format_percent

	moveq	#0,d0			; determine compression type
	move.b	H_code(a5),d0
	lsl.b	#3,d0			; D0 = code*8
	lea	methods-8(pc),a2
	add.l	d0,a2			; A2 -> correct method name
	lea	v_method(pc),a3
	move.l	(a2)+,(a3)+		; move entry's 8 bytes
	move.l	(a2),(a3)

	move.l	H_sizenow(a5),d0	; format file's compressed size
	add.l	d0,Total_sizenow(a5)
	lea	v_sizenow(pc),a1
	bsr	format_dec32

	move.l	H_length(a5),d0		; format file's real length
	add.l	d0,Total_length(a5)
	lea	v_length(pc),a1
	bsr	format_dec32

	move.w	H_date(a5),d0		; format file date
	lea	v_date(pc),a1
	bsr	format_date

	move.w	H_time(a5),d0		; format file time
	lea	v_time(pc),a1
	bsr	format_time

	print	verbose_line		; display this file info

	move.l	H_sizenow(a5),d2	; D2 = size of file to skip
	bsr	seek_arc		; seek to next arcfile entry

	bra	process_arc_entry


check_totals:
;------------
;
	bsr	close_arc		; close the arcfile first

	move.w	Total_cnt(a5),d0	; D0 = total # of files
	 beq	output_nl		; were there any?  N: skip totals

	lea	v_total_cnt(pc),a1	; format count of files in arc
	bsr	format_dec16

	move.l	Total_length(a5),d0	; format total of actual file size
	lea	v_total_len(pc),a1
	bsr	format_dec32

	move.l	Total_sizenow(a5),d0	; format total of archived file size
	lea	v_total_size(pc),a1
	bsr	format_dec32

	move.l	Total_length(a5),d0	; format entire savings rate
	move.l	Total_sizenow(a5),d2
	lea	v_total_rate(pc),a1
	bsr	format_percent

	leaprt	verbose_totals		; display totals
	bsr.s	print_string

output_nl:
	print	nl_msg

	tst.b	Wild_flag(a5)		; were there wildcards?
	 beq.s	exit			;  N: we're all done!
	bra	find_next


arcname_error_msg:
;-----------------
; Entry:
;	D1 -> message to print before printing the arc_name & trying next arc
	bsr.s	print_string
	lea	arc_name,a1
;	bra.s	error_msg


error_msg:	; output error message, then check totals for this arcfile
;---------
; Entry:
;	D1 -> message to print
;
	move.l	Stack_ptr(a5),sp	; naturalize stack first
	bsr.s	print_string		; then output the error message
	bra.s	check_totals		; produce totals anyway if needed


arcname_msg_exit:
;----------------
; Entry:
;	D1 -> message to print before printing the arc_name & exiting
;
	bsr.s	print_string
	lea	arc_name,a1
;	bra.s	msg_exit


msg_exit:
;--------
; Entry:
;	D1 -> message to print before exiting
;
	bsr.s	print_string
	leaprt	nl_msg
	bsr.s	print_string
;	bra.s	exit


exit:		; clean up, then call exit2 if we are really finished
;----
;
	bsr.s	close_arc		; close the archive if necessary
	move.l	Dir_lock(a5),d1		; D1 = directory lock or 0
	 beq.s	check_arglist		; is it 0?  Y: nothing to unlock
	sys	UnLock			;  N: release the lock
check_arglist:
	move.l	Arg_ptr(a5),a1		; A1 -> where we left off in arg list
	 cmp.b	#' ',(a1)		; is it at a space?
	 beq	process_next_arg	;  Y: continue arg parsing
	leaprt	nl_msg			;  N: output final NewLine
	bsr.s	print_string
	move.l	a6,a1			; A1 = the DOS library
	move.l	_AbsExecBase,a6		; A6 -> exec base
	sys	CloseLibrary		; Close the DOS library
;	bra.s	exit2			; and REALLY exit


exit2:		; set DOS error level and exit
;-----
;
	move.l	Stack_ptr(a5),sp	; restore stack (just in case)
	moveq	#0,d0			; zap error code
	rts				; exit


;------------
print_string:	; output a null terminated string
;------------
; Entry:
;	A1 -> null terminated string
; Changed:
;	D0,D1,D2,D3, A0*,A1*
;
	move.l	a1,d2			; save start of string in D2
string_count_loop:
	tst.b	(a1)+			; found ending hex null?
	 bne.s	string_count_loop	;  N: keep looking
	subq.l	#1,a1			; backup to null

	move.l	Stdout(a5),d1		; D1 = stdout
	sub.l	d2,a1			; (D2 -> string)
	move.l	a1,d3			; D3 = length
	sys	Write			; write the string
	cmp.l	d0,d3			; was the write successful?
	 bne.s	exit			;  N: might as well exit w/o error
	rts


;--------
open_arc:	; open an archive
;--------
; Changed:
;	D0,D1,D2, A0*,A1*
;
	move.l	#arc_name,d1		; D1 -> arc_name to open
	move.l	#MODE_OLDFILE,d2	; Open file for shared input
	sys	Open
	tst.l	d0
	 beq.s	open_error
	move.l	d0,Arc_handle(a5)	; save arc's file handle
	rts
open_error:
	leaprt	arc_open_err
	bra	arcname_error_msg

;---------
close_arc:
;---------
; Changed:
;	D0,D1, A0*,A1*
;
	move.l	Arc_handle(a5),d1	; D1 = previous handle
	 beq.s	close_arc_ret		; if not open, we're done
	moveq	#0,d0
	move.l	d0,Arc_handle(a5)	; zero handle # as flag
	sys	Close			;  Y: close it
close_arc_ret:
	rts


;--------
seek_arc:
;--------
; Entry:
;	D2 = amount to seek forward from the "current location" taking into
;	     account the "unread" bytes already in the read-ahead buffer.
; Changed:
;	D0,D1,D2,D3, A0*,A1*
;
	move.l	Arc_handle(a5),d1	; D1 = arcfile's handle #
	moveq	#0,d3			; D3.l = relative to current position
	sub.l	Inbuf_len(a5),d2	; D2.l = actual amount to seek forward
	 bls.s	use_buffer		; if it's already in the buffer, use it
	sys	Seek			; otherwise attempt to seek there
	addq.l	#1,d0			; did we fail? (D0 == -1?)
	 bne.s	we_are_there		;  N: use 0 in D3 to force inbuf read

	leaprt	seek_err		; let them know the bad news
	bra	error_msg

use_buffer:
	sub.l	d2,d3			; D3 = (0-D2): # of bytes now in buffer
	add.l	Inbuf_len(a5),d2	; D2 = # of bytes forward in buffer
	add.l	d2,Inbuf_ptr(a5)	; bump the buffer ptr

we_are_there:
	move.l	d3,Inbuf_len(a5)	; save current length of buffer
a_ret:
	rts


;----------
get_header:
;----------
; Exit:
;	ZF set if EOF
; Changed:
;	D0,D1,D2,D3,D6,D7, A0*,A1*,A2,A3
;
	bsr	get_arc_byte		; get next file	byte
	 beq.s	a_ret			; at eof?  Y: return w/o error
	cmp.b	#ARCMARK,d0		; start	of header?
	 beq.s	get_code		;  Y: we're AyeOkay
	moveq	#127-1,d7		;  N: scan thru extra 127 bytes
find_mark_loop:
	bsr	get_arc_byte		; get next file	byte
	 beq.s	report_skip		; eof?  Y: tell 'em about it
	cmp.b	#ARCMARK,d0		; start	of header?
	 beq.s	report_skip		;  Y: report skip & continue
	dbra	d7,find_mark_loop	;  N: keep tryin'!
no_header:
	leaprt	no_header_err		; tell 'em that it wasn't found
	bra	error_msg

header_eof:
	leaprt	header_eof_err
	bra	error_msg
report_skip_1:
	print	byte_skip_1
	bra.s	get_code

report_skip:
	moveq	#126,d0
	sub.w	d7,d0			; D0 = # of bytes skipped-1
	 beq.s	report_skip_1		; if 0, we skipped only 1
	addq.w	#1,d0			; else, increment it for output
	lea	skip_cnt(pc),a1		; A1 -> number in skip message
	bsr	format_dec16
	print	byte_skip_msg		; tell 'em about the skipped bytes

get_code:
	bsr	get_arc_byte		; grab the code byte
	 beq.s	no_header		; if none, we can assume no header
	move.b	d0,H_code(a5)		; store compression code
	 beq.s	get_header_ret		; found archive EOF?  Y: done w/ZF set

	moveq	#13-1,d7		; get 13 char member name
	lea	H_name(a5),a3		; A3 -> raw storage location
get_name_loop:
	bsr.s	get_arc_byte
	 beq.s	header_eof
	move.b	d0,(a3)+		; slam it in w/o checking it
	dbra	d7,get_name_loop

	moveq	#14-1,d7		; D7 = # of remaining bytes
	cmp.b	#1,H_code(a5)		; is this the old format?
	 bne.s	get_remaining_bytes	;  N: it's the full header
	moveq	#10-1,d7		;  Y: it's missing arced size
get_remaining_bytes:
	lea	header_lengths(pc),a2	; A2 -> table of value lengths
get_next_item_loop:
	add.l	(a2)+,a3		; table: 0,4,2,2,2,4
	move.l	(a2),d6			; used to reverse the Intel order
get_next_byte_loop:
	subq.l	#1,d6			; decrease current value's count
	 bmi.s	get_next_item_loop	; end of value?  Y: go on to next
	bsr.s	get_arc_byte
	 beq.s	header_eof
	move.b	d0,0(a3,d6.l)		; store value in Motorola order
	dbra	d7,get_next_byte_loop

	move.b	H_code(a5),d0
	cmp.b	#ARC_VER_MAX,d0		; reasonable code value?
	 bhi.s	arc_code_error		;  N: junk out
	subq.b	#1,d0			; is this the old format?
	 bne.s	get_header_ret		;  N: return w/ZF reset
	move.l	H_sizenow(a5),H_length(a5);Y: set arced size to length
	addq.b	#1,d0			; reset ZF for return
get_header_ret:
	rts

arc_code_error:
	print	arc_code_err
	move.l	H_sizenow(a5),d2
	bsr	seek_arc		; attempt to skip past this entry
	bra	get_header		; and try again


;------------
get_arc_byte:	; Return next arcfile byte
;------------
; Exit:
;	D0.b == next char from the arcfile
;	ZF is set on EOF
; Changed:
;	D0,D1,D2,D3, A0*,A1*,A4
;
	subq.l	#1,Inbuf_len(a5)	; any chars left in buffer?
	 bcc.s	grab_arc_byte		;  Y: go grab it
	bsr.s	read_arc		;  N: read another buffer
	 bne.s	get_arc_byte		; if !EOF, try again
	rts				; else return ZF as EOF flag
grab_arc_byte:
	move.l	Inbuf_ptr(a5),a4	; A4 -> next byte
	move.b	(a4)+,d0
	move.l	a4,Inbuf_ptr(a5)	; save ptr & reset ZF
	rts


;--------
read_arc:	; Read another block from the arcfile
;--------
; Exit:
;	input_buffer is filled from arcfile and inbuf_ptr & inbuf_len set
;	ZF is set on EOF
; Changed:
;	D0,D1,D2,D3, A0*,A1*
;
	move.l	Arc_handle(a5),d1	; arc file handle
	move.l	#input_buffer,d2	; D2 -> input buffer
	move.l	d2,Inbuf_ptr(a5)	; reset input buffer ptr
	move.l	#INBUF_SIZE,d3		; input	buffer size
	sys	Read			; read a block
	tst.l	d0			; was there a read error?
	 bmi.s	read_arc_error		;  Y: report the bad news
	move.l	d0,Inbuf_len(a5)	; return byte count & set ZF if EOF
	rts

read_arc_error:
	leaprt	io_err
	bra	arcname_msg_exit	; split the entire scene


;-----------
format_date:
;-----------
; Entry:
;	D0.w == Date in (weirdly packed) IBM standard:  yyyyyyymmmmddddd
;	A1   -> Date destination (Format: dd Mmm yy)
; Changed:
;	D0,D1,D2,D3, A1,A2
;
	move.w	d0,-(sp)		; push date
	and.b	#$1F,d0			; get day part
	bsr	format_dec8		; store it as ascii numerals

	move.w	(sp),d0			; peek at the date
	lsr.w	#3,d0			; shift it 3 of 5 bits
	and.l	#$3C,d0			; so that D0 = month * 4
	lea	months(pc),a2
	add.l	d0,a2			; A2 -> month name
	addq.l	#1,a1			; point A1 past space
	move.l	(a2),(a1)+		; store all 4 bytes

	move.w	(sp)+,d0		; pop the date back
	add.w	#80*512,d0		; adjust for base year in high bits
	moveq	#7-1,d2			; process top 7 bits
	bra	format_dec8_b		; store the year


;-----------
format_time:
;-----------
; Entry:
;	D0.w == Time in (weirdly packed) IBM standard:  hhhhhmmmmmmsssss
;	A1   -> Time destination (Format: hh:mmx)
; Changed:
;	D0,D1,D2,D3,D6, A1,A2
;
	moveq	#'a',d6			; assume am for now
	rol.w	#5,d0			; rotate hour low, minutes high
	move.w	d0,-(sp)		; push modified time
	and.w	#$1F,d0			; D0 = hour
	 bne.s	not_midnight		; is it midnight?  N: skip
	moveq	#12,d0			;  Y: change it to twelve
	bra.s	format_hour
not_midnight:
	cmp.b	#12,d0			; is this pm? or noon?
	 bcs.s	format_hour		;  N:
	 beq.s	use_pm			;  Y: found noon
	sub.b	#12,d0			;  Y: subtract 12 from pm
use_pm:
	moveq	#'p',d6
format_hour:
	bsr	format_dec8		; store hour
	addq.l	#1,a1			; point A1 past ':'

	move.w	(sp)+,d0		; pop time back
	moveq	#6-1,d2			; process top 6 bits
	bsr.s	format_dec8_b		; store minutes
	or.b	#'0',-2(a1)		; make sure mins has a leading zero
	move.b	d6,(a1)			; save 'a'm or 'p'm
	rts


;--------------
format_percent:
;--------------
; Entry:
;	D0.l == real length
;	D2.l == arced length
;	A1   -> 4 byte ascii storage area w/preceeding space
; Changed:
;	D0,D1,D2,D3,D6, A1,A2
;
	swap	d0			; put high word in low word for test
smallify_loop:
	tst.w	d0			; big number?
	 beq.s	smallify_done		;  N: we can use it
	ror.l	#1,d0			;  Y: reduce it by a factor of two
	and.w	#$7FFF,d0		; remove bit from its odd position
	lsr.l	#1,d2			; reduce corresponding length too
	bra.s	smallify_loop

smallify_done:
	swap	d0			; D0 & D2 = smallified sizes
	move.l	d0,d1			; save uncompressed length in D1
	move.w	#1000,d3		; D3 = 1000 -- we use it a lot
	moveq	#' ',d6			; assume positive savings factor
	move.b	d6,-1(a1)		; zap any previous '-' (as in -100.0%)

	sub.l	d2,d0			; D0 = amount saved
	 beq.s	store_result		; is it zero?  Y: report no savings
	 bcc.s	times_1000		; was the savings negative?
	neg.l	d0			;  Y: negate it
	moveq	#'-',d6			; and use negative sign
times_1000:
	mulu	d3,d0			; setup for 3 digits of % accuracy
	tst.l	d1			; is the file's real length 0?
	 bne.s	divide_em		;  N: proceed with divide
	moveq	#0,d0			;  Y: File savings is 0%
	tst.b	d2			;     ...if arclength is also 0
	 beq.s	store_result
	bra.s	store_1000		; otherwise, use -100.0%

divide_em:
	divu	d1,d0			; (savings*1000)/full-size
	lsr.l	#1,d1			; D1 = half the divisor
	swap	d0
	sub.w	d1,d0			; is remainder > .5*divisor?
	swap	d0			; (set NF if so)
	 bmi.s	check_1000		;  N: leave it alone
	addq.w	#1,d0			;  Y: round up
check_1000:
	cmp.w	d3,d0			; don't ever display > 100.0%
	 bls.s	store_result
store_1000:
	move.w	d3,d0			; force it to be 100.0%
store_result:
	bsr.s	format_dec16		; output it as a 4 digit number
	move.b	(a2),(a1)		; bump last digit (tenths) down a byte
	move.b	#'.',(a2)		; to make room for the '.'
	or.b	#'0',-(a2)		; make sure ones digit is non-space
sign_loop:
	cmp.b	#' ',-(a2)		; look for first empty digit
	 bne.s	sign_loop
	move.b	d6,(a2)			; store sign ('-' or ' ')
	rts


;-----------
format_dec8:	; formats an 8 bit integer into 2 bytes of ascii
;-----------
; Entry:
;	D0.b == value to convert
;	A1   -> 2 byte zone to fill with ascii output
; Exit:
;	(see format_decimal)
; Changed:
;	D0,D1,D2,D3,D4, A1,A2
;
	ror.w	#8,d0			; put bits in high part of word
	moveq	#8-1,d2			; D2 = process 8 bits
format_dec8_b:				; (use this entry to use less bits)
	moveq	#1,d1			; use 1 packed BCD byte of precision
	swap	d0			; put bits in high part of long
	bra.s	format_decimal


;------------
format_dec16:	; formats a 16 bit integer into 4 bytes of ascii
;------------
; Entry:
;	D0.w == value to convert
;	A1   -> 4 byte zone to fill with ascii output
; Exit:
;	(see format_decimal)
; Changed:
;	D0,D1,D2,D3,D4, A1,A2
;
	moveq	#16-1,d2		; D2 = process 16 bits
	moveq	#2,d1			; use 2 packed BCD bytes of precision
	swap	d0			; put bits up high whar we can get 'em
	bra.s	format_decimal


;------------
format_dec32:	; formats a 32 bit integer into ascii
;------------
; Entry:
;	D0.l == value to convert
;	A1   -> 8 byte zone to fill with ascii output
; Exit:
;	(see format_decimal)
; Changed:
;	D0,D1,D2,D3,D4, A1,A2
;
	moveq	#32-1,d2		; D2 = process all 32 bits
	moveq	#4,d1			; use 4 packed BCD bytes of precision
;	bra.s	format_decimal


;--------------
format_decimal:
;--------------
; Entry:
;	D0.l == value to convert (in highest bits)
;	D1.l == # of packed BCD digits to use (generates two ascii per BCD)
;	D2.w == # of bits to process - 1
;	A1   -> byte zone (2*D1 long) to fill with ascii output
; Exit:
;	A1   -> past last digit
;	A2   -> @last digit
;	byte zone is stuffed w/ascii digits w/leading spaces
; Changed:
;	D0,D1,D2,D3,D4, A1,A2
;
	move.l	a1,a2
	add.l	d1,a2			; A2 -> start of BCD zone

	move.l	d1,d3
	subq.w	#1,d3			; loop through all the BCD bytes
	move.l	d3,d4			; save BCD count for first ripple_loop
clear_zone_loop:
	move.b	#0,(a2)+		; zero the BCD working area
	dbra	d3,clear_zone_loop	; A2 -> past end of BCD zone when done
	move.l	a2,a3			; A3 -> same spot

find_1st_bit_loop:	; cheat by skipping unset high bits
	lsl.l	#1,d0			; keep shifting
	dbcs	d2,find_1st_bit_loop	; until we find one (CF set)
	 bcs.s	ripple_loop		; if CF is set, let's get started!
	sub.l	d1,a2			; A2 -> start of BCD zone
	bra.s	f_dec_ascii		; otherwise there twarnt any, weese done

bit_loop:		; take the long road thru all the remaining bits
	add.l	d1,a2			; point A2 back past end of BCD zone
	add.l	d1,a3			; ditto A3
	move.l	d1,d4
	subq.w	#1,d4			; loop through all the BCD bytes
	lsl.l	#1,d0			; set CF to next bit
ripple_loop:
	 abcd	-(a2),-(a3)		; ripple bit into our BCD number
	 dbra	d4,ripple_loop
	dbra	d2,bit_loop		; loop for more bits

f_dec_ascii:				; (A1 still -> start of ascii output)
	moveq	#' ',d3			; use leading spaces

	move.l	d1,d4			; process all the BCD bytes
	subq.w	#1,d4
f_dec_ascii_loop:
	move.b	(a2),d0			; grab next BCD digit pair
	lsr.b	#4,d0			; shift for high nibble, sets ZF if 0
	bsr	store_one_digit		; stores digit & bumps ptr (A1)
	move.b	(a2)+,d0		; grab BCD digits again & bump ptr (A2)
	and.b	#$0F,d0			; mask for low nibble, sets ZF if 0
	bsr	store_one_digit		; store & bump
	dbra	d4,f_dec_ascii_loop	;...till we're done

	or.b	#'0',-(a2)		; make sure last digit is present
	rts

store_one_digit:
	 beq.s	pod_move		; if zero, leave leading char alone
	move.b	#'0',d3			; else, start using ascii digits
pod_move:
	or.b	d3,d0			; insert ' ' or add in '0'
	move.b	d0,(a1)+		; store next char
	rts


;----------
match_wild:
;----------
; Entry:
;	A1 -> wild name
;	A2 -> name to match
; Exit:
;	ZF set if equal
; Changed:
;	D0,D1,D2,D3, A1,A2,A3
;
	moveq	#0,d3			; past_splat (D3) -> NULL
next_match:
	move.b	(a1)+,d1		; D1 = wild_char
	cmp.b	#'*',d1			; is it a '*'?
	 bne.s	not_splat		;  N: skip
	moveq	#'?',d0			;  Y: D0 = multi-match '?'
	bra.s	setup_multi_match
not_splat:
	cmp.b	#'#',d1			; is wild_char a '#'?
	 bne.s	match_em_up		;  N: skip
	move.b	(a1)+,d0		;  Y: D0 = multi-match 'char'
	 beq.s	wildcard_error		; no 'char'? Y: report invalid wildcard
setup_multi_match:
	move.l	a1,d3			; past_splat (D3) -> next wild_char
	move.l	a2,a3			; A3 -> string to recurse at
	bra.s	next_match		; start new multi-match
match_em_up:
	move.b	(a2)+,d2		; D2 = next match_char
	 beq.s	match_wild_ret		; end of match str?  Y: check success
	cmp.b	#'?',d1			; is wild-char a '?' ?
	 beq.s	next_match		;  Y: we match anything, continue
	cmp.b	#'Z',d1			; lower-case wild_char
	 bhi.s	m_upper1
	cmp.b	#'A',d1
	 bcs.s	m_upper1
	add.b	#$20,d1
m_upper1:
	cmp.b	#'Z',d2			; lower-case match_char
	 bhi.s	m_upper2
	cmp.b	#'A',d2
	 bcs.s	m_upper2
	add.b	#$20,d2
m_upper2:
	cmp.b	d1,d2			; are they equal?
	 beq.s	next_match		;  Y: continue
	tst.l	d3			; is any multi-matching going on?
	 beq.s	fail_match_ret		;  N: fail
	move.l	d3,a1			; restart post-splat matching string
	move.l	a3,a2			; restart match_char's end too
	addq.l	#1,a3			; bump recurse ptr
	move.b	d0,d1			; put the wild_match char in D1
	bra.s	match_em_up		; & see if they qualify to continue
fail_match_ret:
	moveq	#1,d1			; force failure
match_wild_ret:
	tst.b	d1			; if match_string is exausted,
	rts				; return success (ZF set)
wildcard_error:
	leaprt	wildcard_err
	bra	arcname_error_msg


header_lengths	dc.l 0,4,2,2,2,4

_data		ds.b 0			; start of relative data section


;	Define A5 relative data structures:  (DS_ & DC_'s)

	DS_long Stack_ptr,1		; stack	pointer	upon entry
	DS_long Stdout,1		; standard output handle

	DC_long Arc_handle,0		; file handle
	DC_long Dir_lock,0		; directory lock

	DC_long Arg_ptr,0		; ptr to next arg (if multiple)

	DC_byte This_path_flag,0	; flag if this path has a file
	DC_byte Got_arg_flag,0		; flag if we process a file

;	These variables will reuse the usage_msg for some space savings

_data2		ds.b 0			; Header:
	DS_byte H_code,1		;  compression code
	DS_byte H_name,13		;  file name
	DS_long H_sizenow,1		;  file size in archive
	DS_word H_date,1		;  creation date
	DS_word H_time,1		;  creation time
	DS_word H_crc,1			;  cyclic redunancy check
	DS_long H_length,1		;  uncompressed length

	DS_long File_ptr,1		; ptr to filename part of arc_name

	DS_long Total_length,1		; total	of file	lengths
	DS_long Total_sizenow,1		; total	of file	sizes
	DS_word Total_cnt,1		; total	number of files

	DS_long Inbuf_ptr,1		; ptr to current byte in buffer
	DS_long Inbuf_len,1		; bytes	left in	buffer

	DS_byte Pre_name_char,1		; holder when we fiddle with path
	DS_byte Wild_flag,1		; flag for wild filespecs


	RORG _data2			; jump back to overlap vars

usage_msg	dc.b NL,'Amiga ARCV v1.06 -- Verbose ARC directory display'
		dc.b ' -- By:  Wayne Davison',NL
		dc.b NL,'Usage:  ARCV [Drv:][Path/]ArcName[.ARC] ...',NL
		dc.b NL,'"ArcName" may contain the wildcards:  ? * and #c',0

verbose_heading	dc.b NL,NL
 dc.b '  Filename     Length    Method   Size now  Savings    Date      Time',NL
 dc.b '------------  --------  --------  --------  -------  ---------  ------',0

	cnop	1,2

verbose_line	dc.b NL
 v_name:	dcb.b 14,' '		; 14 spaces for name
 v_length:	dc.b '       0  '	; length of uncompressed file
 v_method:	dc.b '.method.  '	; compression type
 v_sizenow:	dc.b '       0  '	; compressed file size
 v_save_rate:	dc.b ' xx.x%   '	; compression factor
 v_date:	dc.b 'dd Mmm yy  '	; creation date
 v_time:	dc.b 'hh:mmx'		; creation time
		dc.b 0

verbose_totals	dc.b NL,'        ----  --------            --------  -------'
		dc.b NL,'Total   '
 v_total_cnt:	dc.b '   0  '
 v_total_len:	dc.b '       0  '
		dc.b '          '
 v_total_size:	dc.b '       0  '
 v_total_rate:	dc.b ' xx.x%'
		dc.b 0

	cnop	0,2

methods		dc.b ' (none) '		; 1 = old, no compression
		dc.b ' (None) '		; 2 = new, no compression
		dc.b ' Packed '		; 3 = dle for repeat chars
		dc.b 'Squeezed'		; 4 = huffman encoding
		dc.b 'crunched'		; 5 = lz, no dle
		dc.b 'crunched'		; 6 = lz with dle
		dc.b 'Crunched'		; 7 = lz with re-adjust
		dc.b 'Crunched'		; 8 = lz with re-adjust and dle
		dc.b 'Squashed'		; 9 = modified lzw, no dle

months		dc.b '--- Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec '

dos_lib		dc.b 'dos.library',0

arc_ext		dc.b '.arc',0

arc_title	dc.b  NL,'Archive:  ',0

byte_skip_msg	dc.b NL,'** Skipped'
 skip_cnt:	dc.b ' xxx bytes **',0
byte_skip_1	dc.b NL,'** Skipped 1 byte **',0
nl_msg		dc.b NL,0

wild_path_err	dc.b NL,'Wildcards not allowed in path.',0
not_found_err	dc.b NL,'File not found:  ',0
path_err	dc.b NL,'Path not found:  ',0
examine_err	dc.b NL,'Examine failed:  ',0
no_match_err	dc.b NL,'No match:  ',0
wildcard_err	dc.b NL,'Invalid wildcard:  ',0
arc_open_err	dc.b NL,'Unable to open archive:  ',0
seek_err	dc.b NL,'** Unable to seek to next arc header -- '
		dc.b 'Premature end-of-file **',0
no_header_err	dc.b NL,'** Unable to find archive header **',0
header_eof_err	dc.b NL,'** Unexpected end-of-file while reading '
		dc.b 'archive header **',0
arc_code_err	dc.b NL,'** Invalid compression code -- Skipping file **',0
io_err		dc.b NL,'I/O error reading ',0


	section	bss_sect,bss


input_buffer	ds.b	INBUF_SIZE

arc_name	ds.b	128		; room for the arc path/name

wild_name	ds.b	128		; room for the wild filespec

fib		ds.b	0		; room for File Info Block
 fib_key:	ds.l	1
 fib_type:	ds.l	1
 fib_name:	ds.b	108
 fib_PESB:	ds.l	4		; (prot, entry, size, blocks)
 fib_date:	ds.l	3
 fib_comment:	ds.b	116

	end
