;	ATTR.ASM -- File Attribute Utility
;	==================================

CSEG		Segment
		Assume	CS:CSEG, DS:CSEG, ES:CSEG, SS:CSEG
		Org	0080h
Parameter	Label	Byte		; Parameter is here
		Org	0100h
Entry:		Jmp	Begin		; Entry Point

;	Most Data (some more at end of program)
;	---------------------------------------

		db	"ATTR (C) 1986, Ziff-Davis Publishing Co.",1Ah
		db	" Programmed by Charles Petzold ",1Ah
SyntaxMsg	db	"Syntax: ATTR [+A|-A] [+S|-S] [+H|-H] [+R|-R] "
		db	"[drive:][path]filename",13,10
		db	"             Archive System  Hidden  Read-Only$"   
DosVersMsg	db	"ATTR: Needs DOS 2.0 +$"
FlagErrMsg	db	"ATTR: Incorrect flag$"
FileSpecMsg	db	"ATTR: Incorrect File Spec$"
Delimiters	db	9,' ,;=',13
FlagList	db	"ASHR", 20h, 04h, 02h, 01h
AllFlagList	db	"    $Arc $Dir $$$$$$Sys $Hid $R-O$"
ChangeFlag	db	0
AndAttrBits	db	0
OrAttrBits	db	0
SearchString	dw	?
AppendFileName	dw	?

;	Check DOS Version
;	-----------------

Begin:		Mov	AH, 30h			; Check for DOS Version
		Int	21h			;   through DOS call
		Cmp	AL, 2			; See if it's 2.0 or above
		Jae	DosVersOK		; If so, continue

		Mov	DX, Offset DosVersMsg	; Error message
ErrorExit:	Mov	AH, 9			; Print String function call
		Int	21h			; Do it
		Int	20h			; And exit prematurely

;	Parse Command Line to get file specification
;	--------------------------------------------

DosVersOK:	Mov	SI, 1+Offset Parameter	; Parameter string pointer
		Cld				; Directions forward

FlagSearch:	Lodsb				; Get Byte
		Mov	DI, Offset Delimiters	; Check if delimiter
		Mov	CX, 5			; Five delimiters to check
		Repne	Scasb			; Scan the string
		Je	FlagSearch		; If delimiter, circle back
		Mov	DX, Offset SyntaxMsg	; Possible error msg 
		Cmp	AL, 13			; If carriage return, no file
		Je	ErrorExit		;   so exit with message
				
		Mov	DI, Offset OrAttrBits	; Pointer to plus flag saver
		Cmp	AL, '+'			; See if plus sign
		Je	PlusOrMinus		; If so, save the bit
		Mov	DI, Offset AndAttrBits	; Pointer to minus flag saver
		Cmp	AL, '-'			; See if minus sign
		Jne	MustBeFile		; If not, it must be file name

PlusOrMinus:	Mov	[ChangeFlag],-1		; Set for changing
		Lodsb				; Get the next byte
		And	AL, 0DFh		; Capitalize it
		Mov	BX, Offset FlagList	; List for scanning
		Mov	CX, 4			; Scan for A, S, H, and R

SearchList:	Cmp	AL, [BX]		; See if a match
		Jz	FoundFlag		; If so, proceed to save	
		Inc	BX			; Kick up pointer 
		Loop	SearchList		; And loop around for next
		Mov	DX, Offset FlagErrMsg	; Otherwise, set message 
		Jmp	ErrorExit		; And terminate

FoundFlag:	Mov	AL, [BX + 4]		; Get bit mask
		Or	[DI], AL		; Turn saved bit on
		Jmp	FlagSearch		; And continue looking

MustBeFile:	Not	[AndAttrBits]		; Invert bits for turn off
		Mov	[SearchString], SI	; Save file name pointer
		Dec	[SearchString]		; Actually one byte lower

EndSearch:	Lodsb				; Get Byte
		Mov	DI, Offset Delimiters	; Check if delimiter
		Mov	CX, 6			; Six delimiters including CR
		Repne	Scasb			; Scan the string
		Jne	EndSearch		; If not delimiter, keep going

;	Transfer Search String down at end of program
;	---------------------------------------------

		Dec	SI			; Points after file spec
		Mov	Byte Ptr [SI], 0	; Make it ASCIIZ string
		Mov	CX, SI			; CX points to end
		Mov	SI, [SearchString]	; SI points to beginning
		Sub	CX, SI			; Now CX is length of it
		Mov	DI, Offset PathAndFile	; Destination of string
		Mov	[AppendFileName], DI	; Save it here also

