[net.sources] Public Domain ATTRIB command for PC

rde@ukc.ac.uk (R.D.Eager) (01/29/86)

[food for line eater]

Craig  Milo  Rogers  asked (in Info-IBMPC) if anyone had a better ATTRIB
command.  Here it is! I know there are plenty of these  around,  but  he
did  ask  and  in  any case this one is upward-compatible with the DOS 3
version.

Perhaps someone could feed this to Info-IBMPC; I can't do it from here.

----------CUT HERE-----------CUT HERE-----------CUT HERE-----------------
	PAGE	64,132
	TITLE	ATTRIB - a program to alter file attributes
	NAME	ATTRIB
;
; This program was written by Bob Eager, Herne Bay, England.
; It is placed in the public domain. No part of this program
; may be copied and sold for gain.
;
; There isn't really much in the way of documentation. ATTRIB
; works like the DOS 3 command of the same name (except that it
; is a lot smaller and also DOES a lot MORE!)
;
; For those unfamiliar with that:
;    ATTRIB filename     -   reports file attributes as single letters
;                            (H, S, R)
;    ATTRIB +x filename  -   sets the x attribute (H, S or R)
;    ATTRIB -x filename  -   unsets the x attribute (H, S or R)
;
;  The filename may be a wildcard.
;
; Inverse attributes are provided as a friendly touch:
;
;      Hidden   <==>   Visible
;      System   <==>   User
;      Read     <==>   Write
;
; So +R is the same as -W, etc.
;
;   Enjoy!
;
	.SALL
;
; Values for exit status:
;    0 - Success
;    1 - Parameter too long
;    2 - Incorrect DOS version
;    3 - File does not exist
;    4 - Invalid attribute letter
;
; Constants
;
TAB	EQU	09H			; Tabulate
LF	EQU	0AH			; Linefeed
CR	EQU	0DH			; Carriage return
PTHSEP1	EQU	'\'			; Normal path separator
PTHSEP2	EQU	'/'			; Alternate path separator
;
STDOUT	EQU	1			; Standard output handle
STDERR	EQU	2			; Standard error handle
MAXFNAM	EQU	63			; Maximum length of filename
;
; Macro definitions
;
MSG	MACRO	X,Y			;; define a message
MES&X	DB	Y
LMES&X	EQU	$-MES&X
	ENDM
;
$MSG	MACRO	X			;; display a message on standard error
	MOV	BX,STDERR		;; file handle
	MOV	DX,OFFSET MES&X		;; location of message
	MOV	CX,LMES&X		;; length of message
	MOV	AH,40H			;; write function
	INT	21H
	ENDM
;
CSEG	SEGMENT
;
	ORG	100H
;
	ASSUME	CS:CSEG,DS:CSEG,ES:CSEG,SS:CSEG
;
MAIN	PROC	FAR
	JMP	MAIN10
;
	SUBTTL	Data areas
	PAGE+
;
; Storage
;
ATT	DW	?			; Mask and bits specified by parameter
FIRST	DB	?			; Used for 'find first' calls
PARPTR	DW	?			; Pointer to next parameter
PSIZE	DW	?			; Size of path part of filename
DISP	DB	?,?,?,'    '		; Attribute display string
LDISP	EQU	$-DISP			; Length of DISP
FNAME	DB	MAXFNAM+1 DUP (?)	; Extra byte for terminator
DTA	DB	43 DUP (?)		; Alternate disk transfer area
;
; Table of attribute settings
;
; The table ATTC is a list of valid attribute letters
; The table ATTON corresponds one-to-one with ATTC for setting an attribute
; The table ATTOFF corresponds one-to-one with ATTC for clearing an attribute
; ATTON and ATTOFF use the high byte of each entry as a mask to clear
; unwanted bits, and the low byte to indicate bits to be set.
;
ATTC	DB	'RWHVSU'
NATT	EQU	$-ATTC
ATTON	DW	0FF01H,0FE00H,0FF02H,0FD00H,0FF04H,0FB00H
ATTOFF	DW	0FE00H,0FF01H,0FD00H,0FF02H,0FB00H,0FF04H
;
CRBUF	DB	CR,LF
;
	DW	32 DUP (?)		; Stack
STACK	LABEL	WORD
;
; Messages
;
	MSG	1,<'Parameter too long',CR,LF>
	MSG	2,<'Filename too long - '>
	MSG	3,<'Incorrect DOS version',CR,LF>
	MSG	4,<'No matching file found for '>
	MSG	5,<'Invalid attribute letter',CR,LF>
;
	SUBTTL	Main code
	PAGE+
;
MAIN10:	MOV	SP,OFFSET STACK		; set up stack
;
	MOV	AH,30H
	INT	21H			; get DOS version
	XCHG	AH,AL			; make more useful form
	CMP	AX,0200H		; version 2 at least?
	JAE	MAIN20			; j if so - OK
	$MSG	3			; "Incorrect DOS version"
	MOV	AL,2			; exit status
	JMP	SHORT MAIN60		; join common exit code
;
MAIN20:	MOV	DX,OFFSET DTA		; set alternate DTA to avoid...
	MOV	AH,1AH			; ...overwriting parameters
	INT	21H
	MOV	PARPTR,81H		; start of parameter area
	CALL	GETFLAGS		; get any attribute flags
	JC	MAIN60			; j if error (unrecognised flag)
	MOV	ATT,AX			; save information for later on
MAIN30:	CALL	GETPAR			; read next parameter item
	JC	MAIN50			; j if no more parameters
	MOV	BYTE PTR FIRST,0	; clear 'find first' flag
MAIN40:	CALL	GETNAM			; get next filename
	JC	MAIN30			; j if no more names
	CALL	DOATTR			; handle attribute
	JC	MAIN60			; j if error (status in AL)
	JMP	MAIN40			; see if more to do
;
MAIN50:	XOR	AL,AL			; zero status
;
MAIN60:	MOV	AH,4CH			; exit
	INT	21H
MAIN	ENDP
;
	SUBTTL	Get attribute flag from command line
	PAGE
;
; This routine reads an attribute flag and its sense from the command line.
; Attribute flags start with '+' or '-' and continue with a letter.
; Possible letters are:  R  -  read only
;                        W  -  read and write (inverse of read only)
;                        H  -  hidden
;                        V  -  visible (inverse of hidden)
;                        S  -  system
;                        U  -  user (inverse of system)
;
; On entry:  PARPTR points to the first byte of the parameter string
;
;  On exit:  PARPTR has been updated past the attribute specification if
;            one was found
;
; Return is made with AH containing a mask to remove any unwanted bits,
; and with AL containing any additional bits to be set in the attributes.
; The value AX=0 is returned if no attribute flag was found, and carry is set
; if an unrecognised attribute letter was encountered.
;
GETFLAGS PROC	NEAR
	CLD				; autoincrement
	MOV	SI,PARPTR		; point to start of parameter list
	PUSH	SI			; save for possible restoration
GETF10:	LODSB				; skip leading spaces...
	CMP	AL,' '
	JE	GETF10
	CMP	AL,TAB			; ...and tabs
	JE	GETF10
	CMP	AL,'+'			; valid sense?
	JE	GETF20			; j if so
	CMP	AL,'-'			; other sense?
	JE	GETF20			; j if so
	POP	AX			; restore the parameter...
	MOV	PARPTR,AX		; ...pointer
	XOR	AX,AX			; indicate no attribute specified
	RET				; return with carry clear
;
GETF20:	POP	BX			; lose saved parameter pointer
	PUSH	AX			; save sense
	LODSB				; get attribute letter
	CALL	UPPER			; convert to upper case
	MOV	BX,OFFSET ATTC		; list of valid letters
	MOV	CX,NATT			; count of valid letters
	XOR	DI,DI			; pointer into letter table
GETF30:	CMP	AL,[BX+DI]		; found the letter?
	JE	GETF40			; j if so
	INC	DI			; point to next possibility
	LOOP	GETF30			; keep trying
	$MSG	5			; "Invalid attribute letter"
	POP	AX			; lose sense from stack
	MOV	AL,4			; exit status
	STC				; indicate error
	RET