SearchTrans:	Lodsb				; Get byte of search string
		Stosb				; And save it down below
		Cmp	AL, ':'			; See if drive marker
		Je	PossibleEnd		; If so, take note of it
		Cmp	AL, '\'			; See if path separator
		Jne	NextCharacter  		; If not, skip next code

PossibleEnd:	Mov	[AppendFileName], DI	; This is the new end
NextCharacter:	Loop	SearchTrans		; Do it again until done

;	Find Files from Search String
;	-----------------------------

		Mov	DX, Offset DTABuffer	; Set File Find buffer
		Mov	AH, 1Ah			;   by calling DOS
		Int	21h

		Mov	DX, [SearchString]	; Search string
		Mov	CX, 16h			; Search Everything
		Mov	AH, 4Eh			; Find first file 

FindFile:	Int	21h			; Call DOS to find file
		Jnc	Continue		; If no error continue
		Cmp	AX, 18			; If not "no more files" error
		Jnz	FindError		;   print error message
		Jmp	NoMoreFiles		; Now get out of the loop

FindError:	Mov	DX, Offset FileSpecMsg	; Error message for file spec
		Jmp	ErrorExit		; Exit and print message

Continue:	Mov	SI, 30+Offset DTABuffer	; Points to filename
		Cmp	Byte Ptr [SI], '.'	; See if "dot" entry
		Jnz	FileIsOK		; If not, continue
		Jmp	FindNextFile		; If so, skip it

FileIsOK:	Mov	DI, [AppendFileName]	; Destination of file name
		Mov	CX, 14			; Number of bytes to display

TransferName:	Lodsb				; Get the byte in file name
		Stosb				; Save it
		Or	AL, AL			; See if terminating zero
		Jz	PadWithBlanks		; If so, display blanks
		Call	DisplayChar		; Display the character
		Loop	TransferName		; And loop back around

PadWithBlanks:	Mov	AL, ' '			; Pad names with blanks
		Call	DisplayChar
		Loop	PadWithBlanks		; And loop until CX is zero

;	Change And Display File Attributes
;	---------------------------------- 

		Mov	DX, Offset PathAndFile	; Points to ASCIIZ string
		Test	[ChangeFlag], -1	; See if changing attributes
		Jz	DisplayIt		; If not, just display them 

		Mov	AX, 4300h		; Get file attribute
		Int	21h			;   by calling DOS
		And	CL, 27h			; Zero out some bits
		And	CL, [AndAttrBits]	; Turn off some bits
		Or	CL, [OrAttrBits]	; Turn on some bits
		Mov	AX, 4301h		; Set file attribute
		Int	21h			;   by calling DOS

DisplayIt:	Mov	AX, 4300h		; Get file attribute
		Int	21h			;   by calling DOS
		Mov	BL, CL			; BL is attributes
		Or	BL, 08h			; Turn on Volume bit
		Shl	BL, 1			; Shift to get rid of
		Shl	BL, 1			;   unused bits
		Mov	CX, 6			; Number of bits left
		Mov	DX, 5+Offset AllFlagList; Storage of abbreviations
		
AttrListLoop:	Push	DX			; Save abbreviation pointer
		Shl	BL, 1			; Shift bit into carry
		Jc	FlagIsOn		; See if it's on
		Mov	DX, Offset AllFlagList	; If not, print blanks

FlagIsOn:	Mov	AH, 9			; Print string
		Int	21h			;   by calling DOS
		Pop	DX			; Get back abbreviation ptr
		Add	DX, 5			; Kick up for next bit
		Loop	AttrListLoop		; And loop around
		Mov	AL, 13			; Print carriage return
		Call	DisplayChar
		Mov	AL, 10			; Print line feed
		Call	DisplayChar

FindNextFile:	Mov	AH, 4Fh			; Find next file
		Jmp	FindFile		; By looping around

NoMoreFiles:	Int	20h			; Terminate

;	SUBROUTINE: Display Character in AL
;	-----------------------------------

DisplayChar:	Push	AX
		Push	DX
		Mov	DL, AL			; Move character to DL
		Mov	AH, 2			; Display it
		Int	21h			;   by calling DOS
		Pop	DX
		Pop	AX
		Ret

;	Some data stored at end to cut down COM size
;	--------------------------------------------

DTABuffer	Label	Byte			; For file find calls
PathAndFile	equ	DTABuffer + 43		; For file path and name 
CSEG		EndS				; End of the segment
		End	Entry			; Denotes entry point