;
GETF40:	MOV	BX,OFFSET ATTON		; assume sense 'on'
	POP	AX			; now check specified sense
	CMP	AL,'-'
	JNE	GETF50			; j if sense 'on' required
	MOV	BX,OFFSET ATTOFF	; else use different table
GETF50:	ADD	DI,DI			; form word offset
	MOV	AX,[BX+DI]		; get mask and attribute word
	MOV	PARPTR,SI		; update parameter line pointer
	CLC				; indicate success
	RET
GETFLAGS ENDP
;
	SUBTTL	Get next parameter
	PAGE
;
; This routine reads the next parameter from the command line, converting
; it to upper case and placing it into the area FNAME.
; Parameters are delimited by spaces, tabs, commas or the end of the line.
;
; On exit:
;    Carry is clear if a parameter was found
;    Carry is set if the parameter list is exhausted
;
GETPAR	PROC	NEAR
	CLD				; autoincrement
	MOV	SI,PARPTR		; point to start of possible parameter
GETP10:	LODSB				; skip leading spaces...
	CMP	AL,' '
	JE	GETP10
	CMP	AL,TAB			; ...and tabs
	JE	GETP10
	MOV	DI,OFFSET FNAME		; area to hold item
	MOV	CX,MAXFNAM		; maximum item size
GETP20:	CMP	AL,CR			; end of list?
	JNE	GETP30			; j if not
	DEC	SI			; must re-read next time
	JMP	SHORT GETP40
GETP30:	CMP	AL,' '			; other terminators...
	JE	GETP40
	CMP	AL,TAB
	JE	GETP40
	CMP	AL,','
	JE	GETP40			; ...are space, tab and comma
	CALL	UPPER			; convert to upper case
	STOSB				; store character away
	LODSB				; get next character
	LOOP	GETP20
;
GETP40:	JCXZ	GETP60			; j if too long
	XOR	AL,AL			; add terminator
	STOSB
	MOV	PARPTR,SI		; save new value
	CMP	CX,MAXFNAM		; null item?
	JNE	GETP50			; j if not
	STC				; indicate no more
	RET
;
GETP50:	CLC				; indicate item found
	RET
;
GETP60:	$MSG	1			; "Parameter too long"
	MOV	AL,1			; exit status
	JMP	MAIN60
GETPAR	ENDP
;
	SUBTTL	Get next filename
	PAGE
;
; This routine gets the next filename matching the parameter.
; The filename is left in FNAME.
;
; On exit:
;    Carry is clear if a match was found
;    Carry is set if no further matches exist
;
GETNAM	PROC	NEAR
	CLD				; autoincrement
	CMP	FIRST,0			; first time with this parameter item?
	JNE	GETN60			; j if not
	INC	FIRST			; mark not first any more
;
	MOV	SI,OFFSET FNAME		; find last separator
GETN10:	MOV	BX,SI			; initial value
GETN20:	LODSB				; get next byte of name
	OR	AL,AL			; end of name?
	JZ	GETN30			; j if so
	CMP	AL,PTHSEP1		; normal separator?
	JE	GETN10			; j if so, and update pointer
	CMP	AL,PTHSEP2		; alternate separator?
	JE	GETN10			; j if so, and update pointer
	CMP	AL,':'			; drive name?
	JE	GETN10			; j if so; this counts as separator
	JMP	GETN20
;
; BX now points at first character of last component of filename
;
GETN30:	SUB	BX,OFFSET FNAME		; derive length of path part
	MOV	PSIZE,BX
	MOV	DX,OFFSET FNAME
	MOV	CX,07H			; get read only, system and hidden files too
	MOV	AH,4EH			; find first matching file
	INT	21H
	JNC	GETN40			; j if match found
	$MSG	4			; "No matching file found for "
	MOV	BX,STDERR
	CALL	PRNAME
	MOV	BX,STDERR
	CALL	CRLF
	STC				; indicate error
	RET
;
GETN40:	MOV	SI,OFFSET DTA+30	; terminal filename
	MOV	DI,OFFSET FNAME
	ADD	DI,PSIZE		; point to filename part of path
	MOV	CX,MAXFNAM
GETN50:	LODSB
	STOSB
	OR	AL,AL
	LOOPNE	GETN50			; copy name and terminator
	JCXZ	GETN70			; j if name too long
	CLC
	RET				; return with carry clear
;
GETN60:	MOV	AH,4FH			; find next matching file
	INT	21H
	JNC	GETN40			; j if match found
	RET				; return with carry still set
;
GETN70:	$MSG	2			; "Filename too long - "
	MOV	BX,STDERR
	CALL	PRNAME
	MOV	BX,STDERR
	CALL	CRLF
	STC				; indicate error
	RET
GETNAM	ENDP
;
	SUBTTL	Actual attribute alteration and display code
	PAGE
;
DOATTR	PROC	NEAR
	CMP	ATT,0			; just display required?
	JZ	DOA100			; j if so
;
	MOV	AX,4300H		; first get existing attributes
	MOV	DX,OFFSET FNAME
	INT	21H			; attribute to CX (just CL really)
	JC	DOA200			; j if error (should not happen)
	AND	CL,BYTE PTR ATT+1	; remove unwanted bits
	OR	CL,BYTE PTR ATT		; add new bits
	MOV	AX,4301H		; set attributes now
	MOV	DX,OFFSET FNAME
	INT	21H
	JC	DOA200			; j if error (should not happen)
	RET
;
DOA100:	MOV	AX,4300H		; get attribute
	MOV	DX,OFFSET FNAME
	INT	21H			; returns attribute in CX
	JC	DOA200			; j if error (should not happen)
;
	MOV	DI,OFFSET DISP		; point to output area
	CLD				; autoincrement
	MOV	AH,' '
	MOV	AL,AH			; assume space initially
	TEST	CL,01H			; read only attribute?
	JZ	DOA110			; j if not
	MOV	AL,'R'			; else set character
DOA110:	STOSB				; set up for output
	MOV	AL,AH			; restore space
	TEST	CL,02H			; hidden attribute?
	JZ	DOA120			; j if not
	MOV	AL,'H'			; else set character
DOA120:	STOSB				; set up for output
	MOV	AL,AH			; restore space
	TEST	CL,04H			; system attribute?
	JZ	DOA130			; j if not
	MOV	AL,'S'			; else set character
DOA130:	STOSB				; set up for output
	MOV	BX,STDOUT		; handle
	MOV	CX,LDISP		; byte count for message
	MOV	DX,OFFSET DISP
	MOV	AH,40H			; write function
	INT	21H
;	JC	DOA200			; j if error
	MOV	BX,STDOUT		; handle
	CALL	PRNAME			; follow up with filename
	MOV	BX,STDOUT
	CALL	CRLF			; finish off line
DOA200:	RET
DOATTR	ENDP
;
	SUBTTL	Miscellaneous subroutines
	PAGE
;
; Routine to convert the letter in AL to upper case.
;
UPPER	PROC	NEAR
	CMP	AL,'a'			; check lower range
	JB	UPP10			; j if out of range
	CMP	AL,'z'			; check upper range
	JA	UPP10			; j if out of range
	SUB	AL,'a'-'A'		; do the conversion
UPP10:	RET
UPPER	ENDP
;
; Routine to write a carriage return and linefeed to the file specified
; by the handle in BX.
;
CRLF	PROC	NEAR
	MOV	CX,2			; byte count
	MOV	DX,OFFSET CRBUF
	MOV	AH,40H			; write function
	INT	21H
	RET
CRLF	ENDP
;
; Routine to output the filename in FNAME to the file specified by the
; handle in BX.
;
PRNAME	PROC	NEAR
	MOV	DI,OFFSET FNAME
	PUSH	DI			; save for later
	CLD				; autoincrement
	XOR	AL,AL			; search for zero byte
	MOV	CX,0FFFFH		; very long loop!
	REPNE	SCASB
	MOV	CX,DI
	POP	DX			; initial pointer
	SUB	CX,DX			; derive length
	MOV	AH,40H			; write function
	INT	21H
	RET
PRNAME	ENDP
;
CSEG	ENDS
;
	END	MAIN
----------CUT HERE-----------CUT HERE-----------CUT HERE-----------------
-- 
           Bob Eager

           rde@ukc.UUCP
           rde@ukc
           ...!mcvax!ukc!rde

           Phone: +44 227 66822 ext 7589