PCjs Machines

Home of the original IBM PC emulator for browsers.

Logo

PC-SIG Diskette Library (Disk #564)

[PCjs Machine "ibm5170"]

Waiting for machine "ibm5170" to load....

Information about “JON DART’S UTILITIES”

Dos Utilities is a collection of usefull utilities for the PC user.
These programs do a number of diffrent things, from making a file read
only to doing a word count. There is even source code included on the
disk if you want to see how the programs were written.

Special Requirements:  None.

How to Start:  Type GO (press enter).

Suggested Registration:

File Descriptions:

CLEAN    EXE  Program to remove control chars. from a file.
CP       EXE  File/directory copy utility.
CRC      EXE  Cyclic Redundancy Code generator.
DETAB    EXE  Program to expand tabs to spaces.
FDUMP    EXE  File dump/patch utility.
FIXLINES EXE  Makes all lines in a file end with CR/LF.
HEAD     EXE  Displays first few lines of a file.
LS-PC    EXE  Directory listing program.
LS       EXE  Directory listing program.
MODEM    EXE  Simple communications program.
MV       EXE  File/directory move utility.
RM       EXE  Improved DEL function.
RO       EXE  Sets file to read-only status.
RW       EXE  Sets file to read-write status.
SHOW     EXE  Bidirectional file display program.
TAIL     EXE  Displays last few lines of a file.
UPDATE   EXE  Makes date and time of a file current.
UTIL     DOC  Documentation for all programs.
WC       EXE  Date/word/line counting utility.
SOURCE   ARC  Source code for the above files.

FILES564.TXT

Disk No  564
Program Title: Dos Utilities version 3.31
PC-SIG version 2.0

Dos Utilities is a collection of usefull utilities for the PC user.
These programs do a number of diffrent things, from making a file read
only to doing a word count. There is even source code included on the
disk if you want to see how the programs were written.

Usage:  Utilities.

Special Requirements:  None.

How to Start:  Type GO (press enter).

Suggested Registration:

File Descriptions:

CLEAN    EXE  Program to remove control chars. from a file.
CP       EXE  File/directory copy utility.
CRC      EXE  Cyclic Redundancy Code generator.
DETAB    EXE  Program to expand tabs to spaces.
FDUMP    EXE  File dump/patch utility.
FIXLINES EXE  Makes all lines in a file end with CR/LF.
HEAD     EXE  Displays first few lines of a file.
LS-PC    EXE  Directory listing program.
LS       EXE  Directory listing program.
MODEM    EXE  Simple communications program.
MV       EXE  File/directory move utility.
RM       EXE  Improved DEL function.
RO       EXE  Sets file to read-only status.
RW       EXE  Sets file to read-write status.
SHOW     EXE  Bidirectional file display program.
TAIL     EXE  Displays last few lines of a file.
UPDATE   EXE  Makes date and time of a file current.
UTIL     DOC  Documentation for all programs.
WC       EXE  Date/word/line counting utility.
SOURCE   ARC  Source code for the above files.

PC-SIG
1030D E Duane Avenue
Sunnyvale Ca. 94086
(408) 730-9291
(c) Copyright 1987,88,89 PC-SIG, Inc.


GO.TXT

╔═════════════════════════════════════════════════════════════════════════╗
║             <<<< Disk No 564  Dos Util. version 3.31 >>>>               ║
╠═════════════════════════════════════════════════════════════════════════╣
║ To run a program, type the name of the file that you wish to run.       ║
║ For example, if you wanted to run the program called SHOW, you would    ║
║ type the following:                                                     ║
║                SHOW (press enter)                                       ║
╚═════════════════════════════════════════════════════════════════════════╝

CONSOLE.ASM

        TITLE   MSDOS CONSOLE I/O ROUTINES
        .XLIST
        INCLUDE ASCII.DEF
        INCLUDE MSDOS2.DEF
        .LIST

	DOSSEG
	.MODEL SMALL
	.CODE
EXTRN	UC:NEAR
PUBLIC  COUT,SCIN,CIN,CLRCO,CRLF,PRTSTR,ERRORMSG,GETYORN

;       CONSOLE I/O ROUTINES FOR MSDOS

;	PRTSTR = PRINTS ASCIIZ STRING TO STDOUT
;       ENTRY: DS:DX POINTS TO MSG (ASCIIZ STRING)
;       USES: AX,DX,FLAGS
;
PRTSTR	PROC   NEAR
        PUSH    BX
PRTSTR1: MOV	BX,DX
        MOV     AL,BYTE PTR [BX]
        CMP     AL,0
        JZ      PRTSTR2
	CALL	COUT
        INC     DX
        JMP     PRTSTR1
PRTSTR2:
        POP     BX
        RET
PRTSTR	ENDP

;       COUT = OUTPUT CHARACTER IN AL
;
COUT    PROC    NEAR
        PUSH    DX
        PUSH    AX
        MOV     DL,AL
        MOV     AH,STD_CON_OUTPUT
        INT     DOS
        POP     AX
        POP     DX
        RET
COUT    ENDP

;       SCIN = SEE IF CONSOLE CHARACTER AVAILABLE
;       RETURNS 'C'=1 IF NOT AVAILABLE
;       ELSE RETURNS CHAR. IN AL
;
SCIN    PROC    NEAR
        MOV     AH,CON_INPUT_STATUS
        INT     DOS
        CMP     AL,0
        JNZ     SHORT GOTSOME
        STC
        RET
GOTSOME:
        CALL    NEAR PTR CIN
        CLC
        RET

SCIN    ENDP

;       CIN = GET CHARACTER FROM KEYBOARD
;       EXIT:  CHAR IN AL
;
CIN     PROC    NEAR
        MOV     AH,CON_INPUT_NO_ECHO
        INT     DOS
        RET
CIN     ENDP

;       CLRCO = CLEAR TYPE-AHEAD BUFFER
;
CLRCO   PROC    NEAR
CLRC1:  CALL    SCIN
        JNC     CLRC1
        RET
CLRCO   ENDP

;
;       CRLF = SEND CR + LF TO CONSOLE
;
CRLF    PROC    NEAR
        MOV     AL,CR
        CALL    COUT
        MOV     AL,LF
        CALL    COUT
        RET
CRLF    ENDP

;       ERRORMSG = WRITE MESSAGE TO STD. ERROR OUTPUT
;       ENTRY: DS:DX POINTS TO MSG (ASCIIZ STRING)
;       USES: AX,DX,FLAGS
;
ERRORMSG PROC   NEAR
        PUSH    BX
        PUSH    CX
ERRMSG1:
	MOV	BX,DX
        MOV     AL,BYTE PTR [BX]
        XCHG    DX,BX
        CMP     AL,0
        JZ      ERRMSG2
        MOV     CX,1
        MOV     AH,WRITE
        MOV     BX,2
        INT     DOS
        INC     DX
        JMP     ERRMSG1
ERRMSG2:
        POP     CX
        POP     BX
        RET
ERRORMSG ENDP

; GETYORN = GET 'Y' OR 'N' RESPONSE FROM CONSOLE
; 	    RETURNS CHAR. ( Y OR N ) IN AX

GETYORN PROC	NEAR
	CALL	CLRCO			;CLEAR TYPE-AHEAD
NOTYN:	CALL	CIN			;GET CONSOLE CHAR.
	CALL	UC			;MAKE UPPER-CASE
	CMP	AL,'Y'
	JE	GOTY
	CMP	AL,'N'
	JNE	NOTYN			;IF NOT Y OR N
GOTY:	PUSH	AX
	CALL	COUT
	CALL	CRLF
	POP	AX
	MOV	AH,0			;IN CASE USER DOES WORD COMPARISON
	RET
GETYORN ENDP

        END

CONVERT.ASM

        TITLE   CONVERSION ROUTINES
;	LAST MODIFIED 04-OCT-86
        .XLIST
        INCLUDE ASCII.DEF
        .LIST

	DOSSEG
	.MODEL	SMALL

	.DATA?
TEMPSTOR DB    5 DUP(?)

	.CODE
EXTRN   COUT:NEAR
        PUBLIC BTODEC, DTOBIN, DECOUT

;       BTODEC - CONVERT BINARY TO DECIMAL
;       ENTRY:  (AX) = BINARY WORD
;               (BX) = ADDRESS FOR DECIMAL DIGITS
;       USES:   AX,BX,CX,DX
;
BTODEC  PROC    NEAR
        MOV     CX,10000
BTOD1:
        PUSH    CX
        MOV     DL,"0"
SUBMORE:
        SUB     AX,CX
        INC     DL
        JNB     SUBMORE
        DEC     DL
        ADD     AX,CX
        MOV     [BX],DL
        INC     BX
        POP     CX
        PUSH    AX
        PUSH    BX
        MOV     AX,CX
        MOV     BX,10
        MOV     DX,0
        DIV     BX
        POP     BX
        MOV     CX,AX
        CMP     CX,0
        POP     AX
        JNE     BTOD1
        RET
BTODEC  ENDP

;       DECOUT = OUTPUT DECIMAL NUMBER TO CONSOLE
;       ENTRY:  BX = NUMBER (IN BINARY)
;               CX = FIELD WIDTH
;               AL = CHAR. TO USE INSTEAD OF LEADING "0"
;		(0 TO SUPPRESS LEADING 0'S)
;
DECOUT  PROC    NEAR
        PUSH    AX
        MOV     AX,BX
        MOV     BX,OFFSET TEMPSTOR
        PUSH    CX
        CALL    BTODEC
        POP     CX
        POP     AX
        MOV     BX,OFFSET TEMPSTOR+5
        SUB     BX,CX
        CMP     CX,1
        JLE     SHOWDEC
        DEC     CX
ZEROSUP:
        MOV     DL,[BX+0]
        CMP     DL,"0"
        JNZ     SHORT DONESUP
	AND	AL,AL
	JZ	SUP0
        CALL    COUT
SUP0:
	INC     BX
        LOOP    ZEROSUP
DONESUP:
        INC     CX
SHOWDEC:
        MOV     AL,[BX]
        CALL    COUT
        INC     BX
        LOOP    SHOWDEC
        RET
DECOUT  ENDP

;       DTOBIN = CONVERT DECIMAL TO BINARY
;       ENTRY: BX POINTS TO DECIMAL DIGIT (MSD)
;              CX HOLDS NUMBER OF DIGITS (5 MAX.)
;       EXIT: 'C'=1 IF OVERFLOW OR ILLEGAL CHAR. IN DIGIT STRING
;               AX = RESULT OF CONVERSION
;
DTOBIN  PROC    NEAR
        MOV     DX,0            ;ACCUMULATOR FOR RESULT
DTOB1:  MOV     AL,[BX+0]
        SUB     AL,"0"
        JB      SHORT BADCHAR
        CMP     AL,10
        JGE     SHORT BADCHAR
        PUSH    AX
        INC     BX
        PUSH    BX
        MOV     AX,DX
        MOV     BX,10
        MUL     BX
        POP     BX
        JC      SHORT OVFLOW    ;IF >65535
        MOV     DX,AX
        POP     AX
        MOV     AH,0
        ADD     DX,AX
        LOOP    DTOB1
        MOV     AX,DX
        CLC
        RET
OVFLOW:
BADCHAR:
        STC
        RET
DTOBIN  ENDP

        END

FIXPATH.ASM

        TITLE   MSDOS2 PATHNAME PARSER

PATHSIZE EQU    65
        .XLIST
        INCLUDE MSDOS2.DEF
	INCLUDE MACROS.DEF
        .LIST

	DOSSEG
	.MODEL SMALL

	.DATA
SSTARS  DB      '\'
STARS   DB      '*.*',0
DOTS    DB      '..',0
SLASHALONE  DB  '\',0

	.DATA?
CURDIR  DB	PATHSIZE DUP (?)
DRIVE	DB	1 DUP (?)

	.CODE
        EXTRN CPYCNT:NEAR
	EXTRN SKIPSP:NEAR
	EXTRN UC:NEAR
        PUBLIC TYPE_UFN,TYPE_AFN,TYPE_DIR
        PUBLIC TYPE_DSP,TYPE_DRV,TYPE_UNK
        PUBLIC FIXPATH

;       THIS PROCEDURE PARSES A (POSSIBLY AMBIGUOUS) DOS PATH NAME
;       INTO A SEARCH PATH AND A LEAD-IN PATH.  THE SEARCH PATH CAN
;       BE USED AS AN ARGUMENT TO DOS FUNCTIONS 4E AND 4F.  THE
;       LEAD-IN PATH IS INTENDED TO BE PREFIXED TO THE SIMPLE FILENAME
;       RETURNED BY THESE FUNCTIONS, IN ORDER TO PRODUCE A COMPLETE
;       PATH NAME USABLE BY OPEN, RENAME, ETC.
;
;       DS = ES IS ASSUMED.
;
;       MODELLED AFTER THE C FUNCTION fixpath() IN DR. DOBB'S JOURNAL,
;       OCT. 1985, PP. 16-18.
;
;       ENTRY: BX - POINTS TO INPUT PATH
;              CX - POINTS TO SEARCH PATH
;              DX - POINTS TO LEAD-IN PATH
;       EXIT:
;              AX = 0 IF PATH IS AN UNAMBIGUOUS FILE NAME
;                 = 1 IF PATH IS AN AMBIGUOUS FILE NAME (E.G. "FOO?.*")
;                 = 2 IF PATH IS A DIRECTORY SPEC (E.G.: A:\FOO\BAR\")
;                 = 3 IF PATH IS A DRIVE SPEC (E.G. "A:") OR NULL STRING
;                 = 4 IF PATH IS A DIRECTORY
;                 = 5 IF PATH TYPE UNKNOWN OR DOES NOT EXIST
;
;       USES:  SI,DI,FLAGS
;
TYPE_UFN EQU    0
TYPE_AFN EQU    1
TYPE_DRV EQU    2
TYPE_DSP EQU    3
TYPE_DIR EQU    4
TYPE_UNK EQU    5

COPYPATH PROC   NEAR
        PUSH    CX
        MOV     CX,PATHSIZE
        CALL    CPYCNT
        POP     CX
	RET
COPYPATH ENDP

FIXPATH PROC    NEAR
	CALL	SKIPSP
        JB      DRVSPEC                 ;IF NULL STRING
	MOV	BYTE PTR DRIVE, 0	;SET DRIVE TO DEFAULT
        CMP     BYTE PTR [BX+1],':'     ;CHECK FOR DRIVE SPEC
        JNE     NODSPEC                 ;IF NONE
	MOV	AL, BYTE PTR [BX]	;GET DRIVE LETTER
	CALL	UC			;MAKE UPPER-CASE
	SUB	AL, 'A'-1		;MAKE BINARY
	MOV	BYTE PTR DRIVE, AL	;SAVE IT
        CMP     BYTE PTR [BX+2],0       ;CHECK FOR ONLY DRIVE SPEC
        JE      DRVSPEC                 ;IF THAT'S WHAT IT IS
NODSPEC:
        MOV     DI,BX
	PUSH	CX
	MOV	CX,PATHSIZE
	XOR	AX,AX
	REPNE	SCASB			;SEARCH FOR 0 BYTE
	JCXZ	TOOLONG			;IF PATH TOO LONG
GOTEND:
	SUB	DI,2			;POINT TO LAST CHAR.
	POP	CX
        MOV     AL,[DI]                 ;GET LAST CHAR.
        CMP     AL,'\'                  ;IS IT BACKSLASH?
        JNE     FIXPATH2                ;NO.
        MOV     AX,TYPE_DSP
        JMP     SHORT DIRSPEC           ;YES, IT IS, ASSUME (*.*)
TOOLONG:
	POP	CX
	MOV	AX,TYPE_UNK		;IF PATH TOO LONG
	RET
DRVSPEC:
        MOV     AX,TYPE_DRV
DIRSPEC:
        PUSH    AX                      ;SAVE PATH TYPE
        MOV     SI,BX
        MOV     DI,DX
        CALL    COPYPATH                ;COPY INPUT TO LEAD-IN PATH
        MOV     SI,BX
        MOV     DI,CX
        CALL    COPYPATH                ;COPY INPUT TO SEARCH PATH
        DEC     DI                      ;BACK UP OVER NULL
        MOV     SI,OFFSET STARS
        CALL    COPYPATH                ;NOW ADD "*.*" TO SEARCH PATH
        POP     AX                      ;RETURN PATH TYPE
        RET
FIXPATH2:
	MOV	SI, OFFSET DOTS
	MOV	DI, BX
	COMP_STRINGS			;SEE IF NAME = ".."
	JNE	NOTUPDIR
;	.. is special case because CHMOD may not return its attribute
;	correctly
	PUSH	DX
	DOSCALL CURRENT_DISK		;GET CURRENT DISK
	PUSH	AX			;SAVE IT
	MOV	SI,OFFSET CURDIR
	MOV	BYTE PTR [SI],0		;CLEAR DIRECTORY BUFFER
	MOV	DL, BYTE PTR DRIVE
	DOSCALL	CURRENT_DIR		;GET CURRENT DIRECTORY
	POP	DX
	DOSCALL	SELECT_DISK		;RESTORE CURRENT DISK
	POP	DX
	CMP	BYTE PTR CURDIR, 0	;ROOT DIRECTORY?
	JNE	FIXPATH4		;IF NOT ROOT DIRECTORY
	MOV	AX, TYPE_UNK		;.. AT ROOT, ERROR
	JMP	FIXPATH3
NOTUPDIR:
        PUSH    CX
        PUSH    DX
        MOV     DX,BX
        MOV     CX,0
        MOV     AL,0
        DOSCALL CHMOD                   ;GET ATTRIBUTES FROM PATH
        POP     DX
        JC      BADNAME                 ;IF ERROR
        TEST    CX,0010H                ;IS IT A DIRECTORY?
        POP     CX
        MOV     AX,TYPE_UFN             ;SIGNAL UFN, JUST IN CASE
        JZ      FIXPATH3                ;IF NOT DIRECTORY, IS UFN
        JMP     SHORT FIXPATH4          ;IF DIRECTORY
BADNAME:
        POP     CX
        CMP     AX,3                    ;IF ERROR CODE 3,
        JZ      TESTWILD                ;MIGHT BE VALID (BUT AMBIGUOUS) PATH NAME
        MOV     AX,TYPE_UNK             ;SET TYPE =UNKNOWN
        JMP     SHORT FIXPATH3          ;WEIRD ERROR CODE, TREAT PATH AS A UFN
TESTWILD:                               ;CAN'T GET ATTRIBUTES, SEE IF
        PUSH    BX                      ;PATH INCLUDES WILDCARDS
WLOOP:  MOV     AL,[BX]
        CMP     AL,0
        JE      NOTWILD
        CMP     AL,'*'
        JE      WILD
        CMP     AL,'?'
        JE      WILD
        INC     BX
        JMP     WLOOP
WILD:   POP     BX                      ;FOUND WILD CARD,
        MOV     AX,TYPE_AFN             ;SET PATH TYPE = AFN
        JMP     SHORT FIXPATH3
NOTWILD:
        POP     BX                      ;NO WILD CARD FOUND,
        MOV     AX,TYPE_UNK             ;SET PATH TYPE UNKNOWN
FIXPATH3:                               ;COME HERE IF PATH IS AFN, UFN OR UNKNOWN
        PUSH    AX
        PUSH    DX
        PUSH    BX
        MOV     SI,BX
        MOV     DI,CX
        PUSH    CX
        CALL    COPYPATH                ;MAKE SEARCH PATH=INPUT PATH
        POP     DX                      ;DX = START OF PATH
        MOV     BX,DI                   ;BX = END OF PATH (1 PAST NULL)
        DEC     BX
SRCLUP:
	CMP     BX,DX                   ;BEFORE START OF PATH?
        JL      RETAIN                  ;YUP.
        MOV     AL,[BX]                 ;GET A CHAR. FROM PATH
        CMP     AL,'\'                  ;IS IT BACKSLASH OR COLON?
        JE      RETAIN
        CMP     AL,':'
        JE      RETAIN
        DEC     BX                      ;NO, BACK UP 1
        JMP     SRCLUP                  ;KEEP SEARCHING
RETAIN: INC     BX
        MOV     [BX],BYTE PTR 0         ;ZAP WHATEVER IS AFTER ':' OR '\'
        POP     BX
        POP     DX
        MOV     SI,CX
        MOV     DI,DX
        CALL    COPYPATH                ;COPY SEARCH PATH TO LEAD-IN PATH
        MOV     SI,BX
        MOV     DI,CX
        CALL    COPYPATH                ;WHOLE INPUT FOR SEARCH
        POP     AX                      ;RESTORE TYPE
        RET
FIXPATH4:                               ;COME HERE IF PATH IS A DIRECTORY
        MOV     SI,BX
        MOV     DI,CX
        CALL    COPYPATH                ;COPY INPUT TO SEARCH PATH
        DEC     DI                      ;BACK UP OVER NULL
        MOV     SI,OFFSET SSTARS   	;POINT TO "\*.*"
        CALL    COPYPATH                ;ADD TO SEARCH PATH
        MOV     SI,BX
        MOV     DI,DX
        CALL    COPYPATH                ;COPY INPUT TO LEAD-IN PATH
        DEC     DI                      ;BACK UP OVER NULL
        MOV     SI,OFFSET SLASHALONE    ;POINT TO "\"
        CALL    COPYPATH                ;ADD TO LEAD-IN PATH
        MOV     AX,TYPE_DIR             ;SIGNAL DIRECTORY
        RET
FIXPATH ENDP

        END

GETARGS.ASM

        TITLE   COMMAND LINE ARGUMENT PARSER

	.XLIST
	INCLUDE	MACROS.DEF
	INCLUDE ASCII.DEF
	.LIST

	DOSSEG
	.MODEL	SMALL
	.CODE
        EXTRN SKIPSP:NEAR
        PUBLIC GETARGS
;
;	COLLECT ARGUMENTS FROM COMMAND LINE
;
;	ENTRY:	DS:BX POINTS TO COMMAND LINE
;		ES:SI POINTS TO SPACE FOR ARGUMENT POINTERS
;		ES:DI POINTS TO BUFFER FOR ARGUMENTS
;		CX = MAX. # OF ARGUMENTS
;
;	EXIT:	CX = ACTUAL # OF ARGUMENTS
;		'C'=1 IF TOO MANY ARGS
;
GETARGS PROC NEAR
        MOV     DX,0                    ;DX COUNTS # OF ARGUMENTS
TOP:
	CALL	SKIPSP
	JB	ENDLINE			;IF END OF LINE
SAVEARG:
        INC     DX                      ;BUMP ARGUMENT COUNT
        CMP     DX,CX
        JG      TOOMANY                 ;IF TOO MANY ARGS
	MOV	AX,DI
        MOV     WORD PTR ES:[SI],AX     ;SAVE POINTER TO ARG
        ADD     SI,2                    ;ADVANCE TO NEXT TABLE ENTRY
CNTARGS:
        MOV     AL,BYTE PTR DS:[BX]     ;GET CHAR. FROM COMMAND LINE
        CMP     AL,0
        JE      ENDLINE                 ;IF END OF LINE
        COMPLIST <SPACE,TAB>,NEXTARG    ;SEE IF SPACE OR TAB
	MOV	BYTE PTR ES:[DI],AL	;NOT DELIM, STORE ARG. TEXT
	INC	DI
        INC     BX
        JMP     CNTARGS                 ;LOOP TILL DELIMITER FOUND
NEXTARG:
	MOV	BYTE PTR ES:[DI],0	;0-TERMINATE ARG
	INC	DI
	JMP	TOP			;BACK TO TOP OF LOOP
ENDLINE:
	MOV	BYTE PTR ES:[DI],0	;END ARG. W. 0
	MOV	CX,DX			;MOVE ARG COUNT TO CX
	CLC				;CLEAR CARRY TO SIGNAL NO ERROR
	RET
TOOMANY:
	MOV	CX,DX			;MOVE ARG COUNT TO CX
	STC				;TOO MANY ARGUMENTS, SET CARRY
	RET
GETARGS ENDP

	END

PRINTDD.ASM

        SUBTTL  PRINTDD - PRINT A DOUBLE WORD IN DI:SI
	PAGE
;       This is a modified version of a routine from SDIR Version
;       2.4, by Ted Reuss (modified from a program by John Chapman), on
;       PC-SIG Volume 185.

        .XLIST
        INCLUDE ASCII.DEF
        INCLUDE MSDOS2.DEF
        .LIST

	DOSSEG
	.MODEL	SMALL

	.DATA
LDZERO  DB      1 DUP (?)
DOUBLE  DB      8 DUP (20H)
        DB      '$'
DBLCNT  DW      1 DUP (?)

	.CODE
PUBLIC  PRINTDD

PRINTDD PROC    NEAR            ;Prints a 32 bit integer in DI:SI
                                ;AL = 0 to suppress leading 0's
				;   = "0" (60Q) to print leading 0's
				;   = " " (20Q) to print leading spaces
        MOV     BYTE PTR LDZERO,AL
	MOV	BX,OFFSET DOUBLE
	MOV	CX,8
FILL:	MOV	BYTE PTR [BX],SPACE  ;FILL BUFFER WITH SPACES
	INC	BX
	LOOP	FILL
        CALL    PRINTDX         ;format the double word into DOUBLE     2.4
        MOV     DX,OFFSET DOUBLE
        MOV     BX,DX
        MOV     CX,7            ;SUPPRESS LEADING 0'S
SUPP0:  CMP	BYTE PTR [BX],SPACE
        JNE     SPDONE
        MOV     AL,BYTE PTR LDZERO
	AND	AL,AL
	JNZ	SUPP2
	INC	DX
SUPP1:
        MOV     [BX],AL
SUPP2:
        INC     BX
        LOOP    SUPP0
SPDONE:
        MOV     AH,CON_STRING_OUTPUT
        INT     DOS             ;print the digits                       2.4
        RET                     ;and return                             2.4
PRINTDD ENDP                    ;                                       2.4

PRINTDX PROC    NEAR            ;converts integer in DI:SI to decimal   2.4
        MOV     WORD PTR DBLCNT,0   ;reset the offset for DOUBLE   2.4
        XOR     AX,AX           ;Zero out the                           2.4
        MOV     BX,AX           ; working
        MOV     BP,AX           ; registers.
        MOV     CX,32           ;# bits of precision
J1:     SHL     SI,1
        RCL     DI,1
        XCHG    BP,AX
        CALL    J6
        XCHG    BP,AX
        XCHG    BX,AX
        CALL    J6
        XCHG    BX,AX
        ADC     AL,0
        LOOP    J1
        MOV     CX,1710H        ;5904 ?
        MOV     AX,BX
        CALL    J2
        MOV     AX,BP
J2:     PUSH    AX
        MOV     DL,AH
        CALL    J3
        POP     DX
J3:     MOV     DH,DL
        SHR     DL,1            ;Move high
        SHR     DL,1            ; nibble to
        SHR     DL,1            ; the low
        SHR     DL,1            ; position.
        CALL    J4
        MOV     DL,DH
J4:     AND     DL,0FH          ;Mask low nibble
        JZ      J5              ;If not zero
        MOV     CL,0
J5:     DEC     CH
        AND     CL,CH
        OR      DL,'0'          ;Fold in ASCII zero
        SUB     DL,CL
        PUSH    DI              ;save DI                                2.4
        MOV     DI,OFFSET DOUBLE ;point to DOUBLE                  2.4
        ADD     DI,WORD PTR DBLCNT  ;add current offset            2.4
        INC     WORD PTR DBLCNT  ;bump offset for next time        2.4
        MOV     BYTE PTR DS:[DI],DL ;move next digit to DOUBLE          2.4
        POP     DI              ;restore DI                             2.4
        RET                     ;Exit to caller
PRINTDX ENDP                    ;                                       2.4

J6      PROC    NEAR
        ADC     AL,AL
        DAA
        XCHG    AL,AH
        ADC     AL,AL
        DAA
        XCHG    AL,AH
        RET
J6      ENDP

        END

UNLINK2.ASM

	INCLUDE	MSDOS2.DEF

	DOSSEG
	.MODEL	SMALL
	.CODE
	PUBLIC	UNLINK2

;	UNLINK2 = DELETE FILE EVEN IF WRITE-PROTECTED
;	ENTRY:	DS:DX POINTS TO FILE NAME
;	EXIT:	'C'=1 IF ERROR
;
UNLINK2 PROC	NEAR
	PUSH	DX
	MOV	AH,UNLINK
	INT	DOS			;TRY TO DELETE FILE
	POP	DX
	JNB	UNLEND			;IF OK
        MOV     AL,1                    ;PREPARE TO CHANGE ATTRIBUTES
        MOV     CX,0                    ;NEW ATTRIBUTES = 0
        MOV     AH,CHMOD
	PUSH	DX
        INT     DOS                     ;MAKE FILE R/W
	POP	DX
	JB	UNLEND
	MOV	AH,UNLINK
	INT	DOS			;NOW TRY TO REMOVE IT AGAIN
UNLEND: RET
UNLINK2	ENDP

	END


UTIL.ASM

        TITLE   GENERAL-PURPOSE MSDOS UTILITIES
        .XLIST
        INCLUDE ASCII.DEF
        .LIST

	DOSSEG
	.MODEL	SMALL
	.CODE
EXTRN   COUT:NEAR
        PUBLIC UC,UCSTR,SKIPSP,CPY,CPYCNT,TYPTX,FILL,ZERO
        PUBLIC CMDSRC

;       COMMON ASSEMBLY-LANGUAGE UTILITY ROUTINES

;       UC = CONVERT CHARACTER IN AL TO UPPER-CASE
;
UC      PROC    NEAR
        CMP     AL,'a'
        JC      SHORT UC1
        CMP     AL,'z'+1
        JNC     SHORT UC1
        SUB     AL,'a'-'A'
UC1:    RET
UC      ENDP

;       UCSTR = CONVERT A LINE (0-TERMINATED) TO UPPER-CASE
;
;       ENTRY:  DS:BX POINTS TO THE LINE
;       EXIT:   LINE CONVERTED, BX IS UNCHANGED
;       USES:   AL,FLAGS
;
UCSTR   PROC    NEAR
        PUSH    BX
UCSTR0: MOV     AL,BYTE PTR [BX]
        CALL    UC
        MOV     BYTE PTR [BX],AL
        INC     BX
        CMP     AL,0
        JNE     UCSTR0
        POP     BX
        RET
UCSTR   ENDP

;       SKIPSP = SKIP OVER BLANKS AND TABS
;       ENTRY:  [BX] POINTS TO CHAR. STRING
;       EXIT:   [BX] POINTS TO 1ST NON-BLANK CHAR., OR NULL
;               AL = 1ST NON-BLANK CHAR.
;               'C' FLAG SET IF AL=NULL.
;
SKIPSP  PROC    NEAR
        JMP     SHORT SKIPS1
SKIPS0: INC     BX
SKIPS1: MOV     AL,BYTE PTR [BX]
        CMP     AL,SPACE
        JE      SKIPS0
        CMP     AL,TAB
        JE      SKIPS0
        CMP     AL,0
        JE      SKIPS2
        CLC                     ;CLEAR CARRY
        RET
SKIPS2: STC
        RET
SKIPSP  ENDP

;       CPY = COPY CX BYTES FROM (SI) TO (DI)
;
CPY     PROC    NEAR
        CLD
        REP     MOVSB
        RET
CPY     ENDP

;       CPYCNT = COPY FROM (DS:SI) TO (ES:DI) UNTIL 0 BYTE TRANSFERRED
;       TRANSFERS CX BYTES MAXIMUM
;
CPYCNT  PROC    NEAR
        CLD
CPYC0:  LODSB
        STOSB
        CMP     AL,0
        JE      SHORT CPYC1
        LOOP    CPYC0
CPYC1:
        RET
CPYCNT  ENDP

;       ZERO = CLEAR MEMORY (CX BYTES, STARTING AT ES:DI)
;
ZERO    PROC    NEAR
        MOV     AL,0

;       FILL = FILL MEMORY (CX BYTES, STARTING AT ES:DI) WITH AL
;
FILL:   CLD
        REP     STOSB
        RET
ZERO    ENDP

;       TYPTX = TYPE TEXT IN LINE
;       LAST CHAR. HAS BIT [7] SET
;
TYPTX   PROC    NEAR
        PUSH    BP              ;SAVE REGISTERS
        PUSH    BX
        MOV     BP,SP
        MOV     BX,[BP+4]       ;GET OFFSET OF DATA
        DEC     BX              ;COMPENSATE FOR NEXT INC
TYPTX1: INC     BX              ;BUMP POINTER
        MOV     AL,BYTE PTR CS:[BX]      ;FETCH A CHAR.
        AND     AL,7FH          ;STRIP HI BIT
        CALL    COUT            ;PRINT IT
        CMP     AL,BYTE PTR CS:[BX]      ;SEE IF HI BIT WAS SET
        JE      TYPTX1          ;IF NOT, KEEP GOING
        INC     BX              ;POINT 1 PAST END OF STRING
        MOV     [BP+4],BX       ;FIX OFFSET IN RETURN LINK
        POP     BX
        POP     BP              ;RESTORE REGISTERS
        RET
TYPTX   ENDP

;       CMDSRC = SEARCH FOR COMMAND IN LIST
;       ENTRY:  AL = COMMAND LETTER
;               BX POINTS TO LIST (0 BYTE ENDS IT)
;       EXIT:   AL = NUMBER OF COMMAND IN LIST, IF FOUND
;               AL = 0 IF NO MATCH
;
CMDSRC  PROC    NEAR
        PUSH    CX
        MOV     CL,0
        MOV     CH,AL
CMDSR1: MOV     AL,BYTE PTR [BX]
        CMP     AL,0
        JE      SHORT NOMATCH
        CMP     AL,CH
        PUSHF
        INC     BX
        INC     CL
        POPF
        JNZ     CMDSR1
        MOV     AL,CL
NOMATCH:
        POP     CX
        RET
CMDSRC  ENDP

        END

CP.ASM

        TITLE CP - MSDOS FILE COPY UTILITY
        PAGE 55,132
;***************************************************************************
;       THIS PROGRAM COPIES ONE OR MORE FILES, OR THE CONTENTS OF A WHOLE
;       DIRECTORY, TO ANOTHER DISK, FILE OR DIRECTORY.
;
;       USAGE (afn = ambiguous file name, ufn = unambiguous file name,
;              dir = directory):
;
;               CP ufn1 ufn2    - copies ufn1 to unf2
;
;               CP afn1 afn2 ... dir  - copies one or more files into dir.
;
;               CP -R dir1 dir2  - if dir2 doesn't exist, creates it and
;                                 copies dir1 to dir2.
;
;                               - if dir2 exists, creates dir2\dir1 and
;                                 copies dir1 to it.
;
;       SWITCHES (OPTIONAL):
;
;		CP -I  prompt before overwriting file
;
;		CP -R  allow recursive copying of directories.
;
;               CP -V  turns on "verbose" mode (shows name of each file as it
;                      is copied).
;***************************************************************************
;   TO BUILD CP.EXE:
;	MASM CP,CP,NUL,NUL
;	LINK CP,CP,NUL,ASM
;	EXEPACK CP.EXE CP.NEW
;	DEL CP.EXE
;	REN CP.NEW=CP.EXE
;***************************************************************************
;       BY: JON DART
;           3012 HAWTHORN ST.,
;           SAN DIEGO, CA 92104
;***************************************************************************
;	Version 3.3, 30-Mar-89 copies with root directory as destination
;				now handled correctly.  Also a bug fix in
;				procedure SETUP_SRC_DIR.
;	Version 3.2, 19-Jan-89 another fix to FIXPATH; relinked.
;	Version 3.1, 17-Oct-88 fix similar to one in MV: CP *.* FOO gives
;			       an error unless FOO is a directory.
;	Version 3.0, 24-Sep-88 bug fix in FIXPATH; relinked. version made
;			       consistent with MV.
;	Version 1.7, 09-Feb-88 assembles under MASM 5.0
;	Version 1.6, 28-Nov-86 fixes bug involving relative directory moves
;				(e.g. mv -r ..\foo .)
;	Version 1.5, 24-Oct-86 switch parser now accepts -rvi as well as
;			       -r -v -i
;	Version 1.4, 06-Jul-86 changes to memory alloc., allows hyphen for
;				switches, now assembles under masm 4.0
;	Version 1.3, 30-Jun-86 minor cleanup, adds i and r switches, removes
;				c switch
;       Version 1.2, 26-Apr-86 doesn't exit after user types n to overwrite
;			        msg
;       Version 1.1, 31-Mar-86 bug fixes
;       Version 1.0, 30-Mar-86 modified from MV v. 1.7
;***************************************************************************

	DOSSEG
	.MODEL	SMALL

PATHSIZE EQU    65                      ;MAX. SIZE OF DOS PATHNAME, +1
BUFSIZE EQU     32*1024                 ;SIZE OF COPY BUFFER
MAXLEVEL EQU    12                      ;MAX NESTING LEVEL FOR DIRECTORIES
MAXARGS EQU     40                      ;MAXIMUM # OF COMMAND LINE ARGUMENTS
TRUE    EQU     0FFH
FALSE   EQU     0
CONFIRM EQU     FALSE                   ;"CONFIRM BEFORE OVERWRITE" OPTION (DEFAULT VALUE)
RECURSE EQU	FALSE			;"RECURSIVE" DEFAULT
VERBOSE EQU     FALSE                   ;"VERBOSE" OPTION (DEFAULT VALUE)
M       EQU     BYTE PTR 0[BX]
BIT$DIR EQU     00010000B               ;BIT IN ATTRIBUTE FOR DIRECTORY
BIT$RO  EQU     00000001B               ;BIT IN ATTRIBUTE FOR WRITE-PROTECT

        .XLIST
        INCLUDE ASCII.DEF
        INCLUDE MSDOS2.DEF
	INCLUDE MACROS.DEF

ADDR    MACRO   REG,OFFST	        ;COMPUTE ADDRESS OFFSET FROM BASE REGISTER (BX)
	MOV	REG,BX
	ADD	REG,OFFST
        ENDM

ERROR   MACRO   ERRNUM                  ;SHOW ERROR MESSAGE
        MOV     DX,OFFSET DGROUP:MSG&ERRNUM
	CALL	PRTERR
	ENDM

COPYPATH MACRO  SOURCE,DEST             ;COPY PATHNAME FROM SOURCE TO DEST
        ADDR    SI,SOURCE
        ADDR    DI,DEST
        MOV     CX,PATHSIZE
        CALL    CPYCNT
        ENDM

        .LIST

MVINFO  STRUC
NEWDIR  DW      1 DUP (?)               ;FLAG, =1 IF NEW DIRECTORY CREATED
SRCTYPE DB     2 DUP (?)                ;TYPE OF SOURCE (0 IF UFN)
DESTTYPE DB    2 DUP (?)                ;TYPE OF DEST (0 IF UFN)
ARG1    DB      PATHSIZE DUP (?)        ;FIRST ARGUMENT (SOURCE)
ARG2    DB      PATHSIZE DUP (?)        ;SECOND ARGUMENT (DEST)
ARG2A   DB      PATHSIZE DUP (?)        ;SOURCE PREFIX + DEST
SSPATH  DB      PATHSIZE DUP (?)        ;SOURCE SEARCH PATH
SPREFIX DB      PATHSIZE DUP (?)        ;SOURCE LEAD-IN PATH
DSPATH  DB      PATHSIZE DUP (?)        ;DEST SEARCH PATH
                                        ;(USED ONLY AS SCRATCH AREA)
DPREFIX DB      PATHSIZE DUP (?)        ;DEST LEAD-IN PATH
SRCNAME DB      PATHSIZE DUP (?)        ;UNAMBIGUOUS SOURCE NAME
DESTNAME DB     PATHSIZE DUP (?)        ;UNAMBIG. DEST NAME
DTA     DB      128 DUP (?)             ;DTA FOR SEARCH FN.
MVINFO  ENDS
MVSTRUCSIZE EQU 9*PATHSIZE+135          ;SIZE OF STRUCTURE

	.DATA
;       INITIALIZED DATA STORAGE:
;
MSG1    DB 'CP Version 3.3 by Jon Dart (30-Mar-89)',CR,LF,CR,LF
        DB 'USAGE (afn = ambiguous file name, ufn = unambiguous file name,'
        DB CR,LF
        DB '       dir = directory):',CR,LF,CR,LF
        DB 'CP ufn1 ufn2          copies ufn1 to ufn2.',CR,LF,CR,LF
        DB 'CP afn1 afn2 ... dir  copies one or more files into dir.',CR,LF
        DB CR,LF
        DB "CP -R dir1 dir2       if dir2 doesn't exist, creates it and copies"
        DB CR,LF
        DB '                      all files from dir1 to dir2.',CR,LF,CR,LF
        DB "                      if dir2 exists, creates dir2\dir1 and copies"
        DB CR,LF
        DB '                      all files from dir1 into it.',CR,LF,CR,LF
        DB 'SWITCHES (OPTIONAL):',CR,LF,CR,LF
	DB '-I   prompt before overwriting files.',CR,LF,CR,LF
	DB '-R   allow recursive copying of directories.',CR,LF,CR,LF
        DB '-V   echo file names as they are copied.',CR,LF
        DB 0
MSG2    DB      ': file not found.',CR,LF,0
MSG3    DB      'Source and dest are incompatible.',CR,LF,0
MSG4    DB      ' exists. Overwrite [Y or N]? ',0
MSG5    DB      ': open failed.',CR,LF,0
MSG6    DB      ': file is R/O. Are you SURE [Y or N]? ',0
MSG7	DB	': directory.',CR,LF,0
MSG8    DB      ': write error.',CR,LF,0
MSG9    DB      ': read error.',CR,LF,0
MSG10   DB      ': write error (probably disk full).',CR,LF,0
MSG11   DB      ': rename failed.',CR,LF,0
MSG12   DB      ": can't create.",CR,LF,0
MSG13   DB      ": can't remove.",CR,LF,0
MSG14   DB      "Directories nested too deep.",CR,LF,0
; MSG15   DB      "Insufficient memory for buffers.",CR,LF,0
MSG16   DB      ": Illegal switch.",CR,LF,0
MSG17   DB      "Error: source and dest are identical.",CR,LF,0
MSG18   DB      "Too many arguments.",CR,LF,0
SWLIST	DB	'IRV',0		;LIST OF VALID SWITCHES

	.STACK
        DB      512 DUP (?)

;       UNITIALIZED VARIABLES AND BUFFER AREAS

	.DATA?
RBASE   DW 	1 DUP (?)               ;POINTER TO MVSTRUC IN RSTACK
LEVEL   DW      1 DUP (?)               ;RECURSION LEVEL
SRCDRIVE  DW    1 DUP (?)               ;SOURCE DRIVE (0 = DEFAULT)
DESTDRIVE  DW   1 DUP (?)               ;DEST DRIVE (0 = DEFAULT)
INHANDLE DW	1 DUP (?)               ;INPUT FILE HANDLE
OUTHANDLE DW	1 DUP (?)               ;OUTPUT FILE HANDLE
IFLAG   DW	1 DUP (?)               ;CONFIRM FLAG
RFLAG	DW	1 DUP (?)		;RECURSIVE FLAG
VFLAG   DW	1 DUP (?)               ;VERBOSE FLAG
CMDTAIL DB	128 DUP (?)             ;STORAGE FOR COMMAND TAIL
DEST_DTA DB     128 DUP (?)             ;HOLDS INFO ON DEST, RETURNED FROM DOS
                                        ;FN 4E.
NUMARGS DW      1 DUP (?)               ;NUMBER OF ARGUMENTS ON COMMAND LINE
ARGPTRS DW      MAXARGS DUP (?)         ;POINTERS TO START OF ARGUMENTS
BUFFER  DB      BUFSIZE DUP (?)         ;FILE XFER BUFFER
RSTACK  MVINFO  MAXLEVEL DUP (<>)	;RECURSION STACK

        PAGE +

	.CODE

EXTRN   CPYCNT:NEAR,UC:NEAR,UCSTR:NEAR,CRLF:NEAR
EXTRN   ERRORMSG:NEAR,COUT:NEAR,CIN:NEAR,CLRCO:NEAR
EXTRN   SKIPSP:NEAR,TYPTX:NEAR,ERRORMSG:NEAR,CLRCO:NEAR
EXTRN   CMDSRC:NEAR,FIXPATH:NEAR,TYPE_DIR:ABS,TYPE_AFN:ABS
EXTRN   TYPE_UFN:ABS,TYPE_DSP:ABS,TYPE_DRV:ABS,TYPE_UNK:ABS
EXTRN   GETYORN:NEAR,UNLINK2:NEAR

;**********************
; PROGRAM ENTRY POINT *
;**********************
ENTRY:
        TEST_DOS2                       ;TEST FOR DOS 2.0, EXIT IF DOS 1
	MOV	AX,DGROUP
	MOV	ES,AX
        MOV     BX,(80H)      		;POINT TO BYTE COUNT FOR COMMAND LINE
        CMP     [BX],BYTE PTR 0
        JE      USEMSG                  ;IF NO COMMAND TAIL
        PUSH    BX
        MOV     DL,[BX]			;LOAD BYTE COUNT IN DX
        MOV     DH,0
        ADD     BX,DX
        INC     BX
        MOV     [BX],BYTE PTR 0         ;PUT 0 BYTE AT END OF COMMAND LINE
        POP     BX
        INC     BX
        CALL    SKIPSP                  ;SKIP LEADING SPACES
        JNC     L_2                     ;IF SOMETHING ON LINE
USEMSG: ERROR   1
	MOV	AL,1			;SET ERROR CODE
        JMP     EXIT2
L_2:
        MOV     SI,BX
        MOV     DI,OFFSET ES:CMDTAIL
        MOV     CX,128
        CALL    CPYCNT                  ;SAVE COMMAND TAIL
        MOV     AX,ES
        MOV     DS,AX                   ;SET DATA SEG TO VARIABLE AREA
        MOV     BYTE PTR DS:IFLAG,CONFIRM  ;SET CONFIRM FLAG DEFAULT
        MOV     BYTE PTR DS:VFLAG,VERBOSE  ;SET VERBOSE FLAG DEFAULT
	MOV	BYTE PTR DS:RFLAG,RECURSE  ;SET RECURSIVE FLAG DEFAULT
	MOV	WORD PTR DS:LEVEL,0	;ZERO RECURSION LEVEL
        MOV     BYTE PTR DS:SRCDRIVE,0  ;SET SOURCE DRIVE DEFAULT
        MOV     BYTE PTR DS:DESTDRIVE,0 ;SET DEST DRIVE DEFAULT
;***************************
; COLLECT SWITCHES, IF ANY *
;***************************
        MOV     BX,OFFSET CMDTAIL       ;POINT TO COMMAND TAIL
        CALL    UCSTR                   ;MAKE UPPER-CASE
NEXTSW:
        CALL    SKIPSP
        JB      USEMSG                  ;IF NOTHING ON LINE BESIDES SWITCHES
        CMP     BYTE PTR [BX],'/'       ;SWITCH SPECIFIED?
        JE      GOTSW			;YES.
	CMP	BYTE PTR [BX],'-'	;CHECK FOR HYPHEN, TOO
	JNE	NOSWITCH
GOTSW:
        INC     BX                      ;SKIP OVER SWITCH CHARACTER
        CALL    SKIPSP                  ;SKIP LEADING BLANKS
        JB      USEMSG                  ;IF NOTHING
SWLOOP:
	PUSH	BX
	MOV	BX,OFFSET SWLIST
	CALL	CMDSRC			;SEARCH LIST OF VALID SWITCHES
	POP	BX
	MOV	AH,0
	ADD	AX,AX			;NOT FOUND IN LIST?
	JZ	BADSWITCH		;NO.
	MOV	SI,OFFSET IFLAG - 2
	ADD	SI,AX			;POINT TO FLAG
	NOT	BYTE PTR [SI]		;1'S COMPLEMENT
	INC	BX
	MOV	AL,BYTE PTR [BX]	;GET NEXT CHAR. FROM LINE
	CMP	AL,SPACE
	JE	NEXTSW			;IF SPACE, LOOK FOR - OR FILENAME
	CMP	AL,TAB
	JE	NEXTSW			;IF TAB, LOOK FOR - OR FILENAME
	JMP	SWLOOP			;ELSE ASSUME THIS IS A SWITCH CHAR.
BADSWITCH:
	MOV	AL,BYTE PTR [BX]
        CALL    COUT                    ;DISPLAY BAD CHAR.
        ERROR   16                      ;ILLEGAL SWITCH
	MOV	AL,16			;SET ERROR CODE
        JMP     EXIT2
;************************************************************
; SCAN THE COMMAND LINE AND STORE POINTERS TO ALL ARGUMENTS *
;************************************************************
NOSWITCH:
        MOV     CX,0                    ;CX COUNTS # OF ARGUMENTS
        MOV     SI,OFFSET DS:ARGPTRS    ;POINT TO ARG POINTER TABLE
SAVEARG:
        INC     CX                      ;BUMP ARGUMENT COUNT
        CMP     CX,MAXARGS
        JG      TOOMANY                 ;IF TOO MANY
        MOV     DS:[SI],BX              ;SAVE POINTER TO ARG
        ADD     SI,2                    ;ADVANCE TO NEXT TABLE ENTRY
CNTARGS:
        MOV     AL,DS:[BX]              ;GET CHAR. FROM COMMAND LINE
        CMP     AL,0
        JE      ENDLINE                 ;IF END OF LINE
        COMPLIST <SPACE,TAB>,NEXTARG    ;SEE IF SPACE OR TAB
        INC     BX
        JMP     CNTARGS                 ;LOOP TILL DELIMITER FOUND
NEXTARG:
        CALL    SKIPSP                  ;SKIP SPACES AND TABS
        JNB     SAVEARG                 ;IF NOT EOL, BACK TO TOP OF LOOP
ENDLINE:
        CMP     CX,0                    ;CHECK ARGUMENT COUNT
        JE      NOARGS                  ;IF NO ARGS
        CMP     CX,1
        JG      ARGSOK                  ;IF >=2 ARGS
        MOV     DS:[SI],BX              ;EXACTLY ONE ARGUMENT, SO
        INC     CX                      ;MAKE A DUMMY (NULL) 2ND ARGUMENT
        JMP     SHORT ARGSOK
NOARGS:
        JMP     USEMSG                  ;ELSE USE MESSAGE
TOOMANY:
        ERROR   18                      ;TOO MANY ARGUMENTS (PRETTY UNLIKELY)
	MOV	AL,18			;SET ERROR CODE
        JMP     EXIT2
;************************************
;* COLLECT THE DESTINATION PATHNAME *
;************************************
ARGSOK: MOV     WORD PTR NUMARGS,CX  	;SAVE NUMBER OF ARGUMENTS
        DEC     CX                      ;NUMBER OF ARGS - 1
        ADD     CX,CX                   ;TIMES TWO
        MOV     SI,OFFSET ARGPTRS
        ADD     SI,CX                   ;POINT TO ADDR. OF LAST ARGUMENT (DEST)
        MOV     BX,DS:[SI]              ;PUT ADDR. INTO BX
        MOV     DI,OFFSET RSTACK.ARG2  	;POINT TO STORAGE FOR DEST
        MOV     AL,[BX+1]
        CMP     AL,':'                  ;CHECK FOR DEST DRIVE SPEC
        JNE     GETLAST                 ;IF NONE
        MOV     AL,[BX]                 ;DRIVE SPECIFIED, GET DRIVE LETTER
        SUB     AL,'A'-1                ;MAKE BINARY
        MOV     BYTE PTR DESTDRIVE,AL   ;SAVE DRIVE
GETLAST: MOV    AL,[BX]                 ;GET A CHAR. FROM CMD LINE
        COMPLIST <0,SPACE,TAB>,ENDARG2  ;IF DELIMITER
        STOSB                           ;NOT DELIMITER, STORE IT
        INC     BX                      ;BUMP POINTER
        JMP     GETLAST                 ;LOOP
ENDARG2:
        MOV     AL,0
        STOSB                           ;END DEST W. 0
;************************************************************
;* LOOP, TAKING ONE SOURCE AT A TIME AND COPYING IT TO DEST *
;************************************************************
        MOV     CX,WORD PTR DS:NUMARGS  ;GET NUMBER OF ARGS AGAIN
        DEC     CX                      ;-1
        MOV     SI,OFFSET ARGPTRS    	;POINT TO POINTER TABLE
COPY1ARG:
        MOV     BX,DS:[SI]              ;GET POINTER TO ARG
        PUSH    SI
        PUSH    CX
        CALL    COPYARG                 ;COPY IT
        POP     CX
        POP     SI
        ADD     SI,2                    ;ADVANCE TO NEXT ARG POINTER
        LOOP    COPY1ARG                ;LOOP TILL NO MORE ARGS TO COPY
	MOV	AL,0			;ERROR CODE 0 MEANS OK
        JMP     EXIT2

;**********************************
;* COPY 1 SOURCE ARGUMENT TO DEST *
;**********************************
COPYARG PROC   NEAR
        MOV     BYTE PTR DS:SRCDRIVE,0  ;SET SOURCE DRIVE DEFAULT
        MOV     DI,OFFSET RSTACK.ARG1  	;COLLECT SOURCE
        MOV     AL,[BX+1]
        CMP     AL,':'                  ;CHECK FOR SOURCE DRIVE SPEC
        JNE     GETARG
        MOV     AL,[BX]                 ;GET DRIVE LETTER
        SUB     AL,'A'-1                ;MAKE BINARY
        MOV     BYTE PTR DS:SRCDRIVE,AL ;SAVE SOURCE DRIVE
GETARG: MOV     AL,[BX]                 ;COPY FROM [BX] TO [DI] UNTIL DELIMITER
        COMPLIST <0,SPACE,TAB>,ENDARG
        STOSB
        INC     BX
        JMP     GETARG
ENDARG: MOV     AL,0
        STOSB                           ;END ARG1 W. ZERO
        MOV     WORD PTR RBASE,OFFSET RSTACK   ;SAVE OFFSET TO RSTACK
        CALL    COPYIT                  ;DO THE STUFF
        RET
COPYARG ENDP

        PAGE +
;********************************************************************
; THIS IS THE COPY SUBROUTINE. IT ASSUMES THAT ARG1 AND ARG2 ARE
; SET UP PROPERLY IN THE RECURSION STACK AREA, AND THAT "RBASE" POINTS
; TO THE START OF THE DATA STRUCTURE FOR THIS RECURSION LEVEL.
;
COPYIT  PROC    NEAR
        MOV     BX,WORD PTR RBASE    	;GET BASE ADDR. (POINTER TO MVINFO)
        CALL    SETUP                   ;PARSE ARGUMENTS, CHECK FOR ERRORS
        JNC     NOARGERR                ;IF OK
        JMP     EXIT2                   ;QUIT IF ERROR
;******************************************
; SEE IF ANYTHING MATCHES THE SOURCE SPEC *
;******************************************
NOARGERR:
        ADDR    DX,SSPATH               ;POINT TO SEARCH PATH
        MOV     CX,31H                  ;SET SEARCH ATTRIBUTES
        MOV     AH,FIND_FIRST
        INT     DOS                     ;SEARCH FOR 1ST MATCH
        JNC     SRCOK                   ;OK IF SOMETHING FOUND
        ADDR    DX,ARG1
        CALL    ERRORMSG                ;ELSE SHOW ARGUMENT
        ERROR   2                       ;SAY IT DOESN'T EXIST
	MOV	AL,2			;SET ERROR CODE
        JMP     EXIT2			;EXIT 2 DOS
;**************************************
; CREATE DEST DIRECTORY, IF NECESSARY *
;**************************************
SRCOK:  CMP     WORD PTR [BX].NEWDIR,1  ;CHECK NEW DIRECTORY FLAG
        JNE     GOTONE                  ;IF NOT SET
        ADDR    DX,ARG2A                ;POINT TO DEST NAME
        MOV     AH,MKDIR
        INT     DOS                     ;CREATE NEW DIRECTORY
        JNB     GOTONE                  ;IF OK
        ADDR    DX,ARG2A                ;IF CAN'T CREATE,
        CALL    ERRORMSG                ;SHOW DIRECTORY NAME
        ERROR   12                      ;AND ERROR MSG.
	MOV	AL,12			;SET ERROR CODE
        JMP     EXIT2                   ;EXIT 2 DOS
;**************
; TOP OF LOOP *
;**************
GOTONE:
        CALL    BUILD_NAMES             ;MAKE UNAMBIG. SOURCE AND DEST NAMES
        JNC     NOSKIP
        JMP     NEXTFILE                ;IF SOURCE IS "." OR ".." OR = DEST
NOSKIP:
        CMP     BYTE PTR VFLAG,TRUE  	;CHECK VERBOSE FLAG
        JNE     QUIET                   ;IF QUIET MODE
        ADDR    DX,SRCNAME              ;NOT QUIET, SHOW VERBIAGE
        CALL    ERRORMSG
        CALL    TYPTX
        DB      ' -->',SPACE+200Q
        ADDR    DX,DESTNAME
        CALL    ERRORMSG
        CALL    CRLF
QUIET:
        TEST    [BX].DTA+21,BIT$DIR     ;IS SOURCE A DIRECTORY?
        JZ      COPYFILE                ;NO
	CMP	BYTE PTR DS:RFLAG,TRUE	;IS R FLAG SET?
	JNE	NEXTFILE		;NO, JUST SKIP OVER DIRECTORY
;***********************************************
; IF SOURCE IS A DIRECTORY, RECURSIVELY COPY IT*
;***********************************************
        CMP     WORD PTR LEVEL,MAXLEVEL-1 ;ARE WE AT MAX LEVEL?
        JL      NOTMAX                  ;NO
        ERROR   14                      ;YES, TOO DEEP
	MOV	AL,14			;SET ERROR CODE
        JMP     EXIT2
NOTMAX:
        COPYPATH SRCNAME,(ARG1+MVSTRUCSIZE)  ;COPY SOURCE DIRECTORY TO ARG1 AT NEXT LEVEL
        COPYPATH DESTNAME,(ARG2+MVSTRUCSIZE) ;COPY DEST NAME TO ARG2 AT NEXT LEVEL
        PUSH    BX                      ;SAVE POINTER TO BASE
        INC     WORD PTR LEVEL       	;INCREMENT RECURSION LEVEL
        ADD     WORD PTR RBASE,MVSTRUCSIZE ;ADVANCE BASE POINTER
        CALL    COPYIT                  ;COPY THE DIRECTORY
        POP     BX                      ;RESTORE BASE POINTER
        DEC     WORD PTR LEVEL          ;DECREMENT RECURSION LEVEL
        SUB     WORD PTR RBASE,MVSTRUCSIZE ;RESTORE BASE TO PREVIOUS LEVEL
        ADDR    DX,DTA
        MOV     AH,SET_DTA
        INT     DOS                     ;RESET DTA
        JMP     SHORT NEXTFILE          ;DO NEXT FILE
;*******************************
; IF SOURCE IS A FILE, COPY IT *
;*******************************
COPYFILE:
        CALL    COPY_FILE		;COPY A FILE
	JNB	NEXTFILE		;IF OK
	JMP	EXIT2			;IF ERROR
;*****************
; BOTTOM OF LOOP *
;*****************
NEXTFILE:
        MOV     AH,FIND_NEXT
        INT     DOS                     ;FIND NEXT MATCH, IF ANY
        JC      NOMORE                  ;IF NONE
        JMP     GOTONE                  ;GOT ONE, BACK TO TOP OF LOOP
;**********************
; NO MORE FILES, DONE *
; *********************
NOMORE:
JUSTCLOSE:
        CALL	CLOSE2                 ;CLOSE OUTPUT FILE
        CALL	CLOSE1                 ;CLOSE INPUT FILE
        RET                             ;ALL DONE
COPYIT  ENDP

        PAGE +
;*************************************************************************
; THIS PROCEDURE PARSES ARG1 AND ARG2 AND CHECKS FOR INCOMPATIBLE TYPES
; (E.G. "CP FOOBAR *.*").  IF SUCCESSFUL, IT SETS SSPATH = SOURCE SEARCH
; PATH, SPREFIX = SOURCE PREFIX, DSPATH = DEST SEARCH PATH (NOT USED),
; DPREFIX = DEST PREFIX, SRCTYPE = SOURCE TYPE, DESTTYPE = DESTINATION TYPE.
; IT ALSO SETS "NEWDIR"=1 IF THE DESTINATION DIRECTORY MUST BE CREATED.
; ALL OF THESE VARIABLES ARE IN A STRUCTURE OF TYPE 'MVINFO' IN THE RECURSION
; STACK AREA (STARTING AT THE ADDRESS IN 'RBASE').
;
; ON RETURN, THE CARRY FLAG IS SET IF AN ERROR OCCURRED, AND AL HOLDS AN
; ERROR CODE (<>0)
;
SETUP   PROC    NEAR
        MOV     BX,WORD PTR DS:RBASE    ;GET ADDR. OF DATA STRUCTURE
        MOV     WORD PTR [BX].NEWDIR,0  ;CLEAR "NEW DIRECTORY" FLAG
        ADDR    DX,DTA
        MOV     AH,SET_DTA
        INT     DOS                     ;SET DTA
        ADDR    SI,ARG1
        ADDR    DI,ARG2
        COMP_STRINGS                    ;COMPARE ARG1 AND ARG2
        JNE     NOTIDENT                ;IF NOT IDENTICAL
        ERROR   17                      ;ARG1 = ARG2, BAD NEWS
	MOV	AL,17			;SET ERROR CODE
        STC
        RET
NOTIDENT:
        ADDR    CX,SSPATH
        ADDR    DX,SPREFIX
        PUSH    BX
        ADD     BX,ARG1
        CALL    FIXPATH                 ;PARSE SOURCE PATHNAME
        POP     BX
        CMP     AX,TYPE_UNK
        JNE     GOODPATH                ;IF APPARENTLY OK
        ADDR    DX,ARG1
        CALL    ERRORMSG
        ERROR   2                       ;NONEXISTENT PATHNAME, COMPLAIN
	MOV	AL,2			;SET ERROR CODE
        STC
        RET
GOODPATH:
        MOV     WORD PTR [BX].SRCTYPE,AX     ;SAVE TYPE OF SOURCE
	CMP	AX,TYPE_DIR		;IS IT DIRECTORY?
	JNE	RSET			;J/NO.
	CMP	BYTE PTR DS:RFLAG,TRUE  ;YES IT IS, IS RFLAG SET?
	JE	RSET			;J/YES.
	ADDR	DX,ARG1			;R FLAG NOT SET,
	CALL	ERRORMSG		;SHOW NAME
	ERROR	7			;COMPLAIN ABOUT DIRECTORY
	MOV	AL,7			;SET ERROR CODE
	STC				;SIGNAL ERROR
	RET
RSET:
        ADDR    SI,ARG2			;GET POINTER TO DEST
PDEST:  PUSH    SI			;SAVE IT ON STACK
	MOV	DI, SI			;AND IN DI
SRCBS:  MOV     AX,[SI]                 ;FIND END OF DEST
        COMPLIST <0,SPACE,TAB>,GOTEND
        INC     SI
        JMP     SRCBS
GOTEND:
        DEC     SI
        CMP     [SI],BYTE PTR '\'       ;DOES DEST SPEC END W. BACKSLASH?
        JNE     NOBS                    ;J/NO, IT DOESN'T
	CMP	SI, DI			;IS DEST="\"?
	JE	NOBS			;J/YES, ROOT DIRECTORY, LEAVE IT ALONE
	CMP	BYTE PTR [SI-1],':'	;IS PREVIOUS CHAR ':'?
	JE	NOBS			;J/YES, ROOT DIRECTORY, LEAVE IT ALONE
        MOV     [SI],BYTE PTR 0         ;\ AT END AND NOT ROOT DIR, REMOVE IT
NOBS:   COPYPATH ARG2,ARG2A   		;MAKE ARG2A = ARG2
        POP     SI                      ;POINT TO START OF ARG2 AGAIN
        ADDR    CX,DSPATH
        ADDR    DX,DPREFIX
        PUSH    BX
        MOV     BX,SI
        CALL    FIXPATH                 ;PARSE DEST PATHNAME
        POP     BX
        MOV     WORD PTR [BX].DESTTYPE,AX       ;SAVE DEST TYPE
        CMP     AL,TYPE_AFN
        JE      BADDEST                 ;DEST CAN'T BE AFN
        CMP     AL,TYPE_UFN
        JNE     NOTUFN                  ;IF DEST TYPE IS UFN,
        CMP     WORD PTR DS:NUMARGS,2   ;BETTER HAVE ONLY 1 SOURCE
        JG      BADDEST
        CMP     WORD PTR DS:SRCTYPE,TYPE_AFN  ;AND SOURCE TYPE BETTER NOT BE AFN
        JNE     NOTUFN
BADDEST:
        ERROR   3
	MOV	AL,3			;SET ERROR CODE
        STC
        RET
NOTUFN:
        COMPLIST <TYPE_DSP,TYPE_DRV,TYPE_DIR>,DESTDSP ;IF DEST IS DIR OR DRIVE
        CMP     AL,TYPE_UNK
        JNE     SHORT DESTOK            ;IF DEST EXISTS
DESTNX:
        MOV     AX,WORD PTR [BX].SRCTYPE  ;GET SOURCE TYPE
        CMP     AL,TYPE_DIR             ;IF SOURCE TYPE IS DIRECTORY,
        JE      DESTDIR			;MAKE DEST A DIRECTORY, TOO
	CMP	WORD PTR [BX].SRCTYPE,TYPE_AFN ;SOURCE NOT A DIRECTORY
	JE	BADDEST			;AFN IS BAD (E.G. CP *.* FOO)
	JMP	SHORT DESTOK		;OTHERWISE OK
DESTDIR:
        CALL    SETUP_DEST_DIR          ;MAKE DEST A DIRECTORY, TOO
        JMP     SHORT DESTOK
DESTDSP: MOV    AX,WORD PTR [BX].SRCTYPE  ;DEST IS DIRECTORY, DIR SPEC OR DRIVE
        CMP     AL,TYPE_DIR             ;IS SOURCE A DIRECTORY? -
        JNE     DESTOK                  ;- NO
        CALL    SETUP_SRC_DIR           ;- YES, SOURCE IS DIRECTORY, SET IT UP
        JB      ERRRET                  ;IF ERROR
        MOV     AX,WORD PTR [BX].DESTTYPE
        CMP     AX,TYPE_DIR             ;IS DEST A DIRECTORY? -
        JNE     DESTOK                  ;- NO
        CALL    SETUP_DEST_DIR          ;- YES, SET IT UP, TOO
;*******************************
; CHECK FOR INCOMPATIBLE TYPES *
;*******************************
DESTOK:
        MOV     CX,WORD PTR [BX].DESTTYPE    ;GET DEST TYPE
        MOV     AX,WORD PTR [BX].SRCTYPE     ;GET SOURCE TYPE
        CMP     AX,TYPE_DIR             ;IS SOURCE A DIRECTORY? -
        JNE     TYPEOK                  ;- NO, IT ISN'T
        CMP     CX,TYPE_DIR             ;- YES, SOURCE IS DIRECTORY,
        JE      TYPEOK                  ;DEST BETTER BE DIRECTORY
        CMP     CX,TYPE_DRV             ;OR DRIVE SPEC
        JE      TYPEOK
ERRRET:
        ERROR   3                       ;ELSE INCOMPATIBLE TYPE ERROR
	MOV	AL,3			;SET ERROR CODE
	STC                             ;ERROR RETURN
        RET
TYPEOK: CLC                             ;NORMAL RETURN
        RET
SETUP   ENDP

;**********************************************
; COPY IS FROM DIR TO DIR, DIR SPEC OR DRIVE
; BUILD FULL DEST PATHNAME (IN ARG2A) AND CHECK
; ITS TYPE.  (SETS CARRY ON ERROR)
;
SETUP_SRC_DIR PROC NEAR
        COPYPATH ARG2,DSPATH  		;COPY DEST DIRECTORY OR DRIVE SPEC TO
                                        ;DEST PATH (USE AS SCRATCH AREA)
        DEC     DI                      ;BACK UP OVER NULL
        MOV     AX,WORD PTR [BX].DESTTYPE
        CMP     AX,TYPE_DIR             ;SEE IF DEST IS DIRECTORY -
        JNE     NOTDIR                  ;- NO
        MOV     BYTE PTR [DI],'\'       ;- YES, ADD BACKSLASH TO DEST NAME
        INC     DI
        DEC     CX			;CX IS # CHARS REMAINING OUT OF 64
NOTDIR: ADDR    SI,ARG1                 ;POINT TO SOURCE DIRECTORY NAME
        CMP     BYTE PTR [SI+1],':'     ;DRIVE SPECIFIED? -
        JNE     NODRV                   ;- NO IT WASN'T
        ADD     SI,2                    ;- YES, IT WAS, SKIP IT
NODRV:
;	skip to the last element in the source path
	MOV	DX, SI			;SAVE START OF SOURCE IN DX
STRIP_PATH:
	CMP	BYTE PTR [SI],0		;END OF SOURCE?
	JE	END_OF_SRC		;J/YES
	CMP	BYTE PTR [SI],'\'	;BACKSLASH IN SOURCE?
	JNE	NEXT_CHAR		;J/NO, KEEP SEARCHING
	MOV	DX, SI			;SAVE LOCATION OF BACKSLASH
NEXT_CHAR:
	INC	SI
	JMP	STRIP_PATH
END_OF_SRC:
	MOV	SI, DX			;POINT TO LAST \ OR START OF STRING
	CMP	BYTE PTR [SI],'\'	;IS THERE A \?
	JNE	NOBKSL			;NO
	INC	SI			;YES, SKIP PAST IT
NOBKSL:
        CALL    CPYCNT                  ;COPY LAST PART OF SOURCE DIRECTORY
					;NAME TO DEST PATH
        COPYPATH DSPATH,ARG2A 		;COPY AUGMENTED DEST PATH TO ARG2A
        ADDR    CX,DSPATH
        ADDR    DX,DPREFIX
        PUSH    BX
        ADD     BX,ARG2A
        CALL    FIXPATH                 ;PARSE FULL DEST PATHNAME
        POP     BX
        MOV     WORD PTR [BX].DESTTYPE,AX    ;SAVE DEST TYPE
        CMP     AL,TYPE_UNK
        JNE     DESTXST                 ;IF DEST EXISTS
        MOV     AX,WORD PTR [BX].SRCTYPE  ;DEST DOESN'T EXIST, GET SOURCE TYPE
        CMP     AX,TYPE_DIR             ;IF SOURCE IS A DIRECTORY,
        JNE     DESTXST
        MOV     [BX].DESTTYPE,TYPE_DIR  ;MAKE DEST A DIRECTORY, TOO
DESTXST:
        CLC
        RET
SETUP_SRC_DIR ENDP

;***************************************************************
; THIS PROCEDURE IS CALLED IF THE DESTINATION IS A NEW DIRECTORY
;
SETUP_DEST_DIR PROC NEAR
        ADDR    SI,DSPATH
        ADDR    DI,DPREFIX
        PUSH    DI
        MOV     CX,PATHSIZE
        CALL    CPYCNT                  ;COPY SEARCH PATH TO PREFIX
        POP     DI
        MOV     CX,PATHSIZE
        MOV     AL,0
        REPNE   SCASB                   ;FIND END OF DEST PREFIX
        DEC     DI
        MOV     [DI],BYTE PTR '\'       ;ADD BACKSLASH
        MOV     [DI+1],BYTE PTR 0
        MOV     [BX].NEWDIR,WORD PTR 1  ;SET "NEW DIRECTORY FLAG"
        MOV     WORD PTR [BX].DESTTYPE,TYPE_DIR ;SET DEST TYPE = DIRECTORY
        RET
SETUP_DEST_DIR ENDP

        PAGE +
;****************************************************************************
; THIS ROUTINE TAKES THE INFO ON A FILE IN THE DTA AND USES IT TO GENERATE
; FULL, UNAMBIGUOUS SOURCE AND DESTINATION FILE NAMES BY ADDING THE NECESSARY
; DIRECTORY PREFIXES TO IT.  IT SETS THE CARRY FLAG IF THE "FILE"
; IS "." OR "..", OR IF THE SOURCE IS IDENTICAL TO THE DESTINATION.
;
BUILD_NAMES  PROC  NEAR
        COPYPATH SPREFIX,SRCNAME	;GET SOURCE PREFIX,
					;COPY TO SOURCE NAME
        DEC     DI                      ;BACK UP OVER NULL
        ADDR    SI,(DTA+30)             ;POINT TO FILE NAME WE FOUND
        CMP     [SI],BYTE PTR '.'       ;DOES IT START WITH .? -
        JNE     NOTDOT                  ;- NO
        STC                             ;- YES, SET CARRY TO SKIP IT
        RET
NOTDOT:
        CALL    CPYCNT                  ;ADD IT TO SOURCE PREFIX
        CMP     WORD PTR [BX].DESTTYPE,TYPE_UFN  ;IS DEST A UFN?
        JE      DESTUFN                 ;YES, JUST USE IT
        CMP     WORD PTR [BX].DESTTYPE,TYPE_UNK
        JE      DESTUFN                 ;DITTO IF DEST DOESN'T EXIST
                                        ;IF NOT, MAKE DEST NAME = SOURCE NAME
        COPYPATH DPREFIX,DESTNAME       ;COPY DEST PREFIX TO DEST NAME
        DEC     DI                      ;BACK UP OVER NULL
        ADDR    SI,DTA+30               ;POINT TO FILE NAME WE FOUND
        CALL    CPYCNT                  ;ADD IT TO DEST PREFIX
        JMP     SHORT DONE              ;GO TO NEXT STEP
DESTUFN:                                ;DEST IS A FILE, NOT A DIRECTORY
        COPYPATH ARG2,DESTNAME          ;SO DESTNAME JUST = ARG2
DONE:
        CALL    CHECK_NAMES             ;CHECK SOURCE AND DEST FOR IDENTITY
        RET
BUILD_NAMES ENDP

        PAGE +
;*********************************************************************
; THIS PROCEDURE CHECKS THE SOURCE AND DEST FILES, AND SETS THE CARRY
; FLAG IF THEY ARE IDENTICAL
;
CHECK_NAMES PROC NEAR
        MOV     BX,WORD PTR DS:RBASE    ;GET ADDR. OF DATA STRUCTURE
        MOV     AH,SET_DTA
        MOV     DX,OFFSET DEST_DTA
        INT     DOS                     ;SET DTA
        ADDR    DX,ARG2                 ;POINT TO DESTINATION
        MOV     CX,0FFH
        MOV     AH,FIND_FIRST
        INT     DOS                     ;SEARCH FOR IT
        PUSHF
        ADDR    DX,DTA
        MOV     AH,SET_DTA
        INT     DOS                     ;RESET DTA
        POPF
        JNB     GOTDEST                 ;IF FOUND IT
        CLC                             ;OK IF IT DOESN'T EXIST
        RET
GOTDEST: ADDR   SI,DTA+21               ;POINT TO INFO ON SOURCE
        MOV     DI,OFFSET DEST_DTA+21  	;POINT TO INFO ON DEST
        MOV     CX,9                    ;SIZE OF FILE ATTRIBUTES
        REPE    CMPSB                   ;COMPARE SOURCE AND DEST
        JNE     NOTEQU                  ;IF NOT =
        COMP_STRINGS                    ;COMPARE STRINGS
        JNE     NOTEQU                  ;IF SOURCE AND DEST NAMES NOT EQUAL
        STC                             ;FILES ARE EQUAL (NAMES AND ATTRIB.)
        RET
NOTEQU: CLC
        RET
CHECK_NAMES ENDP
        PAGE +
;**************************************************************************
; THIS ROUTINE COPIES A SINGLE FILE (SRCNAME) TO ITS DESTINATION (DESTNAME)
;
; IT SETS THE CARRY FLAG ON ERROR, AND RETURNS AN ERROR CODE IN AL.
;
COPY_FILE PROC  NEAR
        ADDR    DX,SRCNAME              ;DX = SOURCE FILE NAME
        MOV     AL,READ_ACCESS
        MOV     AH,DOS2_OPEN
        INT     DOS                     ;TRY TO OPEN SOURCE FILE
        JC      FAIL                    ;IF CAN'T
        MOV     WORD PTR DS:INHANDLE,AX    ;SAVE FILE HANDLE
        CALL    GETOK                   ;GET CONFIRMATION IF DEST EXISTS
        JNC     OKGIVEN                 ;IF OK TO DELETE
        MOV     BX,WORD PTR DS:INHANDLE ;IF NOT OK,
        MOV     AH,DOS2_CLOSE
        INT     DOS                     ;CLOSE INPUT FILE
	CLC
        RET                             ;QUIT W/O COPYING
FAIL:   ADDR    DX,SRCNAME              ;POINT TO SOURCE FILE NAME
        CALL    ERRORMSG                ;SHOW IT
        ERROR   5                       ;COMPLAIN THAT CAN'T OPEN
	MOV	AL,5			;SET ERROR CODE
	STC
        RET				;ERROR RETURN
OKGIVEN: ADDR   DX,DESTNAME
        MOV     CL,BYTE PTR [BX].DTA+21  ;MAKE DEST ATTRIBUTES=SOURCE ATTRIB.
        MOV     CH,0
        MOV     AH,DOS2_CREATE
        INT     DOS                     ;CREATE DEST FILE (OR TRUNCATE OLD ONE)
        JNC     CREATED                 ;IF OK
        ADDR    DX,DESTNAME
        CALL    ERRORMSG                ;CREATE FAILED, SHOW FILE NAME
        ERROR   5                       ;COMPLAIN THAT CAN'T OPEN
	CALL	CLOSE1			;CLOSE INPUT FILE
	MOV	AL,5			;SET ERROR CODE
	STC				;SIGNAL ERROR
	RET
CREATED:
        MOV     WORD PTR DS:OUTHANDLE,AX   ;SAVE OUTPUT HANDLE
COPYLOOP:
        PUSH    BX
        MOV     BX,WORD PTR DS:INHANDLE ;BX = FILE HANDLE
        MOV     DX,OFFSET BUFFER        ;DX = BUFFER OFFSET
        MOV     CX,BUFSIZE              ;CX = BUFFER SIZE
        MOV     AH,READ                 ;USING DOS 2 CALL,
        INT     DOS                     ;READ FROM SOURCE
        POP     BX
        JC      RDERR                   ;IF ERROR
        MOV     CX,AX                   ;SAVE AMT. READ
        CMP     AX,0
        JE      ENDCOPY                 ;IF 0
        PUSH    BX
        MOV     BX,WORD PTR DS:OUTHANDLE ;BX = FILE HANDLE
        MOV     DX,OFFSET BUFFER        ;DX = BUFFER OFFSET
        MOV     AH,WRITE
        INT     DOS                     ;WRITE BUFFER TO DEST
        POP     BX
        JC      WRERR                   ;IF ERROR
        CMP     AX,CX                   ;WERE ALL BYTES WRITTEN?
        JE      COPYLOOP                ;YES, KEEP GOING
	ADDR	DX,DESTNAME		;ERROR, POINT TO DEST NAME
	CALL	ERRORMSG		;SHOW IT
        ERROR   10                      ;PROBABLY DISK FULL
	MOV	AL,10
        JMP     SHORT SCRATCH		;ABORT
WRERR:  ADDR	DX,DESTNAME		;POINT TO DEST NAME
	CALL	ERRORMSG		;SHOW IT
	ERROR   8                       ;WRITE ERROR
	MOV	AL,8
        JMP     SHORT SCRATCH		;ABORT
RDERR:  ADDR	DX,SRCNAME		;POINT TO SOURCE NAME
	CALL	ERRORMSG		;SHOW IT
	ERROR   9                       ;READ ERROR
	MOV	AL,9
        JMP     SHORT SCRATCH		;ABORT
ENDCOPY: PUSH   BX                      ;NO ERRORS
        MOV     BX,WORD PTR DS:INHANDLE
        MOV     AH,FILE_TIMES
        MOV     AL,0                    ;GET FILE DATE AND TIME FOR SOURCE
        INT     DOS
        MOV     BX,WORD PTR DS:OUTHANDLE   ;GET OUTPUT FILE HANDLE
        MOV     AH,FILE_TIMES
        MOV     AL,1
        INT     DOS                     ;SET OUTPUT DATE AND TIME SAME AS INPUT
        MOV     BX,WORD PTR DS:INHANDLE
        MOV     AH,DOS2_CLOSE
        INT     DOS                     ;CLOSE INPUT FILE
        MOV     BX,WORD PTR DS:OUTHANDLE
        MOV     AH,DOS2_CLOSE
        INT     DOS                     ;CLOSE OUTPUT FILE
        POP     BX
        RET
SCRATCH:
	PUSH	AX			;SAVE ERROR CODE
	ADDR	DX,DESTNAME		;POINT TO DEST FILE
	CALL	UNLINK2			;REMOVE IT
	CALL	CLOSE1			;CLOSE INPUT FILE
	POP	AX
	STC				;SIGNAL ERROR
	RET
COPY_FILE ENDP

        PAGE +
;*******************************************************************
;       THIS ROUTINE CHECKS TO SEE IF THE DEST FILE EXISTS.
;       IF IT DOES, AND IF THE CONFIRM FLAG IS SET, IT ASKS
;       FOR CONFIRMATION BEFORE DELETING THE FILE.  IF THE
;       CONFIRM FLAG IS NOT SET, IT JUST DELETES IT.
;       ON EXIT, 'C'=1 IF THE USER REPLIED 'N' TO THE CONFIRM MSG.
;
GETOK   PROC    NEAR
        ADDR    DX,DESTNAME
        MOV     AL,0
        MOV     AH,CHMOD
        INT     DOS                     ;GET ATTRIBUTES FOR DEST
        JNC     EXISTS                  ;IF FILE EXISTS
        CLC
        RET
EXISTS:
        CMP     BYTE PTR DS:IFLAG,TRUE  ;IS FLAG SET TO CONFIRM?
        JNE     COK                     ;NO.
        ADDR    DX,DESTNAME
        CALL    ERRORMSG                ;SHOW DEST FILE
        ERROR   4                       ;ASK FOR CONFIRMATION
	CALL	GETYORN			;GET Y OR N RESPONSE
        CMP     AL,'Y'                  ;IS IT YES?
        JNE     ABEXIT                  ;NO, SIGNAL CALLING ROUTINE
COK:
TRYAGAIN:
        ADDR    DX,DESTNAME
        MOV     AH,UNLINK
        INT     DOS                     ;DELETE THE FILE
        JNC     OKEXIT                  ;IF NO PROBLEM
        CMP     AX,5                    ;WAS FILE R/O? (THIS OUGHT TO BE
                                        ; THE ONLY POSSIBLE ERROR)
        JNE     ABEXIT                  ;EXIT IF SOME OTHER ERROR
	ADDR	DX,DESTNAME
	CALL	ERRORMSG		;SHOW DEST FILE NAME
        ERROR   6                       ;R/O FILE, QUERY USER AGAIN
	CALL	GETYORN			;GET Y OR N RESPONSE
        CMP     AL,'Y'                  ;TEST FOR 'Y' RESPONSE
        JNE     ABEXIT                  ;ABORT IF USER SAYS 'N'
        MOV     CX,0                    ;'Y' TYPED CLEAR ALL ATTRIBUTES
        ADDR    DX,DESTNAME             ;POINT TO FILE NAME
        MOV     AH,CHMOD
        MOV     AL,1                    ;CHANGE ATTRIBUTES
        INT     DOS                     ;TRY MAKING IT R/W
        JMP     TRYAGAIN                ;TRY TO ERASE IT AGAIN
ABEXIT: STC
        RET
OKEXIT: CLC
        RET
GETOK   ENDP

; PRTERR = PRINT ERROR MESSAGE (DX POINTS TO OFFSET FROM DGROUP)

PRTERR:
        PUSH    DS
        MOV     AX,DGROUP
        MOV     DS,AX
        CALL    ERRORMSG
        POP     DS
	RET

CLOSE2:  MOV    BX,WORD PTR OUTHANDLE
        MOV     AH,DOS2_CLOSE
        INT     DOS                     ;CLOSE OUTPUT FILE
	RET
CLOSE1:
        MOV     BX,WORD PTR INHANDLE
        MOV     AH,DOS2_CLOSE
        INT     DOS                     ;CLOSE INPUT FILE
	RET
EXIT2:
        MOV     AH,EXIT
        INT     DOS                     ;EXIT TO DOS

        END     ENTRY

HEAD.ASM

        TITLE HEAD - A FILTER FOR MSDOS2
        PAGE 55,132
;******************************************************************************
;       THIS PROGRAM PRINTS THE FIRST FEW LINES OF A TEXT FILE.
;
;       IT READS FROM A FILE IF SPECIFIED, OR FROM THE STANDARD INPUT OTHERWISE
;       IT WRITES TO THE STANDARD OUTPUT.
;
;       USAGE:  HEAD -n file1 file2 ...
;               -n is optional, limits output to n lines (20 default)
;		file names can be ambiguous
;
;
;	VERSION 1.8, 02-OCT-87: assembles under MASM 5.0
;	VERSION 1.7, 21-DEC-86: now supports wildcards, multiple files
;	VERSION 1.6, 06-SEP-86: minor cleanup, fixed bug involving failure
;				to set data segment when reading from stdin.
;	VERSION 1.5, 13-AUG-86: handles arbitrary -n (up to 65535)
;				also now buffers output, doesn't expand tabs
;	VERSION 1.4, 06-JUL-86: changes to memory allocation, now assembles
;				under MASM 4.0
;       VERSION 1,3, 04-FEB-86: fixed bug in reading from std. input
;       VERSION 1.2, 16-JAN-86: makes UTIL and CONSOLE external modules
;       VERSION 1.1, 27-SEP-85: defaults to std. input if no file given
;       VERSION 1.0, 19-SEP-85
;
;       BY: JON DART
;           3012 HAWTHORN ST.,
;           SAN DIEGO, CA 92104
;
;   	TO BUILD HEAD EXE:
;
;	MASM HEAD,HEAD,NUL,NUL
;	LINK HEAD,HEAD,NUL,ASM

INBUFSIZE EQU   8192                    ;SIZE OF INPUT BUFFER
OUTBUFSIZE EQU	512			;SIZE OF OUTPUT BUFFER
MAXARGS EQU	40			;MAX. # CMD. LINE ARGUMENTS
PRGSIZE EQU	1000H			;MAX. SIZE OF CODE + FIXED DATA
M       EQU     BYTE PTR 0[BX]

        .XLIST
        INCLUDE ASCII.DEF
        INCLUDE MSDOS2.DEF
	INCLUDE MACROS.DEF
        .LIST

	DOSSEG
	.MODEL	SMALL

;       MEMORY DEFINITIONS:
;
	.DATA
INHANDLE DW     0                       ;INPUT FILE HANDLE
                                        ;(DEFAULTS TO STD. INPUT)
NUMARGS DW	1			;NUMBER OF COMMAND LINE ARGUMENTS
OUTNDX	DW	0			;OFFSET TO NEXT SLOT IN OUTPUT BUFFER
NUMLIN  DW      20                      ;NUMBER OF LINES TO LIST
ONEFILE	DB	1			;SET 1 IF ONE UNAMBIG. ARGUMENT
MSG1    DB      CR,LF,"head: can't open ",0
MSG1E   DB      CR,LF,0
MSG2    DB      CR,LF,"head: read error on file ",0
MSG4	DB	CR,LF,"head usage: head -n filename",CR,LF,0
MSG5	DB	CR,LF,"head: too many command line arguments",CR,LF,0
MSG6	DB	CR,LF,"head: unknown pathname ",0

;	UNINITIALIZED DATA SPACE:

	.DATA?
INBUF   DB      INBUFSIZE DUP (?)       ;INPUT BUFFER
OUTBUF	DB      OUTBUFSIZE DUP (?)	;OUTPUT BUFFER
ARGPTRS DB	(2*MAXARGS) DUP (?)	;POINTERS TO ARGUMENTS
ARGBUF  DB	512 DUP (?) 		;ARGUMENT BUFFER
SPATH	DB	65 DUP (?)		;SEARCH PATH
PREFIX	DB      65 DUP (?)		;SEARCH PREFIX
FILENAME DB	65 DUP (?)		;COMPOSITE FILE NAME (PREFIX + NAME)
DTA	DB	128 DUP (?)		;DTA  AREA
MAXMEM	EQU	DTA + 128		;MAX. MEMORY USED

	.STACK
        DB 512 DUP (?)

	.CODE
        EXTRN   COUT:NEAR,ERRORMSG:NEAR
        EXTRN   SKIPSP:NEAR,DTOBIN:NEAR,CPYCNT:NEAR,TYPTX:NEAR
	EXTRN	FIXPATH:NEAR,GETARGS:NEAR
	EXTRN	TYPE_UNK:ABS,TYPE_UFN:ABS

ENTRY:
        TEST_DOS2                       ;TEST FOR DOS 2.0, EXIT IF DOS 1
MEMOK:
	MOV     AX,DGROUP
	MOV	ES,AX			;SET EXTRA SEG TO POINT TO BUFFERS
        MOV     BX,80H		        ;GET BYTE COUNT FOR COMMAND LINE
        CMP     BYTE PTR [BX],0         ;TEST IT
        JNE     L_1                     ;IF NOT 0
        JMP     START			;NO ARGUMENTS, USE STDIN
L_1:
        PUSH    BX
        MOV     DL,BYTE PTR [BX]
        MOV     DH,0
        ADD     BX,DX
        INC     BX
        MOV     BYTE PTR [BX],0         ;PUT 0 BYTE AT END OF COMMAND LINE
        POP     BX
        INC     BX			;POINT TO START OF COMMAND LINE
	MOV	SI,OFFSET ARGPTRS
	MOV	DI,OFFSET ARGBUF
	MOV	CX,MAXARGS
	CALL	GETARGS			;COLLECT COMMAND LINE ARGUMENTS
	JNB	L_2			;IF OK
        MOV     AX,DGROUP
        MOV     DS,AX
        MOV     DX,OFFSET MSG5
        CALL    ERRORMSG                ;TOO MANY ARGUMENTS
        JMP     EXIT2
L_2:
	MOV	AX,DGROUP
	MOV	DS,AX			;SET DATA SEG TO FIXED DATA
	MOV	WORD PTR NUMARGS,CX	;SAVE # OF ARGUMENTS
	CMP	CX,0
	JNE	L_2A			;IF SOME ARGUMENT
	JMP	START			;NO ARGUMENTS, USE STDIN
L_2A:
	MOV	SI,OFFSET ARGPTRS
	MOV	BX,WORD PTR [SI]	;POINT TO 1ST ARGUMENT
	MOV	AL,BYTE PTR [BX]	;GET 1ST CHAR.
        CMP     AL,'-'
        JE      L_3                     ;IF SWITCH SPECIFIED
	MOV	SI,OFFSET ARGPTRS
	MOV	CX,WORD PTR NUMARGS	;GET # OF ARGUMENTS
        JMP     GETFILES
L_3:
        INC     BX
        MOV     CL,0
        PUSH    BX
CD:     MOV     AL,BYTE PTR [BX]     	;HAVE NUMBER, COUNT NUMBER OF DIGITS
        CMP     AL,'0'
        JC      NODGT
        CMP     AL,'9'+1
        JNC     NODGT
        INC     BX
        INC     CL
        JMP     SHORT CD
NODGT:
        POP     BX
        MOV     CH,0                    ;CL HOLDS DIGIT COUNT
        CALL    DTOBIN                  ;CONVERT NUMBER TO BINARY
        JNC     L_4                     ;IF OK
	JMP	SHORT USE
L_4:
        CMP     AX,0                    ;IS NUMBER 0?
        JNE     NOT0                    ;NO.
        JMP     EXIT2                   ;YES, IT IS, JUST EXIT
NOT0:
        MOV     WORD PTR NUMLIN,AX      ;STORE # LINES
	MOV	SI,OFFSET ARGPTRS+2
	MOV	CX,WORD PTR NUMARGS	;GET # OF ARGUMENTS
	DEC	CX			;-1 CAUSE OF SWITCH
	JMP	SHORT GETFILES
USE:
        MOV     DX,OFFSET MSG4
        CALL    ERRORMSG                ;TELL USER HOW TO USE
        JMP     EXIT2
START:
	CALL	HEAD
	JMP	EXIT2
GETFILES:
	MOV	AX,DGROUP
	MOV	DS,AX
	CMP	CX,0			;NO FILE ARGUMENTS?
	JNE	GOT1
	JMP	START			;NO, JUST USE STDIN
GOT1:
	CMP	CX,1			;>1 ARG?
	JE	ONLY1			;NOPE
	MOV	BYTE PTR ONEFILE,0	;>1 ARG
ONLY1:
ARGLUP:	MOV	BX,WORD PTR [SI]	;BX POINTS TO FILE NAME
	PUSH	CX
	PUSH	SI
	CALL	DOARG
	POP	SI
	POP	CX
SKIPSW:
	ADD	SI,2
	LOOP	ARGLUP
	JMP	EXIT2

;	DOARG = PROCESS 1 ARGUMENT
;	ES:BX POINTS TO IT

DOARG	PROC 	NEAR
NONUM:
	MOV	AH,SET_DTA
	MOV	DX,OFFSET DTA
	INT	DOS			;SET DTA
        MOV	CX,OFFSET SPATH
        MOV	DX,OFFSET PREFIX
        PUSH    BX
        CALL    FIXPATH                 ;PARSE PATHNAME
	POP	BX
	CMP	AL,TYPE_UNK
	JE	BADNAME
	CMP	AL,TYPE_UFN
	JE	GOTNAME			;IF UNAMBIG. FILE NAME
	MOV	BYTE PTR ONEFILE,0	;AMBIG. FILE NAME OR DIR, FLAG IT
	JMP	GOTNAME
BADNAME:
	MOV	DX,OFFSET MSG6
	CALL	ERRORMSG		;BAD FILE NAME, SHOW MSG.
	MOV	DX,BX
	CALL	ERRORMSG		;SHOW FILE THAT CAUSED IT
	RET
GOTNAME:
        MOV     DX,OFFSET SPATH        	;POINT TO SEARCH PATH
        MOV     CX,31H                  ;SET SEARCH ATTRIBUTES
        MOV     AH,FIND_FIRST
        INT     DOS                     ;SEARCH FOR 1ST MATCH
	JB	BADNAME			;IF NOTHING FOUND
GOTFILE:
	MOV	SI,OFFSET PREFIX
	MOV	DI,OFFSET FILENAME
	MOV	CX,65
	CALL	CPYCNT			;COPY PREFIX TO FILE NAME AREA
        DEC     DI                      ;BACK UP OVER NULL
        MOV     SI,(OFFSET DTA)+30     	;POINT TO FILE NAME WE FOUND
        CMP     [SI],BYTE PTR '.'       ;DOES IT START WITH .? -
        JNE     NOTDOT                  ;- NO
	JMP     SHORT SKIPFILE 		;YES, JUST SKIP IT
NOTDOT:
	CALL	CPYCNT			;COPY FILE WE FOUND AFTER PREFIX
	CALL	DOFILE			;DO 1 FILE
SKIPFILE:
        MOV     AH,FIND_NEXT
        INT     DOS                     ;FIND NEXT MATCH, IF ANY
        JC      NOMORE                  ;IF NONE
        JMP     GOTFILE                 ;GOT ONE, BACK TO TOP OF LOOP
NOMORE:
	RET
DOARG	ENDP

;	DOFILE = PROCESS 1 FILE (NAME IN ES:FILENAME)

DOFILE  PROC	NEAR
        MOV     DX,OFFSET FILENAME     	;DX POINTS TO FILE NAME
        MOV     AL,READ_ACCESS
        MOV     AH,DOS2_OPEN
        INT     DOS                     ;TRY TO OPEN FILE
        JNC     NAMEOK                  ;IF OK
        JMP     SHORT BADOPN
NAMEOK:
        MOV     WORD PTR INHANDLE,AX    ;SAVE FILE HANDLE
	CMP	BYTE PTR ONEFILE,1
	JE	NOSHOW
	CALL	SHOWFILE		;SHOW FILE NAME (ONLY IF >1 SPECIFIED)
NOSHOW:
	CALL	HEAD			;SHOW HEAD OF FILE
	RET
BADOPN:
        MOV     DX,OFFSET MSG1
        CALL    ERRORMSG                ;BAD OPEN, SHOW MSG.
        MOV     DX,OFFSET FILENAME     	;DX POINTS TO FILE NAME
        CALL    ERRORMSG                ;SHOW IT
	RET
DOFILE	ENDP

;	SHOW FILE NAME

SHOWFILE PROC	NEAR
	MOV	AX,"="
	MOV	CX,5
BARS:
	CALL	WRITEBYTE
	LOOP	BARS
	MOV	AX,SPACE
	CALL	WRITEBYTE
	MOV	BX,OFFSET FILENAME
SHOWF:	MOV	AL,BYTE PTR [BX]
	CMP	AL,0
	JE	ENDF
	CALL	WRITEBYTE
	INC	BX
	JMP	SHOWF
ENDF:	MOV	AL,SPACE
	CALL	WRITEBYTE
	MOV	AL,'='
	MOV	CX,5
BARS2:
	CALL	WRITEBYTE
	LOOP	BARS2
	MOV	AL,CR
	CALL	WRITEBYTE
	MOV	AL,LF
	CALL	WRITEBYTE
	RET
SHOWFILE ENDP

HEAD	PROC	NEAR
	MOV	CX,WORD PTR NUMLIN
NEWBUF:
	CALL	FILLBUF			;FILL INPUT BUFFER
	JNB	BEGIN			;IF OK
	JMP	BADREAD
BEGIN:
	CMP	BX,DX			;SEE IF ANYTHING READ
	JE	CLOSEFILE		;NO, ASSUME EOF

SHOW:   CMP     BX,DX                   ;DISPLAY TEXT - TOP OF LOOP
        JGE     NEWBUF			;IF END OF BUFFER
        MOV     AL,BYTE PTR [BX]	;GET CHAR. FROM BUFFER
        INC     BX			;BUMP POINTER
	CALL	WRITEBYTE		;WRITE CHAR. TO STDOUT
        CMP     AL,CTRL$Z		;TEST FOR ^Z
	JE	CLOSEFILE		;IF FOUND ^Z
        CMP     AL,CR                   ;IS CR?
        JNE     SHOW                    ;NOPE
        LOOP    SHOW                    ;YES, COUNT A LINE

;	LINE COUNT REACHED

	CMP	BX,DX			;EXACTLY AT END OF BUFFER?
	JNE	NOTEND			;NO
	CALL	FILLBUF			;YES, JUST OUR LUCK, FILL IT AGAIN
	JNB	NOTEND
	JMP	SHORT BADREAD		;IF ERROR
NOTEND:
        MOV     AL,BYTE PTR [BX]	;GET CHAR. AFTER LAST CR
        CMP     AL,LF
        JNE     CLOSEFILE
        CALL    WRITEBYTE               ;SHOW LF IF AFTER LAST CR
	JMP	CLOSEFILE
BADREAD:
        MOV     DX,OFFSET MSG2		;SHOW ERROR
        CALL    ERRORMSG
	MOV	DX,OFFSET FILENAME
	CALL	ERRORMSG		;AND FILENAME
	MOV	DX,OFFSET MSG1E
	CALL	ERRORMSG		;AND CR/LF
CLOSEFILE:
        MOV     BX,WORD PTR INHANDLE
        MOV     AH,DOS2_CLOSE
        INT     DOS                     ;CLOSE INPUT FILE
EXIT1:
	CALL	FLUSH			;CLEAR OUTPUT BUFFER
	RET
HEAD	ENDP

;	FILL BUFFER FROM DISK - RETURN 'C'=1 IF ERROR, BX = POINTER
;	TO START OF BUFFER, DX = POINTER TO LAST CHAR + 1

FILLBUF PROC	NEAR
	PUSH	CX			;SAVE LINE COUNT
        MOV     CX,INBUFSIZE            ;CX = # BYTES TO READ
        MOV     BX,WORD PTR INHANDLE    ;BX = FILE HANDLE
        MOV     DX,OFFSET INBUF
        MOV     AH,READ
        INT     DOS                     ;FILL BUFFER FROM FILE
	POP	CX			;RESTORE LINE COUNT
        JC      BAD                     ;IF ERROR
        MOV     BX,OFFSET INBUF
        ADD     AX,BX                   ;COMPUTE ADDR. OF LAST BYTE
        MOV     DX,AX                   ;SAVE IN DX
	CLC
BAD:
	RET
FILLBUF ENDP

; 	WRITE A BYTE TO THE OUTPUT
;
WRITEBYTE PROC 	NEAR
	PUSH	BX
	PUSH	CX
	PUSH	DX
	MOV	BX,WORD PTR OUTNDX
	MOV	BYTE PTR [BX+OUTBUF],AL
	INC	BX
	MOV	WORD PTR OUTNDX,BX
	CMP	BX,OUTBUFSIZE
	JL	NOTFUL
	CALL	FLUSH
NOTFUL:
	POP	DX
	POP	CX
	POP	BX
	RET
WRITEBYTE ENDP

;	FLUSH - WRITE OUTPUT BUFFER

FLUSH	PROC	NEAR
	PUSH	AX
	MOV	CX,WORD PTR OUTNDX
	MOV	DX,OFFSET OUTBUF
	MOV	BX,1
	MOV	AH,WRITE
	INT	DOS		;WRITE TO OUTPUT
	POP	AX
	MOV	WORD PTR OUTNDX,0
	RET
FLUSH	ENDP

EXIT2:
        MOV     AH,EXIT
        INT     DOS                     ;EXIT TO DOS

        END     ENTRY



LS.ASM

        TITLE LS - SORTED DIRECTORY PROGRAM
        PAGE  55,132
;************************************************************************
;       THIS PROGRAM DISPLAYS A SORTED DIRECTORY WITH FILE SIZES.
;       IT REQUIRES MSDOS VERSION 2 OR HIGHER.
;
;       usage:  LS [-A -D -L -N -R -S - T -1] afn
;               -A includes hidden and system files
;		-D displays information on directory (not its contents)
;               -L shows date, time, file size in bytes for each file
;		-N suppresses sorting
;		-R reverses order of sort
;		-T sorts by date/time of file
;		-S shows file sizes
;		-1 forces single-column display with no paging
;               afn may contain * and ?: e.g. "LS PROG?.*"
;
;       BY: JON DART
;           3012 HAWTHORN ST.,
;           SAN DIEGO, CA 92104
;
;	Version 3.2, 30-Mar-89 -T option without -R sorts dates so that most
;			       recent files are first (like unix).  Illegal
;			       switch error msg. also corrected.
;	Version 3.1, 21-Mar-89 fixed bug in free space display.
;	Version 3.0, 18-Jan-89 modifications to handle disks > 65 Megabytes.
;		 	       also a bug fix in FIXPATH.
;	Version 2.2, 30-Apr-88 allows multiple file arguments, uses GETARGS
;		     library routine, adds NODUPES procedure.
;	Version 2.1, 09-Feb-88 minor cleanup, some redundant code removed.
;	Version 2.0, 28-Nov-87 fixes bug in display of very large directories.
;		     -S and -D options and PAD asssembly option added.  Old
;		     -D option changed to -T.
;	Version 1.9, 01-Nov-87 assembles under MASM 5.0
;	Version 1.8, 24-Oct-86 adds LOWCASE assembly option, -1, -D, -N and
;		     -R switches.  Some problems in redirecting output to
;		     disk were fixed.  Also minor bugs in display routine.
;	Version 1.7, 04-Oct-86 linked with new library, no longer writes
;		     nulls to console (which interfered with screen print)
;	Version 1.6, 05-Jul-86 fixes sometime loss of top display line.
;		     Now accepts - as well as / for switches.
;		     Also modified to assemble under MASM 4.0.
;       Version 1.5, 04-May-86 fixes clear screen routine
;       Version 1.4, 17-Jan-86 UTIL and CONSOLE made external modules,
;                    adds FIXPATH function
;       Version 1,3, 07-Nov-85 /L option added
;       Version 1.2, 03-Nov-85 fixes bug in size function
;       Version 1.1, 18-Oct-85
;
;   TO BUILD LS.EXE:
;	MASM LS,LS,NUL,NUL
;	LINK LS,LS,NUL,ASM
;	EXEPACK LS.EXE LS.NEW
;	DEL LS.EXE
;	REN LS.NEW LS.EXE

TRUE    EQU     1
FALSE   EQU     0
IBM     EQU     TRUE            ;TRUE IF IBM-COMPATIBLE AT BIOS LEVEL
LOWCASE EQU	FALSE		;TRUE FOR LOWER-CASE OUTPUT (FOR UNIX PURISTS)
PAD	EQU	TRUE		;PAD FILE NAMES WITH SPACES (SO EXTENSIONS LINE
				;UP). FALSE MEANS DON'T PAD WITHIN FILE NAMES.
BIOS    EQU     10H             ;BIOS INTERRUPT FOR VIDEO FUNCTIONS
MAXARGS EQU	100		;MAX # COMMAND-LINE ARGUMENTS
MAXFILES EQU    512             ;MAX # OF FILES
NCOLUMN EQU     5               ;# OF COLUMNS TO DISPLAY (SHORT MODE,
				;WITH NO FILE SIZES)
NCOLUMNS EQU	4		;# OF COLUMNS TO DISPLAY (SHORT MODE,
				;WITH FILE SIZES)
MAXLINES EQU    24              ;# LINES ON SCREEN FOR FILE DISPLAY
	IF	IBM
FENCE	EQU	179		;VERTICAL BAR CHAR. (IBM ONLY)
	ELSE
FENCE   EQU     '|'             ;BORDER BETWEEN COLUMNS
	ENDIF
K       EQU     1024            ;1 K

M       EQU     BYTE PTR 0[BX]

        .XLIST
        INCLUDE ASCII.DEF
        INCLUDE MSDOS2.DEF
	INCLUDE MACROS.DEF
        .LIST

ERROR   MACRO   ERRNUM                  ;SHOW ERROR MESSAGE
        MOV     DX,OFFSET MSG&ERRNUM
	CALL	ERRORMSG
	ENDM

;	UNINITIALIZED DATA

DATA	SEGMENT PUBLIC PARA 'DATA'
DGROUP  GROUP DATA
	ASSUME DS:DGROUP
ARGPTRS DW      MAXARGS DUP (?)         ;POINTERS TO START OF ARGUMENTS
ARGBUF  DB      256 DUP (?)		;BUFFER FOR ARGUMENTS
FILNAM  DB      80 DUP (?)              ;FILE NAME FROM COMMAND LINE
SPATH   DB      80 DUP (?)              ;SEARCH PATH
SPREFIX DB      80 DUP (?)              ;SEARCH PREFIX (NOT USED)
OUR_DTA DB      128 DUP (?)             ;DISK TRANSFER ADDRESS, USED BY MSDOS
                                        ;FUNCTIONS 4EH AND 4FH.
ADDRLIST DB     (2*MAXFILES) DUP (?)    ;HOLDS POINTERS TO INFO ON EACH FILE
BUFFER  DB      (22*MAXFILES) DUP (?)   ;HOLDS INFO ON EACH FILE, RETURNED BY
                                        ;MSDOS FUNCTION 4EH OR 4FH.
ENDBUF  EQU     BUFFER+(MAXFILES*22)

STACK   SEGMENT PARA STACK 'STACK'
        ASSUME SS:STACK
        DB 512 DUP (?)
STACK   ENDS

;*********************
; MEMORY DEFINITIONS *
;*********************
;

SWLIST		DB	'ADLNRST1',0	;LIST OF LEGAL SWITCHES
AFLAG   	DW      0               ;SET <>0 IF -A
DFLAG		DW	0		;SET <>0 IF -D
LFLAG   	DW      0               ;SET <>0 IF -L
NFLAG		DW	0		;SET <>0 IF -N
RFLAG		DW	0		;SET <>0 IF -R
SFLAG   	DW	0		;SET <>0 IF -S
TFLAG		DW	0		;SET <>0 IF -T
ONEFLAG 	DW	0		;SET <>0 IF -1
DRIVE   	DB      0               ;DRIVE (0 FOR DEFAULT, 1 FOR A, ETC)
ISDEV		DB	0		;=0 IF OUTPUT REDIRECTED TO DISK
VPAGE   	DB      0               ;VIDEO PAGE # (IBM ONLY)
SCRSIZE 	DB      80              ;SCREEN WIDTH (80 IS DEFAULT FOR NON-IBM)
VMODE   	DB      0               ;DISPLAY MODE (IBM ONLY)
ATTRIBUTES 	DB  	00010001B       ;DEFAULT FILE ATTRIBUTES
NUMFILES 	DW      0               ;NUMBER OF FILES FOUND
NDUPES		DW	0	        ;NUMBER OF DUPLICATE FILE NAMES
GAP     	DW      0               ;USED BY SORT ROUTINE
COLCOUNT 	DW      1               ;COUNT OF COLUMNS DISPLAYED
LINECOUNT 	DW      1               ;COUNT OF LINES DISPLAYED
FIRSTSCREEN 	DW 	1 DUP (?)	;SET =1 IF -L OR -S AND FIRST SCREEN
FILECOUNT 	DW      0              	;COUNT OF FILES DISPLAYED
BASE    	DW      1	        ;FIRST FILE TO SHOW
NEXTFREE 	DW      1 DUP (?)       ;ADDR. OF NEXT FREE SLOT IN BUFFER
MAXLINE 	DW      1 DUP (?)       ;NO. OF LINES TO DISPLAY
MAXFILE 	DW      1 DUP (?)       ;MAX FILE TO SHOW
MAXCOL  	DB      1 DUP (?)       ;COLUMN WIDTH OF DISPLAY
FILESPERSCREEN  DW 1 DUP (?)		;FILES PER SCREEN
LOSIZE  DW      1 DUP (?)               ;USED IN CALCULATING FREE SPACE
USED    DQ      0                       ;COUNT OF FILE SPACE USED
DEFAULT DB      '????????.???',0        ;DEFAULT FILE NAME
TXT0    DB      '<DIR','>',0            ;SAY IT'S A DIR
TXT1    DB      ' files, using ',0
TXT2    DB      ' bytes ','(',0
TXT3    DB      ' bytes free',')',0
TXT4    DB      ' free',')',0
TXT5	DB	CR,LF,0
MSG2    DB      ' : no matching files.',CR,LF,0
MSG3    DB      '[ More ] ',0
MSG4	DB	CR,LF,'illegal switch: ',0
MSG5    DB      CR,LF,'too many arguments',CR,LF,0

DATA	ENDS

CODE	SEGMENT PUBLIC PARA 'CODE'
	ASSUME CS:CODE
        EXTRN   SKIPSP:NEAR,UC:NEAR,DECOUT:NEAR,CPYCNT:NEAR,CPY:NEAR
	EXTRN   UCSTR:NEAR
	EXTRN	CMDSRC:NEAR
        EXTRN   ERRORMSG:NEAR
	EXTRN	TYPTX:NEAR
	EXTRN	PRTSTR:NEAR
        EXTRN   PRINTDD:NEAR
        EXTRN   COUT:NEAR,CIN:NEAR,CLRCO:NEAR,CRLF:NEAR
        EXTRN   FIXPATH:NEAR
	EXTRN   TYPE_DIR:ABS
	EXTRN   GETARGS:NEAR

;*********************
; START OF CODE HERE *
;*********************
ENTRY:
        TEST_DOS2                       ;TEST FOR DOS 2.0, EXIT IF DOS 1
	PUSH	DS
	MOV	AX,DGROUP
	MOV	DS,AX
	MOV	AL,0			;PREPARE TO CHECK DEVICE STATUS
	MOV	BX,1			;OF STDOUT
	MOV	AH,IO_CTL
	INT	DOS			;GET STATUS
	AND	DX,0080H		;MASK ISDEV BIT
	MOV	BYTE PTR ISDEV,DL	;SAVE RESULT
        IF      IBM
        MOV     AH,15
        INT     BIOS                    ;GET VIDEO MODE
        MOV     BYTE PTR VPAGE,BH       ;STORE VIDEO PAGE #
        MOV     BYTE PTR SCRSIZE,AH     ;STORE CHARS/LINE
        MOV     BYTE PTR VMODE,AL       ;STORE DISPLAY MODE
        ENDIF
	POP	DS
        CALL    PARSE_SWITCHES		;PARSE COMMAND-LINE SWITCHES
        MOV     AX,OFFSET BUFFER
        MOV     WORD PTR NEXTFREE,AX	;INIT "NEXTFREE" TO START OF FILE LIST
GETFILES:
	PUSH	CX
	PUSH	SI
	CALL	PARSE_ARG		;BUILD SEARCH PATH
        CALL    FIND_FILES              ;SEARCH FOR FILES, BUILD LIST IN MEMORY
	POP	SI
	POP	CX
	ADD	SI,2
	CMP	CX,0
	JLE	SORT
	LOOP	GETFILES
SORT:
        CALL    SORT_FILES              ;SORT FILE LIST
        CALL    SHOW_INFO               ;SHOW DISK USAGE INFO
        CALL    SHOW_FILES              ;DISPLAY DIRECTORY
        MOV     AX,256*EXIT
        INT     DOS                     ;EXIT TO DOS

;***********************************************
; PARSE COMMAND LINE SWITCHES                  *
; ENTRY:   DS:BX POINTS TO DOS COMMAND LINE    *
; RETURNS: CX = # OF ARGUMENTS (LESS SWITCHES) *
;	   SI = POINTER TO ARGUMENT POINTERS   *
;***********************************************
PARSE_SWITCHES  PROC  NEAR
	MOV	AX,DGROUP
	MOV	ES,AX
        MOV     BX,(80H)         	;GET BYTE COUNT FOR COMMAND LINE
        PUSH    BX
        MOV     DL,[BX]
        MOV     DH,0
        ADD     BX,DX
        INC     BX
        MOV     [BX],BYTE PTR 0         ;PUT 0 BYTE AT END OF COMMAND LINE
        POP     BX
        INC     BX
	MOV	SI,OFFSET ARGPTRS
	MOV	DI,OFFSET ARGBUF
	MOV	CX,MAXARGS
	CALL	GETARGS			;COLLECT COMMAND LINE ARGUMENTS
	JNB	L_2			;IF OK
TOOMANY:
        ERROR   5                       ;TOO MANY ARGUMENTS (PRETTY UNLIKELY)
	MOV	AL,5			;SET EXIT CODE
	MOV	AH,EXIT
	INT	DOS			;EXIT TO DOS
;***************************
; COLLECT SWITCHES, IF ANY *
;***************************
L_2:
	MOV	AX,DGROUP
	MOV	DS,AX			;POINT TO OUR DATA SEG, NOT PSP
	MOV	SI,OFFSET ARGPTRS
NEXTSW:
	MOV	BX,WORD PTR [SI]	;POINT TO ARGUMENT
	MOV	AL,BYTE PTR [BX]	;GET 1ST CHAR.
        CMP     AL,'-'
        JE      GOTSW                   ;IF SWITCH SPECIFIED
	CMP	AL,'/'
	JE 	GOTSW
	RET
GOTSW:
        INC     BX                      ;SKIP OVER SWITCH CHARACTER
	MOV	AL,BYTE PTR [BX]
SWLOOP:
        CALL    UC                      ;MAKE SWITCH UPPER CASE
	PUSH	BX
	MOV	BX,OFFSET SWLIST
	CALL	CMDSRC			;SEARCH LIST OF VALID SWITCHES
	POP	BX
	MOV	AH,0
	ADD	AX,AX			;NOT FOUND IN LIST?
	JZ	BADSWITCH		;NO.
	MOV	DI,OFFSET AFLAG - 2
	ADD	DI,AX			;POINT TO FLAG
	NOT	WORD PTR [DI]		;1'S COMPLEMENT
	INC	BX
	MOV	AL,BYTE PTR [BX]	;GET NEXT CHAR. FROM LINE
	COMPLIST <SPACE,TAB,NULL>,ENDSW ;IF DELIM
	JMP	SWLOOP			;ELSE ASSUME THIS IS A SWITCH CHAR.
ENDSW:	ADD	SI,2			;POINT TO NEXT POINTER TO AN ARG
	LOOP	NEXTSW
	RET				;NO ARGS BESIDES SWITCHES
BADSWITCH:
        ERROR   4                       ;ILLEGAL SWITCH
	MOV	AL,BYTE PTR [BX]
        CALL    COUT                    ;DISPLAY BAD CHAR.
	CALL	CRLF
	MOV	AL,4			;SET EXIT CODE
	MOV	AH,EXIT
	INT	DOS			;EXIT TO DOS
PARSE_SWITCHES  ENDP

;*****************************************************************
; TAKE A COMMAND LINE ARGUMENT AND SET UP SEARCH PATH IN "SPATH" *
;*****************************************************************
PARSE_ARG PROC	NEAR
	CMP	CX,0
	JE	NONAME			;IF NULL ARGUMENT
	MOV	BX,[SI]
	MOV	AL,BYTE PTR [BX+1]
        CMP     AL,':'			;SEE IF DRIVE SPECIFIED
        JNE     NODRIVE                 ;IF NOT
        MOV     AL,BYTE PTR [BX]        ;GET DRIVE LETTER
        CALL    UC                      ;MAKE UPPER-CASE
        MOV     BYTE PTR FILNAM,AL   	;SAVE DRIVE LETTER IN FILE NAME BUFFER
        MOV     BYTE PTR (FILNAM+1),':'   ;SAVE COLON ALSO
        SUB     AL,'A'-1                ;CONVERT ASCII TO BINARY (A=1, ETC.)
        MOV     BYTE PTR DRIVE,AL       ;SAVE BINARY DRIVE
        ADD     BX,2                    ;SKIP OVER DRIVE AND COLON
        CALL    SKIPSP                  ;ANYTHING AFTER DRIVE SPEC?
        MOV     SI,BX                   ;SAVE START OF FILE NAME, JUST IN CASE
        JNB     DRVPLUSFILE             ;IF FILE NAME GIVEN, COPY INTO NAME BUFFER
        MOV     SI,OFFSET DEFAULT       ;ELSE SI POINTS TO DEFAULT FILE NAME
DRVPLUSFILE:
        MOV     DI,OFFSET FILNAM+2      ;DI POINTS TO DESTINATION
        JMP     SHORT COPYNAME2         ;GO COPY FILENAME TO FILE BUFFER
NONAME:
        MOV     SI,OFFSET DEFAULT	;IF NO NAME, JUST COPY DEFAULT TO FILNAM
        MOV     DI,OFFSET FILNAM
        JMP     SHORT COPYNAME2
NODRIVE:
COPYNAME:
        MOV     SI,BX                   ;DS:SI POINTS TO START OF NAME
        MOV     DI,OFFSET FILNAM
COPYNAME2:
        MOV     CX,80
        CALL    CPYCNT                  ;SAVE FILE NAME
        MOV     BX,OFFSET FILNAM        ;BX - FILE NAME
        MOV     CX,OFFSET SPATH         ;CX - STORAGE FOR SEARCH PATH
        MOV     DX,OFFSET SPREFIX       ;DX - STORAGE FOR SEARCH PREFIX (NOT USED)
        CALL    FIXPATH                 ;PARSE FILE NAME
	CMP	AX,TYPE_DIR
	JE	GOTDIR			;IF DIRECTORY
        RET
GOTDIR:	CMP	BYTE PTR DFLAG,0	;-D FLAG SET?
	JNE	DFLAGSET
	RET
DFLAGSET:
	MOV	SI,OFFSET FILNAM
	MOV	DI,OFFSET SPATH
	MOV	CX,65
	CALL	CPYCNT			;MAKE SEARCH PATH = FILE NAME
	RET
PARSE_ARG ENDP

;**********************
; FILE SEARCH ROUTINE *
;**********************
FIND_FILES PROC NEAR
        MOV     DX,OFFSET OUR_DTA       ;LOAD DTA OFFSET
        MOV     AH,SET_DTA

        INT     DOS                     ;SET UP DTA
        MOV     CX,WORD PTR ATTRIBUTES  ;GET DEFAULT ATTRIBUTES
        CMP     BYTE PTR AFLAG,0        ;A FLAG SET?
        JE      SHORT NOAFLAG           ;NO
        OR      CX,00000110B            ;YES, SET SYSTEM & HIDDEN ATTRIBUTES
NOAFLAG:
;***********************
; SEARCH FOR 1ST MATCH *
;***********************
        MOV     DX,OFFSET SPATH         ;POINT TO SEARCH PATH
        MOV     AH,FIND_FIRST
        INT     DOS                     ;SEARCH FOR 1ST MATCH
        JNC     FOUND1ST                ;IF FOUND
	MOV	DX,OFFSET TXT5
	CALL	ERRORMSG
	MOV	DX,OFFSET FILNAM	;POINT TO FILE NAME
	CALL	ERRORMSG
	ERROR	2			;NO MATCHING FILES
	MOV	AX,256*EXIT+2
	INT	DOS
;*********************************************************************
; COLLECT FILE INFO BLOCKS IN BUFFER, POINTERS TO BLOCKS IN ADDRLIST *
;*********************************************************************
FOUND1ST:
FOUND1: INC     WORD PTR NUMFILES
        CMP     WORD PTR NUMFILES,MAXFILES
        JGE     LASTFILE
        MOV     DI,WORD PTR NEXTFREE
        PUSH    DI
        MOV     SI,OFFSET OUR_DTA+21
        MOV     CX,22
        CALL    CPY             ;COPY FILE INFO TO TABLE
        MOV     AX,WORD PTR OUR_DTA+26  ;LSW OF FILE SIZE
        MOV     DX,WORD PTR OUR_DTA+28  ;MSW OF FILE SIZE
        ADD     AX,WORD PTR USED        ;ADD LSW OF FILE SIZE TO
                                        ;LSW OF USED COUNT
        MOV     WORD PTR USED,AX
        ADC     WORD PTR USED+2,DX      ;NOW ADD MSW
        ADD     WORD PTR NEXTFREE,22
        MOV     AX,NUMFILES
        ADD     AX,AX
        ADD     AX,OFFSET ADDRLIST
        MOV     SI,AX
        POP     BX
        MOV     WORD PTR [SI],BX   ;STORE ADDR IN LIST
        MOV     AH,FIND_NEXT
        INT     DOS             ;FIND NEXT FILE
        JNC     FOUND1          ;LOOP UNTIL NO MORE
LASTFILE:
        RET
FIND_FILES ENDP

;********************************************************
; SORT POINTERS IN ADDRLIST, USING SHELL SORT ALGORITHM *
;********************************************************
SORT_FILES PROC NEAR
	CMP	BYTE PTR NFLAG,0	;CHECK N FLAG
	JNE	NODUPES			;IF NO SORTING
        MOV     AX,WORD PTR NUMFILES
SORT1:					;TOP OF LOOP 1
        SHR     AX,1
        MOV     WORD PTR GAP,AX
        JNZ     SORT11			;DONE IF GAP = 0

;***********************************************************
; make one more pass to eliminate duplicate file names     *
; (necessary if user types something like "LS *.ASM H*.*") *
;***********************************************************
NODUPES:
	MOV	AX,WORD PTR NUMFILES
	CMP	AX,2
	JGE	NODUPES0
	RET
NODUPES0:
	MOV	CX,1
NODUPES1:
	MOV	AX,WORD PTR NDUPES
	AND	AX,AX
	JZ	NODUPES2
	ADD	AX,CX
	MOV	SI,AX
	ADD	SI,SI
	ADD	SI,OFFSET ADDRLIST
	MOV	DI,CX
	ADD	DI,DI
	ADD	DI,OFFSET ADDRLIST
	MOV	AX,WORD PTR [SI]
	MOV	WORD PTR [DI],AX
NODUPES2:
	MOV	DX,CX
	INC	DX
	ADD	DX,WORD PTR NDUPES
	CALL	COMPARE
	JNE	NODUPES3
	INC	WORD PTR NDUPES		;BUMP # OF DUPLICATES
NODUPES3:
	INC	CX
	MOV	AX,WORD PTR NUMFILES
	CMP	CX,AX
	JL	NODUPES1
	MOV	AX,WORD PTR NUMFILES
	SUB	AX,WORD PTR NDUPES
	MOV	WORD PTR NUMFILES,AX
        RET

SORT11:
        INC     AX
        MOV     BX,AX			;BX = COUNTER FOR LOOP 2
SORT2:  CMP     BX,WORD PTR NUMFILES	;TOP OF LOOP 2
        JG      SHORT SORT5
        MOV     CX,BX
SORT3:					;TOP OF LOOP 3
        MOV     DX,CX
        SUB     CX,WORD PTR GAP
        JBE     SHORT SORT4
	CMP	BYTE PTR TFLAG,0
	JE	SORT3A			;IF SORTING NAMES
	CALL	COMPDATE		;SORTING DATES, USE SPECIAL COMPARE
	JMP	SHORT SORT3B
	CALL	SWAP
	JMP	SORT3
SORT3A:
        CALL    COMPARE                 ;COMPARE ADDRLIST[DX], ADDRLIST[CX]
SORT3B:
	MOV	AX,0
        JLE     SORT3C
	NOT	AX
SORT3C:
	CMP	BYTE PTR RFLAG,0	;R FLAG SET?
	JE	SORT3D			;NO
	NOT	AX			;YES, NEGATE RESULT OF COMPARISON
SORT3D:
	AND	AX,AX
	JZ	SORT4
        CALL    SWAP			;OUT OF ORDER, SO SWAP
        JMP     SORT3			;BOTTOM OF LOOP 3
SORT4:
        INC     BX
        JMP     SORT2			;BOTTOM OF LOOP 2
SORT5:
        MOV     AX,WORD PTR GAP
        JMP     SORT1			;BOTTOM OF LOOP 1
SORT_FILES ENDP

;********************************************************
; COMPARE TWO FILE NAMES                                *
; ENTRY:  CX AND DX ARE INDEX TO POINTERS (1..NUMFILES) *
;         IN ADDRLIST                                   *
;********************************************************
COMPARE PROC    NEAR
        PUSH    CX
        MOV     SI,CX
        ADD     SI,SI
        ADD     SI,OFFSET ADDRLIST
        MOV     AX,WORD PTR [SI]
        ADD     AX,9
        MOV     SI,AX
        MOV     DI,DX
        ADD     DI,DI
        ADD     DI,OFFSET ADDRLIST
        MOV     AX,WORD PTR [DI]
        ADD     AX,9
        MOV     DI,AX
        MOV     CX,11
	COMP_STRINGS
        POP     CX
        RET
COMPARE ENDP

;*********************************************************
; COMPARE TWO FILES BY DATE                              *
; ENTRY:   CX AND DX ARE INDEX TO POINTERS (1..NUMFILES) *
;  	   IN ADDRLIST                                   *
;*********************************************************
COMPDATE PROC    NEAR
        PUSH    CX
        MOV     SI,CX
        ADD     SI,SI
        ADD     SI,OFFSET ADDRLIST
        MOV     AX,WORD PTR [SI]
        MOV     SI,AX
        MOV     DI,DX
        ADD     DI,DI
        ADD     DI,OFFSET ADDRLIST
        MOV     AX,WORD PTR [DI]
        MOV     DI,AX
	MOV	AX,WORD PTR [DI+3]
	CMP	AX,WORD PTR [SI+3]
	JNE	COMPD1			;J/IF DATES NOT =
	MOV	AX,WORD PTR [DI+1]	;COMPARE TIMES
	CMP	AX,WORD PTR [SI+1]
COMPD1:
        POP     CX
        RET
COMPDATE ENDP

;*********************************************************
; SWAP TWO OUT-OF-ORDER FILES                            *
; ENTRY:   CX AND DX ARE INDEX TO POINTERS (1..NUMFILES) *
; 	   IN ADDRLIST                                   *
;*********************************************************
SWAP    PROC    NEAR
        PUSH    BX
        MOV     SI,CX
        ADD     SI,SI
        ADD     SI,OFFSET ADDRLIST
        MOV     BX,WORD PTR [SI]
        MOV     DI,DX
        ADD     DI,DI
        ADD     DI,OFFSET ADDRLIST
        MOV     AX,WORD PTR [DI]
        MOV     WORD PTR [SI],AX
        MOV     WORD PTR [DI],BX
        POP     BX
        RET
SWAP    ENDP

;********************
; DISPLAY DIRECTORY *
;********************
SHOW_FILES PROC NEAR
        MOV     SI,OFFSET ADDRLIST
	MOV	WORD PTR FIRSTSCREEN,0
	MOV	AL,BYTE PTR LFLAG
	OR	AL,BYTE PTR SFLAG	;S OR L FLAG SET?
	JZ	DISP0A			;NO
	INC	WORD PTR FIRSTSCREEN	;YES, SET FLAG
DISP0A:
	CMP	BYTE PTR ONEFLAG,0
	JE	DISP0			;IF -1 FLAG NOT SET
        MOV     AX,WORD PTR NUMFILES    ;NUMBER OF FILES THERE ARE
        MOV     WORD PTR MAXFILE,AX     ;=LAST FILE # TO DISPLAY
	MOV	BX,2			;INDEX TO 1ST FILE
	JMP	DISP11			;GO SHOW FILES IN 1 COLUMN
DISP0:
        MOV     BYTE PTR MAXCOL,2	;INITIALIZE MAXCOL FOR LONG DISPLAY
        CMP     BYTE PTR LFLAG,0        ;LONG DISPLAY FLAG SET?
        JNE     DISP2                   ;YES, KEEP MAXCOL INTACT
	CMP	BYTE PTR SFLAG,0	;S FLAG SET?
	JE	DISP1			;NO
        MOV     BYTE PTR MAXCOL,NCOLUMNS   ;YES, SET MAXCOL APPROPRIATELY
	JMP	SHORT DISP2
DISP1:	MOV	BYTE PTR MAXCOL,NCOLUMN	;SET NCOLUMN FOR SHORT DISPLAY, NO SIZES
DISP2:
        MOV     BL,BYTE PTR MAXCOL
        MOV     AL,MAXLINES
	SUB	AL,BYTE PTR FIRSTSCREEN
        MUL     BL                      ;COMPUTE (MAXLINES)*MAXCOL
	MOV	WORD PTR FILESPERSCREEN,AX ; SAVE RESULT
        ADD     AX,WORD PTR BASE        ;ADD BASE TO GET LAST FILE # WE
                                        ;CAN SHOW ON THIS SCREEN
        MOV     BX,AX
        MOV     AX,WORD PTR NUMFILES    ;COMPARE WITH TOTAL # FILES
        INC     AX                      ;+1
        CMP     AX,BX                   ;WILL ALL FILES FIT ON SCREEN?
        JG      DISP6                   ;NO
	MOV	AX,WORD PTR NUMFILES	;GET MAX. FILE NUMBER
	SUB	AX,WORD PTR BASE	;FIND HOW MANY FILES TO GO
	INC	AX
        DIV     BYTE PTR MAXCOL         ;COMPUTE # OF LINES/COLUMN
        CMP     AH,0                    ;IS REMAINDER 0?
        JE      DISP5                   ;YES, SKIP AHEAD
        INC     AL                      ;NO, ADD 1 TO LINE COUNT
DISP5:  MOV     AH,0                    ;ZERO HI BYTE OF LINES/COLUMN
        MOV     WORD PTR MAXLINE,AX     ;MAXLINE=# OF LINES TO SHOW IN
                                        ;THIS SCREEN
        MOV     AX,WORD PTR NUMFILES    ;NUMBER OF FILES THERE ARE
        MOV     WORD PTR MAXFILE,AX     ;=LAST FILE # TO DISPLAY
        JMP     SHORT DISP7
DISP6:  MOV     AX,MAXLINES             ;TOO MANY FILES FOR 1 SCREEN, SO
	SUB	AX,WORD PTR FIRSTSCREEN
        MOV     WORD PTR MAXLINE,AX     ;USE WHOLE SCREEN FOR DISPLAY
	MOV	AX,WORD PTR FILESPERSCREEN ; GET MAX. FILES/SCREEN
        MOV     WORD PTR MAXFILE,AX     ;STORE AS # OF FILES TO SHOW
DISP7:
        MOV     WORD PTR COLCOUNT,1	;START AT COLUMN 1
	MOV	WORD PTR LINECOUNT,1	;AND LINE 1
        MOV     WORD PTR FILECOUNT,0    ;# OF FILES DISPLAYED IN THIS SCREEN
DISP8:
        MOV     CX,1                    ;CX WILL COUNT UP TO COLCOUNT
        MOV     BX,0                    ;BX WILL HOLD (CX-1)*MAXLINE
DISP9:                                  ;COMPUTE (COLCOUNT-1)*MAXLINE
        CMP     CX,WORD PTR COLCOUNT
        JGE     DISP10
        ADD     BX,WORD PTR MAXLINE
        INC     CX
        JMP     DISP9
DISP10:
        ADD     BX,WORD PTR LINECOUNT   ;ADD LINE COUNT
        DEC     BX                      ;-1
        ADD     BX,WORD PTR BASE        ;ADD TO BASE
        CMP     BX,WORD PTR NUMFILES    ;BLANK SLOT IN LAST COLUMN?
        JG      DISP12                   ;YUP.
	ADD	BX,BX			;DOUBLE BX TO GET INDEX TO ADDRLIST
        CMP     WORD PTR COLCOUNT,1     ;1ST COLUMN?
        JE      DISP11                   ;YES, NO FENCE
        CALL    DOFENCE                 ;SHOW FENCE
DISP11:
        MOV     DI,WORD PTR [SI+BX]     ;GET ADDRESS OF FILE INFO
	PUSH	BX
        CALL    SHOW                    ;SHOW A FILE
        CALL    BUMPCOL                 ;UPDATE LINE & COLUMN COUNTS
	POP	BX
        INC     WORD PTR FILECOUNT      ;BUMP FILE COUNT
        MOV     AX,WORD PTR FILECOUNT   ;GET IT
        CMP     AX,WORD PTR MAXFILE     ;DONE ALL FILES?
        JGE     DISP14                  ;IF DONE
	CMP	BYTE PTR ONEFLAG,0	;TEST 1-COLUMN FLAG
	JE	DISP13			;IF NOT SET, DO PAGING
	CALL	CRLF			;SHOW CR/LF
	ADD	BX,2			;ADVANCE TO NEXT SLOT IN ADDRLIST
	JMP	DISP11			;DO NEXT FILE
DISP12:
        CALL    BUMPCOL
DISP13:
        MOV     AX,WORD PTR LINECOUNT   ;GET LINE COUNT
        CMP     AX,WORD PTR MAXLINE     ;IS IT >MAX LINE?
        JLE     DISP8                   ;IF NOT

;       COME HERE WHEN SCREEN IS FULL OR ALL FILES DONE:
DISP14:
	CMP	BYTE PTR ONEFLAG,0
	JE	DISP15			;IF NOT -1
	RET				;DONE IF -1
DISP15:
        CMP     WORD PTR COLCOUNT,1     ;IS COLUMN COUNT =1?
        JE      DISP16                  ;YES, NO CR NEEDED
	CMP	WORD PTR NUMFILES,0	;WAS AT LEAST ONE FILE DISPLAYED?
	JE	DISP16			;NO, NO CR NEEDED
        CALL    CRLF                    ;SHOW CR AFTER LAST LINE
DISP16:
        MOV     AX,WORD PTR BASE
        ADD     AX,WORD PTR MAXFILE
        MOV     WORD PTR BASE,AX        ;GET BASE FOR NEXT SCREEN
        MOV     BX,AX
        MOV     AX,WORD PTR NUMFILES
        SUB     AX,BX                   ;COMPUTE # FILES REMAINING
        JBE     DISP17                  ;IF NO MORE
        CALL    WAITCR                  ;WAIT FOR USER
	MOV	WORD PTR FIRSTSCREEN,0	;CLEAR "FIRST SCREEN" FLAG
        JMP     DISP2                   ;DO NEXT SCREEN
DISP17:
        RET
SHOW_FILES ENDP

BUMPCOL PROC    NEAR
	CMP	BYTE PTR ONEFLAG,0
	JNE	BC1			;JUST RETURN IF 1 COLUMN
        INC     WORD PTR COLCOUNT       ;BUMP COLUMN COUNT
        MOV     AX,WORD PTR COLCOUNT
        CMP     AL,BYTE PTR MAXCOL      ;PAST LAST COLUMN?
        JLE     BC1                     ;NO, KEEP GOING
        CALL    CRLF                    ;YES, START NEW LINE
        MOV     WORD PTR COLCOUNT,1     ;RESET COLUMN COUNTER
        INC     WORD PTR LINECOUNT      ;UPDATE LINE COUNTER
BC1:
        RET
BUMPCOL ENDP

DOFENCE PROC    NEAR                    ;SHOW "FENCE" BETWEEN COLUMNS
        CMP     BYTE PTR LFLAG,0        ;L FLAG SET?
        JE      SHORTFENCE              ;NO.
	CALL    TWOSP
	MOV	AX,FENCE
        CALL	COUT
TWOSP:
	MOV	AL,SPACE		;YES, LONGER GAP BTWN. COLUMNS
	CALL	COUT
	MOV	AL,SPACE
	CALL	COUT
        RET
SHORTFENCE:
        MOV	AX,SPACE
	CALL	COUT
	MOV	AX,FENCE
	CALL	COUT
	MOV	AX,SPACE
	CALL	COUT
        RET
DOFENCE ENDP

;*******************************************
; SHOW FILE NAME, SIZE (DI POINTS TO INFO) *
;*******************************************
SHOW    PROC    NEAR
        PUSH    DI
        MOV     CX,8
SHOWNAME:                               ;SHOW FILE NAME
        MOV     AL,BYTE PTR [DI+9]      ;GET CHAR.
        CMP     AL,'.'
        JE      SHOW1                   ;DOT MEANS END OF NAME
        CMP     AL,0
        JE      SHOW1			;SO DOES NULL
	IF	LOWCASE
	CALL	LC			;MAKE LOWER CASE
	ENDIF
        CALL    COUT                    ;SHOW CHAR.
        INC     DI                      ;BUMP POINTER
	DEC	CX
	JMP	SHOWNAME		;LOOP

SHOW1:
	IF	PAD
	PUSH    AX                      ;FOUND END OF NAME, UNUSED COUNT IN CX
        CALL    SPOUT                   ;WRITE CX SPACES
        POP     AX
	ENDIF
	CMP	AL,'.'			;WAS LAST CHAR DOT?
	JNE	NODOT			;NO
	INC	DI			;POINT PAST DOT
	MOV	AX,'.'
        CALL    COUT                    ;NOW WRITE DOT
	JMP	SHORT SHOW3
NODOT:	MOV	AX,SPACE		;SHOW SPACE (INSTEAD OF DOT)
	CALL	COUT
SHOW3:
	PUSH	CX
	MOV     CX,3                    ;PREPARE TO SHOW EXTENSION (3 CHARS.)
SHOW4:  MOV     AL,BYTE PTR [DI+9]      ;GET CHAR.
        CMP     AL,0
        JE      SHOW4A                  ;IF NULL, SHOW A SPACE
	IF	LOWCASE
	CALL	LC			;MAKE LOWER CASE
	ENDIF
        INC     DI                      ;BUMP POINTER
	JMP	SHORT SHOW4B
SHOW4A:	MOV	AX,SPACE
SHOW4B:
        CALL    COUT                    ;SHOW CHAR.
        LOOP	SHOW4			;LOOP
	POP	CX
SHOW5:
	CALL    SPOUT                   ;PAD OUT FIELD
SHOW6:  POP     DI                      ;POINT TO START OF INFO AGAIN
        MOV     AL,BYTE PTR [DI]        ;GET FILE ATTRIBUTE
        AND     AL,00010000B            ;IS IT A DIRECTORY?
        JZ      SHOW7                   ;NO.
        CMP     BYTE PTR LFLAG,0        ;LONG DISPLAY?
        JE      SHOW6B                  ;NO
        MOV     CX,3                    ;YES, 3 EXTRA SPACES
        CALL    SPOUT
SHOW6B:                                 ;HAVE DIRECTORY
	CMP	BYTE PTR SFLAG,0
	JNE	SHOW6C			;IF S FLAG SET
	CMP	BYTE PTR LFLAG,0
	JNE	SHOW6C			;IF L FLAG SET
	RET				;OTHERWISE JUST RETURN
SHOW6C:
	MOV	DX,OFFSET TXT0
	CALL	PRTSTR			;PRINT <DIR>
        CMP     BYTE PTR LFLAG,0        ;IS L FLAG SET?
        JNE     SHOW8A                  ;YES, FINISH UP
        RET                             ;NO, ALL DONE
SHOW7:                                  ;HAVE FILE, NOT DIR
        CMP     BYTE PTR LFLAG,0        ;L FLAG SET?
        JNE     SHOW8                   ;YES, LONG DISPLAY
	CMP	BYTE PTR SFLAG,0	;S FLAG SET?
	JE	SHOW9			;NO, SUPPRESS FILE SIZE DISPLAY
        MOV     AX,WORD PTR [DI+5]      ;SHORT DISPLAY, GET LSW FILE SIZE
	MOV	DX,WORD PTR [DI+7]	;AND MSW FILE SIZE
        CALL    MAKEK                   ;CONVERT TO KBYTES
        MOV     AL,SPACE                ;WITH LEADING SPACES,
        MOV     CX,5                    ;AND FIELD WIDTH = 5,
        CALL    DECOUT                  ;SHOW FILE SIZE
SHOW9:
        RET                             ;DONE

SHOW8:  MOV     AX,WORD PTR [DI+5]      ;LONG DISPLAY
        PUSH    SI                      ;MOVE SIZE TO DI:SI
        MOV     SI,AX
        MOV     AX,WORD PTR [DI+7]
        PUSH    DI
        MOV     DI,AX
        MOV     AL,SPACE                ;WITH LEADING SPACES,
        CALL    PRINTDD                 ;SHOW FILE SIZE IN BYTES
        POP     DI
        POP     SI
SHOW8A:
        MOV     CX,2
        CALL    SPOUT                   ;PRINT 2 SPACES
        MOV     AX,WORD PTR [DI+3]      ;GET PACKED DATE
        CALL    PRTDTE                  ;SHOW IT
        MOV     CX,2
        CALL    SPOUT                   ;PRINT 2 SPACES
        MOV     AX,WORD PTR [DI+1]      ;GET PACKED TIME
        CALL    PRTTME                  ;SHOW IT
        RET                             ;DONE
SHOW    ENDP

	IF 	LOWCASE
;************************************
; CONVERT CHAR. IN AL TO LOWER CASE *
;************************************
LC	PROC	NEAR
	CMP	AL,'A'
	JL	NOTUC
	CMP	AL,'Z'
	JG	NOTUC
	ADD	AL,'a'-'A'
NOTUC:
	RET
LC	ENDP
	ENDIF

;*******************************************
; SUBROUTINE - WRITES CX SPACES TO SCREEN  *
;*******************************************
SPOUT   PROC    NEAR
        JCXZ    SPOUT1
        MOV     AL,SPACE
        CALL    COUT
        DEC     CX
        JMP     SPOUT
SPOUT1: RET
SPOUT   ENDP

;****************************************
; SUBROUTINE - PAUSES AT END OF SCREEN  *
;****************************************
WAITCR  PROC    NEAR
	CMP	BYTE PTR ISDEV,0  ;ARE WE WRITING TO A DEVICE?
	JE	FILEOUT		;NO
	MOV	DX,OFFSET MSG3
	CALL	ERRORMSG	;PROMPT USER FOR OK TO SHOW NEXT SCREEN
        CALL    CLRCO           ;CLEAR TYPE-AHEAD
        CALL    CIN             ;GET A CHAR.
        CALL    CLRSCR          ;CLEAR SCREEN
        RET
FILEOUT:
	CALL	CRLF		;FILE OUTPUT, JUST SHOW CR/LF TO SEPARATE SCREENS
	RET
WAITCR  ENDP

;********************************************
; ROUTINE TO CLEAR SCREEN, HOME CURSOR:     *
; (MAY HAVE TO CHANGE FOR NON-IBM MACHINES) *
;********************************************
CLRSCR  PROC    NEAR
        PUSH    BX
        IF      IBM
        MOV     DX,0
        CALL    MOVEXY                  ;MOVE TO HOME POSITION
        MOV     AL,BYTE PTR VMODE  	;GET CURRENT VIDEO MODE
        MOV     AH,0
        INT     BIOS                    ;RESET MODE (CLEARS SCREEN)
        ELSE
        MOV     AL,FF                   ;NON-IBM CLEAR SCREEN
        CALL    COUT                    ;USE FF (WILL WORK ON MOST TERMINALS)
        ENDIF
        POP     BX
        RET
CLRSCR  ENDP

;*****************************************
; CURSOR POSITIONING ROUTINE (USES BIOS) *
;*****************************************
MOVEXY  PROC    NEAR                    ;MOVE TO ROW (DH) AND COLUMN (DL)
        MOV     AH,2
        MOV     BH,BYTE PTR VPAGE       ;VIDEO PAGE #
        MOV     BL,0                    ;COLOR (DON'T CHANGE)
        INT     BIOS                    ;MOVE CURSOR
        RET
MOVEXY  ENDP

;**************************************************
; DISPLAY NUMBER OF FILES, SPACE USED, SPACE FREE *
;**************************************************
SHOW_INFO PROC NEAR
	MOV	AL,BYTE PTR SFLAG
	OR	AL,BYTE PTR LFLAG	;S OR L FLAG SET?
	JNE	INFO0A			;YES, GO AHEAD
	RET				;NO, SUPPRESS DISPLAY
INFO0A:
        MOV     BX,WORD PTR NUMFILES    ;GET NUMBER OF FILES
	OR	BX,BX			;> 0?
	JNE	INFO0B			;YES, GO AHEAD
	RET
INFO0B:
        MOV     AL,0                    ;SUPPRESS LEADING 0'S
        MOV     CX,5                    ;FIELD WIDTH = 5
        CALL    DECOUT                  ;SHOW # FILES
	MOV	DX,OFFSET TXT1
	CALL	PRTSTR
        CMP     BYTE PTR LFLAG,0        ;L FLAG SET?
        JE      INFO1                   ;NO, SHORT DISPLAY
        MOV     DI,WORD PTR USED+2
        MOV     SI,WORD PTR USED        ;GET SPACE USED IN SI:DI
        MOV     AL,0                    ;SUPPRESS LEADING 0'S
        CALL    PRINTDD                 ;SHOW SPACE USED
	MOV	DX,OFFSET TXT2
	CALL	PRTSTR
        JMP     SHORT INFO2
INFO1:                                  ;SHORT DISPLAY
        MOV     AX,WORD PTR USED
        MOV     DX,WORD PTR USED+2      ;GET AMT. USED IN DX:AX
        CALL    MAKEK                   ;CONVERT TO K
        MOV     AL,0                    ;SUPPRESS LEADING 0'S
        MOV     CX,5                    ;FIELD WIDTH = 5
        CALL    DECOUT                  ;SHOW AMT. USED
	IF	LOWCASE
	MOV	AL,'k'
	ELSE
        MOV     AL,'K'
	ENDIF
        CALL    COUT                    ;SHOW LETTER K
INFO2:
        MOV     AH,CURRENT_DISK
        INT     DOS                     ;GET CURRENT DISK
        PUSH    AX                      ;SAVE IT
        MOV     DL,BYTE PTR DRIVE       ;DL = DRIVE WE SPECIFIED
        CMP     DL,0                    ;CURRENT DISK?
        JE      NOCHANGE                ;YES, NO NEED TO CHANGE
        INC     AL
        CMP     AL,DL                   ;SAME AS CURRENT DISK?
        JE      NOCHANGE                ;YES, NO NEED TO CHANGE
        DEC     DL                      ;MAKE 0=A, ETC.
        MOV     AH,SELECT_DISK
        INT     DOS                     ;SELECT DISK
NOCHANGE:
        MOV     DL,0                    ;NOW USING CURRENT DISK,
        MOV     AH,GET_FREE_SPACE
        INT     DOS                     ;GET FREE SPACE
        MUL     BX                      ;COMPUTE # SECTORS FREE
        PUSH    DX                      ;SAVE HI WORD
        MUL     CX                      ;MULTIPLY LO WORD OF SECTOR COUNT
                                        ;BY BYTES/SECTOR
        MOV     WORD PTR LOSIZE,AX      ;SAVE LO WORD OF FREE SPACE
        POP     BX                      ;GET HI WORD OF SECTOR COUNT
        PUSH    DX                      ;SAVE CARRY FROM LO WORD MULT.
        MOV     AX,BX                   ;MULTIPLICAND IN AX
        MUL     CX                      ;MULTIPLY HI WORD OF SECTOR COUNT
                                        ;TIMES BYTES/SECTOR
        POP     BX                      ;GET CARRY FROM LO WORD
        ADD     AX,BX                   ;ADD IT
        MOV     DX,AX                   ;DX = HI WORD OF FREE SPACE
        MOV     AX,WORD PTR LOSIZE      ;AX = LO WORD OF FREE SPACE
        CMP     BYTE PTR LFLAG,0        ;L FLAG SET?
        JE      INFO3                   ;NO, SHORT DISPLAY
        MOV     DI,DX                   ;LONG DISPLAY,
        MOV     SI,AX                   ;MOVE AMT. FREE TO DI:SI
        MOV     AL,0                    ;SUPPRESS LEADING 0'S
        CALL    PRINTDD                 ;SHOW AMT. FREE
	MOV	DX,OFFSET TXT3
	CALL	PRTSTR
        JMP     SHORT INFO4
INFO3:                                  ;SHORT DISPLAY
	CMP	DX, K
	JGE	BIG_DISK		;IF >65 MEGABYTES

        MOV     CX,K                    ;CX = 1 K
        DIV     CX                      ;DIVIDE TO GET KBYTES FREE
	MOV	DX, 0
	JMP	SHORT INFO3A

BIG_DISK:
	MOV	CX, 10*K
	DIV	CX			;DIVIDE BY 10K
	MOV	BX,10
	MUL	BX			;GET KBYTES FREE IN DX:AX

INFO3A:
	PUSH	AX			;SAVE LO WORD OF SPACE FREE
        MOV     AL,SPACE
        CALL    COUT                    ;WRITE SPACE
        MOV     AL,'('
        CALL    COUT                    ;WRITE PAREN
	POP	SI			;RESTORE LO WORD OF SPACE FREE
        MOV     DI,DX                   ;LONG DISPLAY,
        MOV     AL,0                    ;SUPPRESS LEADING 0'S
        CALL    PRINTDD                 ;SHOW AMT. FREE
	IF	LOWCASE
	MOV	AL,'k'
	ELSE
	MOV	AL,'K'
	ENDIF
	CALL	COUT
	MOV	DX,OFFSET TXT4
	CALL	PRTSTR
INFO4:
        CALL    CRLF                    ;SHOW CR/LF
        POP     DX
        MOV     AH,SELECT_DISK          ;RESET DEFAULT DISK
        INT     DOS
        RET                             ;DONE
SHOW_INFO ENDP

;***************************************************
; MAKEK = TAKE QUAD # IN DX:AX, CONVERT TO K IN BX *
;***************************************************
MAKEK   PROC    NEAR
        MOV     BX,K
        DIV     BX                      ;DIVIDE TO GET SIZE IN K
        MOV     BX,AX                   ;QUOTIENT IN BX
        CMP     DX,0                    ;MODULUS>0?
        JE      NOROUND
        INC     BX                      ;IF SO, ROUND UP
NOROUND:
        RET
MAKEK   ENDP

;       The following two routines are adapted from SDIR Version
;       2.4, by Ted Reuss (modified from a program by John Chapman), on
;       PC-SIG Volume 185.

        SUBTTL  PRINT DATE, TIME & # FILES ROUTINES
        PAGE

; PC-DOS packed date   <yyyyyyym mmmddddd>
P_DTE       RECORD  P_YR:7,P_MO:4,P_DY:5
; PC-DOS packed time   <hhhhhmmm mmmsssss>
P_TME       RECORD  P_HR:5,P_MI:6,P_2S:5

PRTDTE  PROC    NEAR    ;Print packed date in AX as MM/DD/YY
        OR      AX,AX
        JNZ     K1              ;If date <> 0
        MOV     CX,8
        CALL    SPOUT            ;Print 8 spaces
        RET
K1:     PUSH    AX
        AND     AX,MASK P_MO    ;Mask the month,
        MOV     CL,P_MO         ; set shift count,
        SHR     AX,CL           ; right justify, &
        CALL    PRTBCD          ; print it.
        MOV     AX,'/'
        CALL    COUT
        POP     AX
        PUSH    AX
        AND     AX,MASK P_DY    ;Mask the day &
        CALL    PRTBCD          ; print it.
        MOV     AX,'/'
        CALL    COUT
        POP     AX
        AND     AX,MASK P_YR    ;Mask the year,
        MOV     CL,P_YR         ; set shift count,
        SHR     AX,CL           ; right justify,
        ADD     AX,80           ; add in year bias, &
                                ; print it.
PRTBCD: AAM                     ;Convert AL to BCD
        OR      AX,'00'         ;Convert to ASCII
        PUSH    AX
        MOV     AL,AH
        CALL    COUT            ;High order digit
        POP     AX
        CALL    COUT            ;Low order digit
        RET
PRTDTE  ENDP

PRTTME  PROC    NEAR            ;Print packed time in AX as HH:MM
        OR      AX,AX
        JNZ     L1              ;if date<>0
        MOV     CX,5
        CALL    SPOUT           ;Print 5 spaces
        RET
L1:     PUSH    AX
        AND     AX,MASK P_HR    ;Mask the hours,
        MOV     CL,P_HR         ; set shift count,
        SHR     AX,CL           ; right justify, &
        CALL    PRTBCD          ; print it.
        MOV     AX,':'
        CALL    COUT
        POP     AX
        AND     AX,MASK P_MI    ;Mask the minutes,
        MOV     CL,P_MI         ; set shift count,
        SHR     AX,CL           ; right justify, &
        CALL    PRTBCD          ; print it.
        RET
PRTTME  ENDP

CODE	ENDS
        END     ENTRY

MV.ASM

        TITLE MV - MSDOS FILE MOVE UTILITY
        PAGE 55,132
;***************************************************************************
;       THIS PROGRAM RENAMES A FILE OR DIRECTORY, OR MOVES ONE OR MORE FILES
;       OR DIRECTORIES BETWEEN DISKS OR DIRECTORIES.
;
;       USAGE (afn = ambiguous file name, ufn = unambiguous file name,
;              dir = directory):
;
;               MV ufn1 ufn2    - renames or moves ufn1 to ufn2.
;
;               MV afn1 afn2 ... dir  - moves one or more files into dir.
;
;               MV -R dir1 dir2  if dir2 doesn't exist, renames or moves
;                                 dir1 to dir2.
;                                 (note: DOS 2 requires that renaming a direc-
;                                 tory be done by creating dir2, moving all
;                                 files from dir1 into it, then deleting dir1).
;
;                               - if dir2 exists, makes dir1 a subdirectory
;                                 of it.
;
;       SWITCHES (OPTIONAL):
;
;		I  prompts before overwriting files
;
;		R  allows recursively moving directories.
;
;               V  turns on "verbose" mode (shows name of each file as it
;                      is moved).
;***************************************************************************
;       BY: JON DART
;           3012 HAWTHORN ST.,
;           SAN DIEGO, CA 92104
;***************************************************************************
;   TO BUILD MV.EXE:
;	MASM MV,MV,NUL,NUL
;	LINK MV,MV,NUL,ASM
;	EXEPACK MV.EXE MV.NEW
;	DEL MV.EXE
;	REN MV.NEW=MV.EXE
;***************************************************************************
;	Version 3.3, 30-Mar-89 moves with root directory as destination
;				now handled correctly.  Also a bug fix in
;				procedure SETUP_SRC_DIR.
;	Version 3.2, 19-Jan-89 fix to FIXPATH; relinked
;	Version 3.1, 03-Nov-88 disallows MV afn foo if foo doesn't exist.
;	Version 3.0, 24-Sep-88 bug fix in FIXPATH; relinked
;	Version 2.5, 09-Feb-88 assembles under MASM 5.0
;       Version 2.4, 30-May-87 fixes a bug causing file deletion when source
;                              is in current directory and dest is omitted.
;	Version 2.3, 28-Nov-86 fixes bug involving relative directory moves
;			       (e.g. mv -r ..\foo .)
;	Version 2.2, 24-Oct-86 switch parser allows -rvi as well as -r -v -i
;	Version 2.1, 06-Jul-86 changes to memory alloc, allows hyphen for
;				switches, now assembles under masm 4.0
;	Version 2.0, 30-Jun-86 minor cleaning up, r and i switches added, c
;				switch removed
;       Version 1.9, 26-Apr-86 doesn't exit after user responds n to
;			        overwrite msg.
;       Version 1.8, 31-Mar-86 more bug fixes (*sigh*)
;       Version 1.7, 29-Mar-86 bug fixes
;       Version 1.6, 24-Mar-86 allows multiple source files
;       Version 1.5, 25-Jan-86 adds check for identical source & dest
;       Version 1.4, 14-Jan-86 adds switches
;       Version 1.3, 12-Jan-86 adds recursion
;       Version 1.2, 01-Jan-86
;       Version 1.1, 01-Nov-85
;       Version 1.0, 29-Sep-85
;***************************************************************************

	DOSSEG
	.MODEL	SMALL

PATHSIZE EQU    65                      ;MAX. SIZE OF DOS PATHNAME, +1
BUFSIZE EQU     32*1024                 ;SIZE OF COPY BUFFER
MAXLEVEL EQU    12                      ;MAX NESTING LEVEL FOR DIRECTORIES
MAXARGS EQU     40                      ;MAXIMUM # OF COMMAND LINE ARGUMENTS
TRUE    EQU     0FFH
FALSE   EQU     0
CONFIRM EQU     FALSE                   ;"CONFIRM BEFORE OVERWRITE" OPTION (DEFAULT VALUE)
RECURSE EQU	FALSE			;"RECURSE" OPTION (DEFAULT VALUE)
VERBOSE EQU     FALSE                   ;"VERBOSE" OPTION (DEFAULT VALUE)
M       EQU     BYTE PTR 0[BX]
BIT$DIR EQU     00010000B               ;BIT IN ATTRIBUTE FOR DIRECTORY
BIT$RO  EQU     00000001B               ;BIT IN ATTRIBUTE FOR WRITE-PROTECT

        .XLIST
        INCLUDE ASCII.DEF
        INCLUDE MSDOS2.DEF
	INCLUDE MACROS.DEF
        .LIST

MVINFO  STRUC
NEWDIR  DW      1 DUP (?)               ;FLAG, =1 IF NEW DIRECTORY CREATED
SRCTYPE DB     2 DUP (?)                ;TYPE OF SOURCE (0 IF UFN)
DESTTYPE DB     2 DUP (?)		;TYPE OF DEST (0 IF UFN)
ARG1    DB      PATHSIZE DUP (?)        ;FIRST ARGUMENT (SOURCE)
ARG2    DB      PATHSIZE DUP (?)        ;SECOND ARGUMENT (DEST)
ARG2A   DB      PATHSIZE DUP (?)        ;SOURCE PREFIX + DEST
SSPATH  DB      PATHSIZE DUP (?)        ;SOURCE SEARCH PATH
SPREFIX DB      PATHSIZE DUP (?)        ;SOURCE LEAD-IN PATH
DSPATH  DB      PATHSIZE DUP (?)        ;DEST SEARCH PATH
                                        ;(USED ONLY AS SCRATCH AREA)
DPREFIX DB      PATHSIZE DUP (?)        ;DEST LEAD-IN PATH
SRCNAME DB      PATHSIZE DUP (?)        ;UNAMBIGUOUS SOURCE NAME
DESTNAME DB     PATHSIZE DUP (?)        ;UNAMBIG. DEST NAME
DTA     DB      128 DUP (?)             ;DTA FOR SEARCH FN.
MVINFO  ENDS
MVSTRUCSIZE EQU 9*PATHSIZE+135          ;SIZE OF STRUCTURE

	.DATA
;       INITIALIZED DATA STORAGE:
;
MSG1    DB 'MV Version 3.3 by Jon Dart (30-Mar-89)',CR,LF,CR,LF
        DB 'USAGE (afn = ambiguous file name, ufn = unambiguous file name,'
        DB CR,LF
        DB '       dir = directory):',CR,LF,CR,LF
        DB 'MV ufn1 ufn2          move or rename ufn1 to ufn2.',CR,LF,CR,LF
        DB 'MV afn1 afn2 ... dir  move one or more files into dir.',CR,LF,CR,LF
        DB "MV -R dir1 dir2       if dir2 doesn't exist, move or rename"
        DB CR,LF
        DB '                      dir1 to dir2.',CR,LF,CR,LF
        DB '                      if dir2 exists, make dir1 a subdirectory'
        DB CR,LF
        DB '                      of it.',CR,LF,CR,LF
        DB 'SWITCHES (OPTIONAL):',CR,LF,CR,LF
        DB '-I   prompt before overwriting files.',CR,LF,CR,LF
	DB '-R   allow recursive moving of directories.',CR,LF,CR,LF
        DB '-V   echo file names as they are moved.',CR,LF
        DB 0
MSG2    DB      ': file not found.',CR,LF,0
MSG3    DB      'Source and dest are incompatible.',CR,LF,0
MSG4    DB      ' exists. Overwrite [Y or N]? ',0
MSG5    DB      ': open failed.',CR,LF,0
MSG6    DB      ': file is R/O. Are you SURE [Y or N]? ',0
MSG7	DB	': directory.',CR,LF,0
MSG8    DB      ': write error.',CR,LF,0
MSG9    DB      '; read error.',CR,LF,0
MSG10   DB      ': write error (probably disk full).',CR,LF,0
MSG11   DB      ': rename failed.',CR,LF,0
MSG12   DB      ": can't create.",CR,LF,0
MSG13   DB      ": can't remove.",CR,LF,0
MSG14   DB      "Directories nested too deep.",CR,LF,0
; MSG15   DB      "Insufficient memory for buffers.",CR,LF,0
MSG16   DB      ": Illegal switch.",CR,LF,0
MSG17   DB      "Error: source and dest are identical.",CR,LF,0
MSG18   DB      "Too many arguments.",CR,LF,0
SWLIST  DB	'IRV',0		;LIST OF LEGAL SWITCHES

	.STACK
        DB      512 DUP (?)

	.DATA?
;	UNINITIALIZED DATA AND BUFFER AREAS

RBASE   DW 	1 DUP (?)               ;POINTER TO MVSTRUC IN RSTACK
LEVEL   DW      1 DUP (?)               ;RECURSION LEVEL
SRCDRIVE  DW    1 DUP (?)               ;SOURCE DRIVE (0 = DEFAULT)
DESTDRIVE  DW   1 DUP (?)               ;DEST DRIVE (0 = DEFAULT)
INHANDLE DW	1 DUP (?)               ;INPUT FILE HANDLE
OUTHANDLE DW	1 DUP (?)               ;OUTPUT FILE HANDLE
IFLAG   DW	1 DUP (?)               ;CONFIRM FLAG
RFLAG	DW	1 DUP (?)		;RECURSIVE FLAG
VFLAG   DW	1 DUP (?)               ;VERBOSE FLAG
CMDTAIL DB	128 DUP (?)             ;STORAGE FOR COMMAND TAIL
DEST_DTA DB     128 DUP (?)             ;HOLDS INFO ON DEST, RETURNED FROM DOS
                                        ;FN 4E.
NUMARGS DW      1 DUP (?)               ;NUMBER OF ARGUMENTS ON COMMAND LINE
ARGPTRS DW      MAXARGS DUP (?)         ;POINTERS TO START OF ARGUMENTS
BUFFER  DB      BUFSIZE DUP (?)         ;FILE XFER BUFFER
RSTACK  MVINFO  MAXLEVEL DUP (<>)	;RECURSION STACK

        PAGE +
	.CODE

EXTRN   CPYCNT:NEAR,UC:NEAR,UCSTR:NEAR,CRLF:NEAR,ERRORMSG:NEAR,COUT:NEAR
EXTRN   CIN:NEAR,CLRCO:NEAR,SKIPSP:NEAR,TYPTX:NEAR,ERRORMSG:NEAR,CLRCO:NEAR
EXTRN   CMDSRC:NEAR,FIXPATH:NEAR,GETYORN:NEAR,UNLINK2:NEAR
EXTRN   TYPE_DIR:ABS,TYPE_AFN:ABS,TYPE_UFN:ABS,TYPE_DSP:ABS,TYPE_DRV:ABS
EXTRN   TYPE_UNK:ABS

;*********************
;* MACRO DEFINITIONS *
;*********************

ADDR    MACRO   REG,OFFST               ;COMPUTE ADDRESS OFFSET FROM BASE REGISTER (BX)
        MOV     REG,BX
        ADD     REG,OFFST
        ENDM

ERROR   MACRO   ERRNUM                  ;SHOW ERROR MESSAGE
        MOV     DX,OFFSET DGROUP:MSG&ERRNUM
	CALL	PRTERR
        ENDM

COPYPATH MACRO  SOURCE,DEST             ;COPY PATHNAME FROM SOURCE TO DEST
        ADDR    SI,SOURCE
        ADDR    DI,DEST
        MOV     CX,PATHSIZE
        CALL    CPYCNT
        ENDM

;**********************
; PROGRAM ENTRY POINT *
;**********************
ENTRY:
        TEST_DOS2                       ;TEST FOR DOS 2.0, EXIT IF DOS 1
	MOV	AX,DGROUP
	MOV	ES,AX
MEMOK:
        MOV     BX,80H		      	;POINT TO BYTE COUNT FOR COMMAND LINE
        CMP     [BX],BYTE PTR 0
        JE      USEMSG                  ;IF NO COMMAND TAIL
        PUSH    BX
        MOV     DL,[BX]
        MOV     DH,0
        ADD     BX,DX
        INC     BX
        MOV     [BX],BYTE PTR 0         ;PUT 0 BYTE AT END OF COMMAND LINE
        POP     BX
        INC     BX
        CALL    UCSTR                   ;CONVERT COMMAND LINE TO UPPER CASE
        CALL    SKIPSP                  ;SKIP LEADING SPACES
        JNC     L_2                     ;IF SOMETHING ON LINE
USEMSG: ERROR   1
	MOV	AL,1			;SET ERROR CODE
        JMP     EXIT2
L_2:
        MOV     SI,BX
        MOV     DI,OFFSET ES:CMDTAIL
        MOV     CX,128
        CALL    CPYCNT                  ;SAVE COMMAND TAIL
        MOV     AX,ES
        MOV     DS,AX                   ;SET DATA SEG TO VARIABLE AREA
        MOV     BYTE PTR DS:IFLAG,CONFIRM  ;SET CONFIRM FLAG DEFAULT
        MOV     BYTE PTR DS:VFLAG,VERBOSE  ;SET VERBOSE FLAG DEFAULT
	MOV	BYTE PTR DS:RFLAG,FALSE	;SET RECURSIVE FLAG DEFAULT
	MOV	WORD PTR DS:LEVEL,0	;SET RECURSION LEVEL = 0
        MOV     BYTE PTR DS:SRCDRIVE,0  ;SET SOURCE DRIVE DEFAULT
        MOV     BYTE PTR DS:DESTDRIVE,0 ;SET DEST DRIVE DEFAULT
;***************************
; COLLECT SWITCHES, IF ANY *
;***************************
        MOV     BX,OFFSET CMDTAIL       ;POINT TO COMMAND TAIL
NEXTSW:
        CALL    SKIPSP
        JB      USEMSG                  ;IF NOTHING ON LINE BESIDES SWITCHES
        CMP     BYTE PTR [BX],'/'       ;SWITCH SPECIFIED?
        JE	GOTSW			;YES
	CMP	BYTE PTR [BX],'-'	;CHECK FOR HYPHEN, TOO
	JNE	NOSWITCH
GOTSW:
        INC     BX                      ;SKIP OVER SWITCH CHARACTER
        CALL    SKIPSP                  ;SKIP LEADING BLANKS
        JB      USEMSG                  ;IF NOTHING
SWLOOP:
        CALL    UC                      ;MAKE SWITCH UPPER CASE
	PUSH	DS
	PUSH	BX
	PUSH	AX
	MOV	AX,DGROUP
	MOV	DS,AX
	POP	AX
	MOV	BX,OFFSET SWLIST
	CALL	CMDSRC			;SEARCH LIST OF VALID SWITCHES
	POP	BX
	POP	DS
	MOV	AH,0
	ADD	AX,AX			;NOT FOUND IN LIST?
	JZ	BADSWITCH		;NO.
	MOV	SI,OFFSET IFLAG - 2
	ADD	SI,AX			;POINT TO FLAG
	NOT	WORD PTR [SI]		;1'S COMPLEMENT
	INC	BX
	MOV	AL,BYTE PTR [BX]	;GET NEXT CHAR. FROM LINE
	CMP	AL,SPACE
	JE	NEXTSW			;IF SPACE, LOOK FOR - OR FILENAME
	CMP	AL,TAB
	JE	NEXTSW			;IF TAB, LOOK FOR - OR FILENAME
	JMP	SWLOOP			;ELSE ASSUME THIS IS A SWITCH CHAR.
BADSWITCH:
	MOV	AL,BYTE PTR [BX]
        CALL    COUT                    ;DISPLAY BAD CHAR.
        ERROR   16                      ;ILLEGAL SWITCH
	MOV	AL,16			;SET ERROR CODE
        JMP     EXIT2
;************************************************************
; SCAN THE COMMAND LINE AND STORE POINTERS TO ALL ARGUMENTS *
;************************************************************
NOSWITCH:
        MOV     CX,0                    ;CX COUNTS # OF ARGUMENTS
        MOV     SI,OFFSET DS:ARGPTRS    ;POINT TO ARG POINTER TABLE
SAVEARG:
        INC     CX                      ;BUMP ARGUMENT COUNT
        CMP     CX,MAXARGS
        JG      TOOMANY                 ;IF TOO MANY
        MOV     DS:[SI],BX              ;SAVE POINTER TO ARG
        ADD     SI,2                    ;ADVANCE TO NEXT TABLE ENTRY
CNTARGS:
        MOV     AL,DS:[BX]              ;GET CHAR. FROM COMMAND LINE
        CMP     AL,0
        JE      ENDLINE                 ;IF END OF LINE
        COMPLIST <SPACE,TAB>,NEXTARG    ;SEE IF SPACE OR TAB
        INC     BX
        JMP     CNTARGS                 ;LOOP TILL DELIMITER FOUND
NEXTARG:
        CALL    SKIPSP                  ;SKIP SPACES AND TABS
        JNB     SAVEARG                 ;IF NOT EOL, BACK TO TOP OF LOOP
ENDLINE:
        CMP     CX,0                    ;CHECK ARGUMENT COUNT
        JE      NOARGS                  ;IF NO ARGS
        CMP     CX,1
        JG      ARGSOK                  ;IF >=2 ARGS
        MOV     DS:[SI],BX              ;EXACTLY ONE ARGUMENT, SO
        INC     CX                      ;MAKE A DUMMY (NULL) 2ND ARGUMENT
        JMP     SHORT ARGSOK
NOARGS:
        JMP     USEMSG                  ;ELSE USE MESSAGE
TOOMANY:
        ERROR   18                      ;TOO MANY ARGUMENTS (PRETTY UNLIKELY)
	MOV	AL,18			;SET ERROR CODE
        JMP     EXIT2
;************************************
;* COLLECT THE DESTINATION PATHNAME *
;************************************
ARGSOK: MOV     WORD PTR DS:NUMARGS,CX  ;SAVE NUMBER OF ARGUMENTS
        DEC     CX                      ;NUMBER OF ARGS - 1
        ADD     CX,CX                   ;TIMES TWO
        MOV     SI,OFFSET DS:ARGPTRS
        ADD     SI,CX                   ;POINT TO ADDR. OF LAST ARGUMENT (DEST)
        MOV     BX,DS:[SI]              ;PUT ADDR. INTO BX
        MOV     DI,OFFSET RSTACK.ARG2   ;POINT TO STORAGE FOR DEST
        MOV     AL,[BX+1]
        CMP     AL,':'                  ;CHECK FOR DEST DRIVE SPEC
        JNE     GETLAST                 ;IF NONE
        MOV     AL,[BX]                 ;DRIVE SPECIFIED, GET DRIVE LETTER
        SUB     AL,'A'-1                ;MAKE BINARY
        MOV     BYTE PTR DS:DESTDRIVE,AL   ;SAVE DRIVE
GETLAST: MOV    AL,[BX]                 ;GET A CHAR. FROM CMD LINE
        COMPLIST <0,SPACE,TAB>,ENDARG2  ;IF DELIMITER
        STOSB                           ;NOT DELIMITER, STORE IT
        INC     BX                      ;BUMP POINTER
        JMP     GETLAST                 ;LOOP
ENDARG2:
        MOV     AL,0
        STOSB                           ;END DEST W. 0
;***********************************************************
;* LOOP, TAKING ONE SOURCE AT A TIME AND MOVING IT TO DEST *
;***********************************************************
        MOV     CX,WORD PTR DS:NUMARGS  ;GET NUMBER OF ARGS AGAIN
        DEC     CX                      ;-1
        MOV     SI,OFFSET DS:ARGPTRS    ;POINT TO POINTER TABLE
MOVE1ARG:
        MOV     BX,DS:[SI]              ;GET POINTER TO ARG
        PUSH    SI
        PUSH    CX
        CALL    MOVEARG                 ;MOVE IT
        POP     CX
        POP     SI
        ADD     SI,2                    ;ADVANCE TO NEXT ARG POINTER
        LOOP    MOVE1ARG                ;LOOP TILL NO MORE ARGS TO MOVE
	MOV	AL,0			;ERROR CODE 0 MEANS OK
        JMP     EXIT2

;**********************************
;* MOVE 1 SOURCE ARGUMENT TO DEST *
;**********************************
MOVEARG PROC   NEAR
        MOV     BYTE PTR DS:SRCDRIVE,0  ;SET SOURCE DRIVE DEFAULT
        MOV     DI,OFFSET RSTACK.ARG1   ;COLLECT SOURCE
        MOV     AL,[BX+1]
        CMP     AL,':'                  ;CHECK FOR SOURCE DRIVE SPEC
        JNE     GETARG
        MOV     AL,[BX]                 ;GET DRIVE LETTER
        SUB     AL,'A'-1                ;MAKE BINARY
        MOV     BYTE PTR DS:SRCDRIVE,AL ;SAVE SOURCE DRIVE
GETARG: MOV     AL,[BX]                 ;COPY FROM [BX] TO [DI] UNTIL DELIMITER
        COMPLIST <0,SPACE,TAB>,ENDARG
        STOSB
        INC     BX
        JMP     GETARG
ENDARG: MOV     AL,0
        STOSB                           ;END ARG1 W. ZERO
        MOV     WORD PTR DS:RBASE,OFFSET RSTACK   ;SAVE OFFSET TO RSTACK
        CALL    MOVEIT                  ;DO THE STUFF
        RET
MOVEARG ENDP

        PAGE +
;********************************************************************
; THIS IS THE MOVE SUBROUTINE. IT ASSUMES THAT ARG1 AND ARG2 ARE
; SET UP PROPERLY IN THE RECURSION STACK AREA, AND THAT "RBASE" POINTS
; TO THE START OF THE DATA STRUCTURE FOR THIS RECURSION LEVEL.
;
MOVEIT  PROC    NEAR
        MOV     BX,WORD PTR DS:RBASE    ;GET BASE ADDR. (POINTER TO MVINFO)
        CALL    SETUP                   ;PARSE ARGUMENTS, CHECK FOR ERRORS
        JNC     NOARGERR                ;IF OK
        JMP     EXIT2                   ;QUIT IF ERROR
;******************************************
; SEE IF ANYTHING MATCHES THE SOURCE SPEC *
;******************************************
NOARGERR:
        ADDR    DX,SSPATH               ;POINT TO SEARCH PATH
        MOV     CX,31H                  ;SET SEARCH ATTRIBUTES
        MOV     AH,FIND_FIRST
        INT     DOS                     ;SEARCH FOR 1ST MATCH
        JNC     SRCOK                   ;OK IF SOMETHING FOUND
        ADDR    DX,ARG1
        CALL    ERRORMSG                ;ELSE SHOW ARGUMENT
        ERROR   2                       ;SAY IT DOESN'T EXIST
	MOV	AL,2			;SET ERROR CODE
        JMP     EXIT2
;**************************************
; CREATE DEST DIRECTORY, IF NECESSARY *
;**************************************
SRCOK:  CMP     WORD PTR [BX].NEWDIR,1  ;CHECK NEW DIRECTORY FLAG
        JNE     GOTONE                  ;IF NOT SET
        ADDR    DX,ARG2A                ;POINT TO DEST NAME
        MOV     AH,MKDIR
        INT     DOS                     ;CREATE NEW DIRECTORY
        JNB     GOTONE                  ;IF OK
        ADDR    DX,ARG2A                ;IF CAN'T CREATE,
        CALL    ERRORMSG                ;SHOW DIRECTORY NAME
        ERROR   12                      ;AND ERROR MSG.
	MOV	AL,12			;SET ERROR CODE
        JMP     EXIT2                   ;EXIT 2 DOS
;**************
; TOP OF LOOP *
;**************
GOTONE:
        CALL    BUILD_NAMES             ;MAKE UNAMBIG. SOURCE AND DEST NAMES
        JNC     NOSKIP
        JMP     NEXTFILE                ;IF SOURCE IS "." OR ".." OR = DEST
NOSKIP:
        CMP     BYTE PTR DS:VFLAG,TRUE  ;CHECK VERBOSE FLAG
        JNE     QUIET                   ;IF QUIET MODE
        ADDR    DX,SRCNAME              ;NOT QUIET, SHOW VERBIAGE
        CALL    ERRORMSG
        CALL    TYPTX
        DB      ' -->',SPACE+200Q
        ADDR    DX,DESTNAME
        CALL    ERRORMSG
        CALL    CRLF
QUIET:
        TEST    [BX].DTA+21,BIT$DIR     ;IS SOURCE A DIRECTORY?
        JZ      CHKDRIVES               ;NO
;***********************************************
; IF SOURCE IS A DIRECTORY, RECURSIVELY MOVE IT*
;***********************************************
	CMP	BYTE PTR DS:RFLAG,TRUE	;R FLAG SET?
	JNE	NEXTFILE		;NO, SKIP OVER DIRECTORY
        CMP     WORD PTR DS:LEVEL,MAXLEVEL-1 ;ARE WE AT MAX LEVEL?
        JL      NOTMAX                  ;NO
        ERROR   14                      ;YES, TOO DEEP
	MOV	AL,14			;SET ERROR CODE
        JMP     EXIT2
NOTMAX:
        COPYPATH SRCNAME,(ARG1+MVSTRUCSIZE)  ;COPY SOURCE DIRECTORY TO ARG1 AT NEXT LEVEL
        COPYPATH DESTNAME,(ARG2+MVSTRUCSIZE) ;COPY DEST NAME TO ARG2 AT NEXT LEVEL
        PUSH    BX                      ;SAVE POINTER TO BASE
        INC     WORD PTR DS:LEVEL       ;INCREMENT RECURSION LEVEL
        ADD     WORD PTR DS:RBASE,MVSTRUCSIZE ;ADVANCE BASE POINTER
        CALL    MOVEIT                  ;MOVE THE DIRECTORY
        POP     BX                      ;RESTORE BASE POINTER
        DEC     WORD PTR DS:LEVEL       ;DECREMENT RECURSION LEVEL
        SUB     WORD PTR DS:RBASE,MVSTRUCSIZE  ;RESTORE BASE TO PREVIOUS LEVEL
        ADDR    DX,DTA
        MOV     AH,SET_DTA
        INT     DOS                     ;RESET DTA
        JMP     SHORT NEXTFILE          ;DO NEXT FILE
;*****************************************
; IF SOURCE IS A FILE, COPY OR RENAME IT *
;*****************************************
CHKDRIVES:
        MOV     AL,BYTE PTR DS:SRCDRIVE    ;GET SOURCE DRIVE
        CMP     AL,BYTE PTR DS:DESTDRIVE   ;COMPARE W. DEST..
        JNE     MOVEM
        CALL    RENAME_FILE             ;SAME DRIVE, JUST RENAME
	JNB	NEXTFILE
	JMP	EXIT2			;ERROR, EXIT TO DOS
MOVEM:  CALL    COPY_FILE               ;DIFFERENT DRIVE, COPY SOURCE TO DEST
	JNB	NEXTFILE
	JMP	EXIT2			;ERROR, EXIT TO DOS
;*****************
; BOTTOM OF LOOP *
;*****************
NEXTFILE:
        MOV     AH,FIND_NEXT
        INT     DOS                     ;FIND NEXT MATCH, IF ANY
        JC      NOMORE                  ;IF NONE
        JMP     GOTONE                  ;GOT ONE, BACK TO TOP OF LOOP
;**********************
; NO MORE FILES, DONE *
; *********************
NOMORE: MOV     AX,WORD PTR [BX].SRCTYPE    ;GET TYPE OF SOURCE
        CMP     AX,TYPE_DIR             ;DIRECTORY?
        JNE     JUSTCLOSE               ;NO
        ADDR    DX,ARG1                 ;YES, POINT TO NAME
        MOV     AH,RMDIR
        INT     DOS                     ;REMOVE IT
        JNB     JUSTCLOSE
        ADDR    DX,ARG1
        CALL    ERRORMSG
        ERROR   13                      ;COMPLAIN IF ERROR
        CALL    CLOSE2			;CLOSE OUTPUT FILE
	MOV	AL,13			;SET ERROR CODE
	JMP	EXIT2			;EXIT TO DOS

;**************
; NORMAL EXIT *
;**************
JUSTCLOSE:
	CALL	CLOSE2			;CLOSE OUTPUT FILE
	CALL	CLOSE1			;CLOSE INPUT FILE
        RET                             ;ALL DONE
MOVEIT  ENDP

        PAGE +
;*************************************************************************
; THIS PROCEDURE PARSES ARG1 AND ARG2 AND CHECKS FOR INCOMPATIBLE TYPES
; (E.G. "MV FOOBAR *.*").  IF SUCCESSFUL, IT SETS SSPATH = SOURCE SEARCH
; PATH, SPREFIX = SOURCE PREFIX, DSPATH = DEST SEARCH PATH (NOT USED),
; DPREFIX = DEST PREFIX, SRCTYPE = SOURCE TYPE DESTTYPE = DESTINATION TYPE.
; IT ALSO SETS "NEWDIR"=1 IF THE DESTINATION DIRECTORY MUST BE CREATED.
; ALL OF THESE VARIABLES ARE IN A STRUCTURE OF TYPE 'MVINFO' IN THE RECURSION
; STACK AREA (STARTING AT THE ADDRESS IN 'RBASE').
;
; ON RETURN, THE CARRY FLAG IS SET IF AN ERROR OCCURRED, AND AL HOLDS AN
; ERROR CODE KEYED TO THE ERROR MESSAGE NUMBER.
;
SETUP   PROC    NEAR
        MOV     BX,WORD PTR DS:RBASE    ;GET ADDR. OF DATA STRUCTURE
        MOV     WORD PTR [BX].NEWDIR,0  ;CLEAR "NEW DIRECTORY" FLAG
        ADDR    DX,DTA
        MOV     AH,SET_DTA
        INT     DOS                     ;SET DTA
        ADDR    SI,ARG1
        ADDR    DI,ARG2
        COMP_STRINGS                    ;COMPARE ARG1 AND ARG2
        JNE     NOTIDENT                ;IF NOT IDENTICAL
        ERROR   17                      ;ARG1 = ARG2, BAD NEWS
	MOV	AL,17
        STC
        RET
NOTIDENT:
        ADDR    CX,SSPATH
        ADDR    DX,SPREFIX
        PUSH    BX
        ADD     BX,ARG1
        CALL    FIXPATH                 ;PARSE SOURCE PATHNAME
        POP     BX
        CMP     AX,TYPE_UNK
        JNE     GOODPATH                ;IF APPARENTLY OK
        ADDR    DX,ARG1
        CALL    ERRORMSG
        ERROR   2                       ;NONEXISTENT PATHNAME, COMPLAIN
	MOV	AL,2
        STC
        RET
GOODPATH:
        MOV     WORD PTR [BX].SRCTYPE,AX     ;SAVE TYPE OF SOURCE
	CMP	AX,TYPE_DIR		     ;IS IT A DIRECTORY?
	JNE	RSET			     ;NO.
	CMP	BYTE PTR DS:RFLAG,TRUE	     ;R FLAG SET?
	JE	RSET			;IF OK
	ADDR	DX,ARG1			;SHOW NAME
	CALL	ERRORMSG
	ERROR	7			;COMPLAIN ABOUT DIRECTORY
	MOV	AL,7
	STC				;SIGNAL ERROR
	RET
RSET:
        ADDR    SI,ARG2			;GET POINTER TO DEST
PDEST:  PUSH	SI			;SAVE IT ON STACK
	MOV	DI, SI			;AND IN DI
SRCBS:  MOV     AX,[SI]                 ;FIND END OF DEST
        COMPLIST <0,SPACE,TAB>,GOTEND
        INC     SI
        JMP     SRCBS
GOTEND:
        DEC     SI
        CMP     [SI],BYTE PTR '\'       ;DOES DEST SPEC END W. BACKSLASH?
        JNE     NOBS                    ;J/NO, IT DOESN'T
	CMP	SI, DI			;IS DEST="\"?
	JE	NOBS			;J/YES, ROOT DIRECTORY, LEAVE IT ALONE
	CMP	BYTE PTR [SI-1],':'	;IS PREVIOUS CHAR ':'?
	JE	NOBS			;J/YES, ROOT DIRECTORY, LEAVE IT ALONE
        MOV     [SI],BYTE PTR 0         ;\ AT END AND NOT ROOT DIR, REMOVE IT
NOBS:   COPYPATH ARG2,ARG2A             ;MAKE ARG2A = ARG2
        ADDR    CX, DSPATH
        ADDR    DX, DPREFIX
	POP	SI			;GET POINTER TO ARG2 AGAIN
        PUSH    BX
        MOV     BX, SI
        CALL    FIXPATH                 ;PARSE DEST PATHNAME
        POP     BX
        MOV     WORD PTR [BX].DESTTYPE,AX       ;SAVE DEST TYPE
        CMP     AL,TYPE_AFN
        JE      BADDEST                 ;DEST CAN'T BE AFN
        CMP     AL,TYPE_UFN
        JNE     NOTUFN                  ;IF DEST TYPE IS UFN,
        CMP     WORD PTR DS:NUMARGS,2   ;BETTER HAVE ONLY 1 SOURCE
        JG      BADDEST
        CMP     WORD PTR DS:SRCTYPE,TYPE_AFN  ;AND SOURCE TYPE BETTER NOT BE AFN
        JNE     NOTUFN
BADDEST:
        ERROR   3
	MOV	AL,3
        STC
        RET
NOTUFN:
        COMPLIST <TYPE_DSP,TYPE_DRV,TYPE_DIR>,DESTDSP ;IF DEST IS DIR OR DRIVE
        CMP     AL,TYPE_UNK
        JNE     SHORT DESTOK            ;IF DEST EXISTS

DESTNX:
        MOV     AX,WORD PTR [BX].SRCTYPE  ;DEST DOESN'T EXIST
        CMP     AL,TYPE_DIR             ;IF SOURCE IS DIRECTORY,
        JE      DESTDIR			;MAKE DEST A DIRECTORY, TOO
	CMP	AL,TYPE_AFN 		;SOURCE NOT A DIRECTORY
	JE	BADDEST			;AFN IS BAD (E.G. MV *.* FOO)
	JMP	SHORT DESTOK		;OTHERWISE OK
DESTDIR:
        CALL    SETUP_DEST_DIR          ;MAKE DEST A DIRECTORY, TOO
        JMP     SHORT DESTOK
DESTDSP: MOV    AX,WORD PTR [BX].SRCTYPE  ;DEST IS DIRECTORY, DIR SPEC OR DRIVE
        CMP     AL,TYPE_DIR             ;IS SOURCE A DIRECTORY? -
        JNE     DESTOK                  ;- NO
        CALL    SETUP_SRC_DIR           ;- YES, SOURCE IS DIRECTORY, SET IT UP
        JB      ERRRET                  ;IF ERROR
        MOV     AX,WORD PTR [BX].DESTTYPE
        CMP     AX,TYPE_DIR             ;IS DEST A DIRECTORY? -
        JNE     DESTOK                  ;- NO
        CALL    SETUP_DEST_DIR          ;- YES, SET IT UP, TOO
;*******************************
; CHECK FOR INCOMPATIBLE TYPES *
;*******************************
DESTOK:
        MOV     CX,WORD PTR [BX].DESTTYPE    ;GET DEST TYPE
        MOV     AX,WORD PTR [BX].SRCTYPE     ;GET SOURCE TYPE
        CMP     AX,TYPE_DIR             ;IS SOURCE A DIRECTORY? -
        JNE     TYPEOK                  ;- NO, IT ISN'T
        CMP     CX,TYPE_DIR             ;- YES, SOURCE IS DIRECTORY,
        JE      TYPEOK                  ;DEST BETTER BE DIRECTORY
        CMP     CX,TYPE_DRV             ;OR DRIVE SPEC
        JE      TYPEOK
ERRRET:
        ERROR   3                       ;ELSE INCOMPATIBLE TYPE ERROR
	MOV	AL,3
	STC                             ;ERROR RETURN
        RET
TYPEOK: CLC                             ;NORMAL RETURN
        RET
SETUP   ENDP

;**********************************************
; MOVE IS FROM DIR TO DIR, DIR SPEC OR DRIVE
; BUILD FULL DEST PATHNAME (IN ARG2A) AND CHECK
; ITS TYPE.  (SETS CARRY ON ERROR)
;
SETUP_SRC_DIR PROC NEAR
        COPYPATH ARG2,DSPATH            ;COPY DEST DIRECTORY OR DRIVE SPEC TO
                                        ;DEST PATH (USE AS SCRATCH AREA)
        DEC     DI                      ;BACK UP OVER NULL
        MOV     AX,WORD PTR [BX].DESTTYPE
        CMP     AX,TYPE_DIR             ;SEE IF DEST IS DIRECTORY
        JNE     NOTDIR                  ;NOPE
        MOV     BYTE PTR [DI],'\'       ;YES, ADD BACKSLASH TO DEST NAME
        INC     DI
        DEC     CX
NOTDIR: ADDR    SI,ARG1                 ;POINT TO SOURCE DIRECTORY NAME
        CMP     BYTE PTR [SI+1],':'     ;DRIVE SPECIFIED? -
        JNE     NODRV                   ;- NO IT WASN'T
        ADD     SI,2                    ;- YES, IT WAS, SKIP IT
NODRV:
;	skip to the last element in the source path
	MOV	DX, SI			;SAVE START OF SOURCE IN DX
STRIP_PATH:
	CMP	BYTE PTR [SI],0		;END OF SOURCE?
	JE	END_OF_SRC		;J/YES
	CMP	BYTE PTR [SI],'\'	;BACKSLASH IN SOURCE?
	JNE	NEXT_CHAR		;J/NO, KEEP SEARCHING
	MOV	DX, SI			;SAVE LOCATION OF BACKSLASH
NEXT_CHAR:
	INC	SI
	JMP	STRIP_PATH
END_OF_SRC:
	MOV	SI, DX			;POINT TO LAST \ OR START OF STRING
	CMP	BYTE PTR [SI],'\'	;IS NEXT CHAR. \?
	JNE	NOBKSL			;NO
	INC	SI			;YES, SKIP PAST IT
NOBKSL:
        CALL    CPYCNT                  ;COPY LAST PART OF SOURCE DIRECTORY
					;NAME TO DEST PATH
        COPYPATH DSPATH,ARG2A           ;COPY AUGMENTED DEST PATH TO ARG2A
        ADDR    CX,DSPATH
        ADDR    DX,DPREFIX
        PUSH    BX
        ADD     BX,ARG2A
        CALL    FIXPATH                 ;PARSE FULL DEST PATHNAME
        POP     BX
        MOV     WORD PTR [BX].DESTTYPE,AX    ;SAVE DEST TYPE
        CMP     AL,TYPE_UNK
        JNE     DESTXST                 ;IF DEST EXISTS
        MOV     AX,WORD PTR [BX].SRCTYPE  ;DEST DOESN'T EXIST, GET SOURCE TYPE
        CMP     AX,TYPE_DIR             ;IF SOURCE IS A DIRECTORY,
        JNE     DESTXST
        MOV     [BX].DESTTYPE,TYPE_DIR  ;MAKE DEST A DIRECTORY, TOO
DESTXST:
        CLC
        RET
SETUP_SRC_DIR ENDP

;***************************************************************
; THIS PROCEDURE IS CALLED IF THE DESTINATION IS A NEW DIRECTORY
;
SETUP_DEST_DIR PROC NEAR
        ADDR    SI,DSPATH
        ADDR    DI,DPREFIX
        PUSH    DI
        MOV     CX,PATHSIZE
        CALL    CPYCNT                  ;COPY SEARCH PATH TO PREFIX
        POP     DI
        MOV     CX,PATHSIZE
        MOV     AL,0
        REPNE   SCASB                   ;FIND END OF DEST PREFIX
        DEC     DI
        MOV     [DI],BYTE PTR '\'       ;ADD BACKSLASH
        MOV     [DI+1],BYTE PTR 0
        MOV     [BX].NEWDIR,WORD PTR 1  ;SET "NEW DIRECTORY FLAG"
        MOV     WORD PTR [BX].DESTTYPE,TYPE_DIR ;SET DEST TYPE = DIRECTORY
        RET
SETUP_DEST_DIR ENDP

        PAGE +
;****************************************************************************
; THIS ROUTINE TAKES THE INFO ON A FILE IN THE DTA AND USES IT TO GENERATE
; FULL, UNAMBIGUOUS SOURCE AND DESTINATION FILE NAMES BY ADDING THE NECESSARY
; DIRECTORY PREFIXES TO IT.  IT SETS THE CARRY FLAG IF THE "FILE"
; IS "." OR "..", OR IF THE SOURCE IS IDENTICAL TO THE DESTINATION.
;
BUILD_NAMES  PROC  NEAR
        COPYPATH SPREFIX,SRCNAME        ;GET SOURCE PREFIX, COPY TO SOURCE NAME
        DEC     DI                      ;BACK UP OVER NULL
        ADDR    SI,(DTA+30)             ;POINT TO FILE NAME WE FOUND
        CMP     [SI],BYTE PTR '.'       ;DOES IT START WITH .? -
        JNE     NOTDOT                  ;- NO
        STC                             ;- YES, SET CARRY TO SKIP IT
        RET
NOTDOT:
        CALL    CPYCNT                  ;ADD IT TO SOURCE PREFIX
        CMP     WORD PTR [BX].DESTTYPE,TYPE_UFN  ;IS DEST A UFN?
        JE      DESTUFN                 ;YES, JUST USE IT
        CMP     WORD PTR [BX].DESTTYPE,TYPE_UNK
        JE      DESTUFN                 ;DITTO IF DEST DOESN'T EXIST
                                        ;IF NOT, MAKE DEST NAME = SOURCE NAME
        COPYPATH DPREFIX,DESTNAME       ;COPY DEST PREFIX TO DEST NAME
        DEC     DI                      ;BACK UP OVER NULL
        ADDR    SI,DTA+30               ;POINT TO FILE NAME WE FOUND
        CALL    CPYCNT                  ;ADD IT TO DEST PREFIX
        JMP     SHORT DONE              ;GO TO NEXT STEP
DESTUFN:                                ;DEST IS A FILE, NOT A DIRECTORY
        COPYPATH ARG2,DESTNAME          ;SO DESTNAME JUST = ARG2
DONE:
        CALL    CHECK_NAMES             ;CHECK SOURCE AND DEST FOR IDENTITY
        RET
BUILD_NAMES ENDP

        PAGE +
;*********************************************************************
; THIS PROCEDURE CHECKS THE SOURCE AND DEST FILES, AND SETS THE CARRY
; FLAG IF THEY ARE IDENTICAL
;
CHECK_NAMES PROC NEAR
        MOV     BX,WORD PTR RBASE    	;GET ADDR. OF DATA STRUCTURE
        MOV     AH,SET_DTA
        MOV     DX,OFFSET DEST_DTA
        INT     DOS                     ;SET DTA
        ADDR    DX,DESTNAME             ;POINT TO DESTINATION
        MOV     CX,0FFH
        MOV     AH,FIND_FIRST
        INT     DOS                     ;SEARCH FOR IT
        PUSHF
        ADDR    DX,DTA
        MOV     AH,SET_DTA
        INT     DOS                     ;RESET DTA
        POPF
        JNB     GOTDEST                 ;IF FOUND IT
        CLC                             ;OK IF IT DOESN'T EXIST
        RET
GOTDEST: ADDR   SI,DTA+21               ;POINT TO INFO ON SOURCE
        MOV     DI,OFFSET DEST_DTA+21   ;POINT TO INFO ON DEST
        MOV     CX,9                    ;SIZE OF FILE ATTRIBUTES
        REPE    CMPSB                   ;COMPARE SOURCE AND DEST
        JNE     NOTEQU                  ;IF NOT =
        COMP_STRINGS                    ;COMPARE STRINGS
        JNE     NOTEQU                  ;IF SOURCE AND DEST NAMES NOT EQUAL
        STC                             ;FILES ARE EQUAL (NAMES AND ATTRIB.)
        RET
NOTEQU: CLC
        RET
CHECK_NAMES ENDP
        PAGE +
;**************************************************************************
; THIS ROUTINE COPIES A SINGLE FILE (SRCNAME) TO ITS DESTINATION (DESTNAME)
; ON ANOTHER DISK.
;
; IT SETS THE CARRY FLAG IF AN ERROR OCCURRED.
;
COPY_FILE PROC  NEAR
        ADDR    DX,SRCNAME              ;DX = SOURCE FILE NAME
        MOV     AL,READ_ACCESS
        MOV     AH,DOS2_OPEN
        INT     DOS                     ;TRY TO OPEN SOURCE FILE
        JC      FAIL                    ;IF CAN'T
        MOV     WORD PTR INHANDLE,AX    ;SAVE FILE HANDLE
        CALL    GETOK                   ;GET CONFIRMATION IF DEST EXISTS
        JNC     OKGIVEN                 ;IF OK TO DELETE
        MOV     BX,WORD PTR INHANDLE 	;IF NOT OK, GET FILE HANDLE
        MOV     AH,DOS2_CLOSE
        INT     DOS                     ;CLOSE INPUT FILE
	CLC
        RET                             ;EXIT W/O MOVING
FAIL:   ADDR    DX,SRCNAME              ;POINT TO SOURCE FILE NAME
        CALL    ERRORMSG                ;SHOW IT
        ERROR   5                       ;COMPLAIN THAT CAN'T OPEN
	MOV	AL,5
	STC
	RET
OKGIVEN: ADDR   DX,DESTNAME
        MOV     CL,BYTE PTR [BX].DTA+21  ;MAKE DEST ATTRIBUTES=SOURCE ATTRIB.
        MOV     CH,0
        MOV     AH,DOS2_CREATE
        INT     DOS                     ;CREATE FILE (OR TRUNCATE OLD ONE)
        JNC     CREATED                 ;IF OK
        ADDR    DX,DESTNAME
        CALL    ERRORMSG                ;CREATE FAILED, SHOW FILE NAME
        ERROR   5                       ;COMPLAIN THAT CAN'T OPEN
	MOV	AL,5
	STC
	RET
CREATED:
        MOV     WORD PTR OUTHANDLE,AX   ;SAVE OUTPUT HANDLE
COPYLOOP:
        PUSH    BX
        MOV     BX,WORD PTR INHANDLE 	;BX = FILE HANDLE
        MOV     DX,OFFSET BUFFER        ;DX = BUFFER OFFSET
        MOV     CX,BUFSIZE              ;CX = BUFFER SIZE
        MOV     AH,READ                 ;USING DOS 2 CALL,
        INT     DOS                     ;READ FROM SOURCE
        POP     BX
        JC      RDERR                   ;IF ERROR
        MOV     CX,AX                   ;SAVE AMT. READ
        CMP     AX,0
        JE      ENDCOPY                 ;IF 0
        PUSH    BX
        MOV     BX,WORD PTR OUTHANDLE 	;BX = FILE HANDLE
        MOV     DX,OFFSET BUFFER        ;DX = BUFFER OFFSET
        MOV     AH,WRITE
        INT     DOS                     ;WRITE BUFFER TO DEST
        POP     BX
        JC      WRERR                   ;IF ERROR
        CMP     AX,CX                   ;WERE ALL BYTES WRITTEN?
        JE      COPYLOOP                ;YES, KEEP GOING
	ADDR	DX,DESTNAME		;POINT TO DEST NAME
	CALL	ERRORMSG		;SHOW IT
        ERROR   10                      ;PROBABLY DISK FULL
	MOV	AL,10			;SET ERROR CODE
	JMP     SCRATCH			;ABORT
WRERR:  ADDR	DX,DESTNAME		;POINT TO DEST NAME
	CALL	ERRORMSG		;SHOW IT
	ERROR   8                       ;WRITE ERROR
	MOV	AL,8			;SET ERROR CODE
        JMP     SCRATCH                 ;ABORT
RDERR:  ADDR	DX,SRCNAME		;POINT TO SOURCE NAME
	CALL	ERRORMSG		;SHOW IT
	ERROR   9                       ;READ ERROR
	MOV	AL,9			;SET ERROR CODE
        JMP     SCRATCH                 ;ABORT
ENDCOPY: PUSH   BX                      ;NO ERRORS
        MOV     BX,WORD PTR INHANDLE    ;GET INPUT FILE HANDLE
        MOV     AH,FILE_TIMES
        MOV     AL,0                    ;GET DATE AND TIME
        INT     DOS                     ;GET IT
        MOV     BX,WORD PTR OUTHANDLE   ;GET OUTPUT FILE HANDLE
        MOV     AH,FILE_TIMES
        MOV     AL,1
        INT     DOS                     ;SET OUTPUT DATE AND TIME SAME AS INPUT
        MOV     BX,WORD PTR INHANDLE
        MOV     AH,DOS2_CLOSE
        INT     DOS                     ;CLOSE INPUT FILE
        MOV     BX,WORD PTR OUTHANDLE
        MOV     AH,DOS2_CLOSE
        INT     DOS                     ;CLOSE OUTPUT FILE
        POP     BX
        ADDR    DX,SRCNAME              ;POINT TO SOURCE NAME
	CALL	UNLINK2			;REMOVE IT
        JB      DELERR                  ;IF SOME PROBLEM
        RET                             ;ALL DONE
DELERR: ADDR    DX,SRCNAME              ;CAN'T DELETE FILE, SHOW FILE NAME
        CALL    ERRORMSG
        ERROR   13                      ;AND ERROR MSG.
	MOV	AL,13
	STC
        RET

SCRATCH:
	PUSH	AX			;SAVE ERROR CODE
	PUSH	BX
	MOV	BX,WORD PTR OUTHANDLE
	MOV	AH,DOS2_CLOSE
	INT	DOS			;CLOSE OUTPUT FILE
	POP	BX
	ADDR	DX,DESTNAME
	CALL	UNLINK2			;REMOVE PARTIAL FILE
	POP	AX
	STC
	RET

COPY_FILE ENDP

PAGE +
;*******************************************************************
; THIS ROUTINE "MOVES" A FILE (SRCNAME) BY RENAMING IT (TO DESTNAME)
;
; IT SETS THE CARRY FLAG IF AN ERROR OCCURRED.
;
RENAME_FILE PROC NEAR
        CALL    GETOK                   ;SEE IF FILE EXISTS
        JNC     OK                      ;USER SAID OK TO ERASE
	MOV	AL,4
	STC
        RET                             ;NOT OK, SKIP THIS ONE
OK:     ADDR    DX,SRCNAME              ;POINT TO SOURCE
        ADDR    DI,DESTNAME             ;AND DEST
        MOV     AH,DOS2_RENAME
        PUSH    BX
        INT     DOS                     ;RENAME
        POP     BX
        JNC     RENOK                   ;IF OK
        ADDR    DX,SRCNAME
        CALL    ERRORMSG                ;SHOW SOURCE
        ERROR   11                      ;SHOULDN'T HAPPEN
	MOV	AL,11			;SET ERROR CODE
	STC				;SIGNAL ERROR
RENOK:  RET
RENAME_FILE ENDP

        PAGE +
;*******************************************************************
;       THIS ROUTINE CHECKS TO SEE IF THE DEST FILE EXISTS.
;       IF IT DOES, AND IF THE CONFIRM FLAG IS SET, IT ASKS
;       FOR CONFIRMATION BEFORE DELETING THE FILE.  IF THE
;       CONFIRM FLAG IS NOT SET, IT JUST DELETES IT.
;       ON EXIT, 'C'=1 IF THE USER REPLIED 'N' TO THE CONFIRM MSG.
;
GETOK   PROC    NEAR
        ADDR    DX,DESTNAME
        MOV     AL,0
        MOV     AH,CHMOD
        INT     DOS                     ;GET ATTRIBUTES FOR DEST
        JNC     EXISTS                  ;IF FILE EXISTS
        CLC
        RET
EXISTS:
        CMP     BYTE PTR DS:IFLAG,TRUE  ;IS FLAG SET TO CONFIRM?
        JNE     COK                     ;NO.
        ADDR    DX,DESTNAME
        CALL    ERRORMSG                ;SHOW DEST FILE
        ERROR   4                       ;ASK FOR CONFIRMATION
	CALL	GETYORN			;GET Y OR N RESPONSE
        CMP     AL,'Y'                  ;IS IT YES?
        JNE     ABEXIT                  ;NO, SIGNAL CALLING ROUTINE
COK:
TRYAGAIN:
        ADDR    DX,DESTNAME
        MOV     AH,UNLINK
        INT     DOS                     ;DELETE THE FILE
        JNC     OKEXIT                  ;IF NO PROBLEM
        CMP     AX,5                    ;WAS FILE R/O? (THIS OUGHT TO BE
                                        ; THE ONLY POSSIBLE ERROR)
        JNE     OKEXIT                  ;EXIT IF SOME OTHER ERROR
	ADDR	DX,DESTNAME
	CALL	ERRORMSG		;SHOW DEST FILE NAME
        ERROR   6                       ;R/O FILE, QUERY USER AGAIN
	CALL	GETYORN			;GET Y OR N RESPONSE
        CMP     AL,'Y'                  ;TEST FOR 'Y' RESPONSE
        JNE     ABEXIT                  ;ABORT IF USER SAYS 'N'
        MOV     CX,0                    ;'Y' TYPED CLEAR ALL ATTRIBUTES
        ADDR    DX,DESTNAME             ;POINT TO FILE NAME
        MOV     AH,CHMOD
        MOV     AL,1                    ;CHANGE ATTRIBUTES
        INT     DOS                     ;TRY MAKING IT R/W
        JMP     TRYAGAIN                ;TRY TO ERASE IT AGAIN
ABEXIT: STC
        RET
OKEXIT: CLC
        RET
GETOK   ENDP

; PRTERR = PRINT ERROR MESSAGE (DX POINTS TO OFFSET FROM DGROUP)

PRTERR: PUSH    DS
        MOV     AX,DGROUP
        MOV     DS,AX
        CALL    ERRORMSG
        POP     DS
	RET

CLOSE2:  MOV    BX,WORD PTR OUTHANDLE
        MOV     AH,DOS2_CLOSE
        INT     DOS                     ;CLOSE OUTPUT FILE
	RET
CLOSE1:
        MOV     BX,WORD PTR INHANDLE
        MOV     AH,DOS2_CLOSE
        INT     DOS                     ;CLOSE INPUT FILE
	RET
EXIT2:
        MOV     AH,EXIT
        INT     DOS                     ;EXIT TO DOS

        END     ENTRY


RM.ASM

        TITLE RM - MSDOS FILE DELETE UTILITY
        PAGE 55,132
;**********************************************************************
;       THIS PROGRAM DELETES ONE OR MORE FILES, OR (WITH THE /R SWITCH)
;	DIRECTORIES.
;
;       USAGE (afn = ambiguous file name):
;
;               RM afn1 afn2 ...  - deletes the named file(s)
;
;       SWITCHES (OPTIONAL):
;
;		RM/F - remove write-protected files without warning
;
;		RM/I - query before each deletion
;
;               RM/R - allows deleting directories (recursively).
;		       (this is DANGEROUS, especially without the /I switch)
;
;               RM/V - "verbose" mode, shows name of each file deleted.
;************************************************************************
;       BY: JON DART
;           3012 HAWTHORN ST.,
;           SAN DIEGO, CA 92104
;************************************************************************
;   TO BUILD RM.EXE:
;	MASM RM,RM,NUL,NUL
;	LINK RM,RM,NUL,ASM
;	EXEPACK RM.EXE RM2.EXE
;	DEL RM.EXE
;	REN RM2.EXE=RM.EXE
;************************************************************************
;       Version 3.2, 04-Feb-89  fixes to CHECKWILD
;	Version 3.1, 19-Jan-89  another fix to FIXPATH; relinked.
;	Version 3.0, 24-Sep-88  bug fix in FIXPATH; relinked. Version made
;				consistent with MV and CP.
;	Version 1.6, 16-Feb-88  assembles under MASM 5.0, uses GETARGS
;				for argument processing.
;	Version 1.5, 24-Oct-86  switch parser modified, recognizes -rv
;				form as well as -r -v
;	Version 1.4, 10-Sep-86  (fixes prompt for write-protected files)
;	Version 1.3, 19-Jul-86  (fixes gross bugs in wildcard handling)
;	Version 1.2, 06-Jul-86  (changes to memory alloc., accepts hyphen
;				 as switch char., assembles under masm 4.0)
;	Version 1.1, 03-Jul-86  (bug fixes)
;       Version 1.0, 30-Jun-86
;************************************************************************

	DOSSEG
	.MODEL	SMALL

PATHSIZE EQU    65                      ;MAX. SIZE OF DOS PATHNAME, +1
MAXLEVEL EQU    12                      ;MAX NESTING LEVEL FOR DIRECTORIES
MAXARGS EQU     40                      ;MAXIMUM # OF COMMAND LINE ARGUMENTS
FALSE   EQU     0
TRUE    EQU     0FFH
CONFIRM EQU     FALSE                   ;"CONFIRM BEFORE DELETE" DEFAULT
VERBOSE EQU     FALSE                   ;"VERBOSE" OPTION (DEFAULT VALUE)
GOOFPROOF EQU	TRUE			;CONFIRM BEFORE DELETING *.*
BIT$DIR EQU     00010000B               ;BIT IN ATTRIBUTE FOR DIRECTORY
BIT$RO  EQU     00000001B               ;BIT IN ATTRIBUTE FOR WRITE-PROTECT

        .XLIST
        INCLUDE ASCII.DEF
        INCLUDE MSDOS2.DEF
	INCLUDE MACROS.DEF
        .LIST

RMINFO  STRUC
NEWDIR  DW      1 DUP (?)               ;FLAG, =1 IF NEW DIRECTORY CREATED
PATHTYPE DB     2 DUP (?)               ;TYPE OF SOURCE (0 IF UFN)
ARG     DB      PATHSIZE DUP (?)        ;FIRST ARGUMENT (SOURCE)
SPATH   DB      PATHSIZE DUP (?)        ;SOURCE SEARCH PATH
SPREFIX DB      PATHSIZE DUP (?)        ;SOURCE LEAD-IN PATH
SCRATCH DB      PATHSIZE DUP (?)        ;SCRATCH AREA
FULLNAME DB     PATHSIZE DUP (?)        ;UNAMBIGUOUS SOURCE NAME
DTA     DB      128 DUP (?)             ;DTA FOR SEARCH FN.
RMINFO  ENDS
RMSTRUCSIZE EQU 5*PATHSIZE+135          ;SIZE OF STRUCTURE

	.DATA
;
;       FIXED DATA AREA:
;
MSG1    DB 'RM Version 3.2 by Jon Dart (04-Feb-89)',CR,LF,CR,LF
        DB 'USAGE:',CR,LF,CR,LF
        DB 'RM [-F -I -R -V] file1 file2  ...      deletes the named file(s)',CR,LF,CR,LF
        DB 'SWITCHES (OPTIONAL):',CR,LF,CR,LF
	DB '-F   remove write-protected files without warning',CR,LF
	DB CR,LF
        DB '-I   query before delete',CR,LF
        DB CR,LF
        DB '-R   allow recursive deletion of directories',CR,LF
	DB CR,LF
	DB '-V   echo file names as they are deleted',CR,LF
        DB 0
MSG2    DB      ': file not found.',CR,LF,0
MSG3    DB      ': directory.',CR,LF,0
MSG4	DB      ' : OK to delete [Y or N]? ',0
MSG5    DB      ": can't remove.",CR,LF,0
MSG6    DB      ': file is R/O. '
MSG6A	DB	'Are you SURE [Y or N]? ',0
MSG7    DB      "Directories nested too deep.",CR,LF,0
; MSG8    DB      "Insufficient memory for buffers.",CR,LF,0
MSG9    DB      ": Illegal switch.",CR,LF,0
MSG10   DB      "Too many arguments.",CR,LF,0
MSG11	DB	' directory. Enter it [Y or N]? ',0
MSG12	DB	' directory. Delete it [Y or N]? ',0
MSG13	DB	'? ',0
WILD	DB	'*.*',0
SWLIST  DB	'FIRV',0		;LIST OF LEGAL SWITCHES

	.STACK
        DB      512 DUP (?)

	.DATA?
;	UNINITIALIZED DATA AND BUFFER AREAS

RBASE   DW 	1 DUP (?)               ;POINTER TO MVSTRUC IN RSTACK
LEVEL   DW      1 DUP (?)               ;RECURSION LEVEL
DRIVE   DW      1 DUP (?)               ;SOURCE DRIVE (0 = DEFAULT)
FFLAG   DW	1 DUP (?)               ;FORCE FLAG
IFLAG	DW	1 DUP (?)		;CONFIRM FLAG
RFLAG   DW	1 DUP (?)               ;RECURSIVE FLAG
VFLAG   DW	1 DUP (?)               ;VERBOSE FLAG
FNAME_START DW  1 DUP (?)               ;USED BY CHECKWILD
NUMARGS DW      1 DUP (?)               ;NUMBER OF ARGUMENTS ON COMMAND LINE
ARGPTRS DW      MAXARGS DUP (?)         ;POINTERS TO START OF ARGUMENTS
ARGBUF  DB      256 DUP (?)		;BUFFER FOR ARGUMENTS
RSTACK  RMINFO  MAXLEVEL DUP (<>)	;RECURSION STACK

        PAGE +
	.CODE
EXTRN   CPYCNT:NEAR,UC:NEAR,UCSTR:NEAR,CRLF:NEAR
EXTRN   ERRORMSG:NEAR,COUT:NEAR,CIN:NEAR,CLRCO:NEAR
EXTRN   SKIPSP:NEAR,TYPTX:NEAR,ERRORMSG:NEAR,CLRCO:NEAR
EXTRN   CMDSRC:NEAR,FIXPATH:NEAR,TYPE_DIR:ABS,TYPE_AFN:ABS
EXTRN   TYPE_UFN:ABS,TYPE_DSP:ABS,TYPE_DRV:ABS,TYPE_UNK:ABS
EXTRN	GETYORN:NEAR
EXTRN	GETARGS:NEAR

;*********************
;* MACRO DEFINITIONS *
;*********************

ADDR    MACRO   REG,OFFST               ;COMPUTE ADDRESS OFFSET FROM BASE REGISTER (BX)
        MOV     REG,BX
        ADD     REG,OFFST
        ENDM

COPYPATH MACRO  SOURCE,DEST             ;COPY PATHNAME FROM SOURCE TO DEST
        ADDR    SI,SOURCE
        ADDR    DI,DEST
        MOV     CX,PATHSIZE
        CALL    CPYCNT
        ENDM

ERROR   MACRO   ERRNUM                  ;SHOW ERROR MESSAGE
        PUSH    DS
        MOV     AX,DGROUP
        MOV     DS,AX
        MOV     DX,OFFSET MSG&ERRNUM
        CALL    ERRORMSG
        POP     DS
        ENDM

;**********************
; PROGRAM ENTRY POINT *
;**********************
ENTRY:
        TEST_DOS2                       ;TEST FOR DOS 2.0, EXIT IF DOS 1
	MOV	AX,DGROUP
	MOV	ES,AX
        MOV     BX,(80H)         	;GET BYTE COUNT FOR COMMAND LINE
        CMP     [BX],BYTE PTR 0
        JE      USEMSG                  ;IF NO COMMAND TAIL
        PUSH    BX
        MOV     DL,[BX]
        MOV     DH,0
        ADD     BX,DX
        INC     BX
        MOV     [BX],BYTE PTR 0         ;PUT 0 BYTE AT END OF COMMAND LINE
        POP     BX
        INC     BX
	MOV	SI,OFFSET ARGPTRS
	MOV	DI,OFFSET ARGBUF
	MOV	CX,MAXARGS
	CALL	GETARGS			;COLLECT COMMAND LINE ARGUMENTS
	JNB	L_2			;IF OK
TOOMANY:
        ERROR   10                      ;TOO MANY ARGUMENTS (PRETTY UNLIKELY)
	MOV	AL,10			;SET EXIT CODE
        JMP     EXIT2
L_2:	CMP 	CX,0
	JG	L_3			;IF AT LEAST 1 ARG
USEMSG: ERROR   1			;NO ARGUMENTS, SHOW CORRECT USAGE
	MOV	AL,1			;SET ERROR CODE
        JMP     EXIT2			;EXIT 2 DOS
L_3:
        MOV     AX,ES
        MOV     DS,AX                   ;SET DATA SEG TO VARIABLE AREA
        MOV     BYTE PTR IFLAG,CONFIRM  ;SET CONFIRM FLAG DEFAULT
        MOV     BYTE PTR VFLAG,VERBOSE  ;SET VERBOSE FLAG DEFAULT
	MOV	BYTE PTR RFLAG,FALSE    ;SET RECURSIVE FLAG DEFAULT
	MOV	BYTE PTR FFLAG,FALSE	;SET FORCE FLAG DEFAULT
	MOV	WORD PTR LEVEL,0	;ZERO RECURSION LEVEL
        MOV     BYTE PTR DRIVE,0     	;SET DRIVE DEFAULT
;***************************
; COLLECT SWITCHES, IF ANY *
;***************************
	MOV	SI,OFFSET ARGPTRS
NEXTSW:
	MOV	BX,WORD PTR [SI]	;POINT TO ARGUMENT
	MOV	AL,BYTE PTR [BX]	;GET 1ST CHAR.
        CMP     AL,'-'
        JE      GOTSW                   ;IF SWITCH SPECIFIED
	CMP	AL,'/'
	JE 	GOTSW
	JMP	GETFILES
GOTSW:
        INC     BX                      ;SKIP OVER SWITCH CHARACTER
	MOV	AL,BYTE PTR [BX]
SWLOOP:
        CALL    UC                      ;MAKE SWITCH UPPER CASE
	PUSH	BX
	MOV	BX,OFFSET SWLIST
	CALL	CMDSRC			;SEARCH LIST OF VALID SWITCHES
	POP	BX
	MOV	AH,0
	ADD	AX,AX			;NOT FOUND IN LIST?
	JZ	BADSWITCH		;NO.
	MOV	DI,OFFSET FFLAG - 2
	ADD	DI,AX			;POINT TO FLAG
	NOT	WORD PTR [DI]		;1'S COMPLEMENT
	INC	BX
	MOV	AL,BYTE PTR [BX]	;GET NEXT CHAR. FROM LINE
	COMPLIST <SPACE,TAB,NULL>,ENDSW ;IF DELIM
	JMP	SWLOOP			;ELSE ASSUME THIS IS A SWITCH CHAR.
ENDSW:	ADD	SI,2			;POINT TO NEXT POINTER TO AN ARG
	LOOP	NEXTSW
	ERROR	1			;NO ARGS BESIDES SWITCHES
	MOV	AL,1
	JMP	EXIT2
BADSWITCH:
	MOV	AL,BYTE PTR [BX]
        CALL    COUT                    ;DISPLAY BAD CHAR.
        ERROR   9                       ;ILLEGAL SWITCH
	MOV	AL,9			;SET EXIT CODE
        JMP     EXIT2
GETFILES:
        CMP     CX,0                    ;CHECK ARGUMENT COUNT
        JG      ARGSOK                  ;IF AT LEAST 1
NOARGS:
        JMP     USEMSG                  ;GIVE USE MESSAGE
ARGSOK: MOV     WORD PTR NUMARGS,CX  	;SAVE NUMBER OF ARGUMENTS (LESS SWITCHES)

;*****************************************
;* LOOP, DELETING FILES IN ARGUMENT LIST *
;*****************************************
RM1ARG:
        MOV     BX, WORD PTR [SI]       ;GET POINTER TO ARG
        PUSH    SI
        PUSH    CX
        CALL    RMARG                   ;DELETE IT
        POP     CX
        POP     SI
        ADD     SI,2                    ;ADVANCE TO NEXT ARG POINTER
        LOOP    RM1ARG                  ;LOOP TILL NO MORE ARGS TO DELETE
	MOV	AL,0			;SET EXIT CODE
        JMP     EXIT2

;*******************
;* REMOVE ONE FILE *
;*******************
RMARG   PROC   NEAR
        MOV     BYTE PTR DRIVE,0     	;SET DRIVE DEFAULT
        MOV     DI,OFFSET RSTACK+ARG    ;COLLECT FILE NAME
        MOV     AL,[BX+1]
        CMP     AL,':'                  ;CHECK FOR DRIVE SPEC
        JNE     GETARG			;IF NONE
        MOV     AL,[BX]                 ;GET DRIVE LETTER
        SUB     AL,'A'-1                ;MAKE BINARY
        MOV     BYTE PTR DRIVE,AL    	;SAVE DRIVE
GETARG: MOV     AL,[BX]                 ;COPY FROM [BX] TO [DI] UNTIL DELIMITER
	CMP	AL,0
	JE	ENDARG
        STOSB
        INC     BX
        JMP     GETARG
ENDARG:
        STOSB                           ;END ARG W. ZERO
        MOV     WORD PTR RBASE,OFFSET RSTACK   ;SAVE OFFSET TO RSTACK
        CALL    RMIT                    ;DO IT TO IT
        RET
RMARG   ENDP

        PAGE +
;*********************************************************************
; THIS ROUTINE REMOVES A FILE OR DIRECTORY WHOSE PATHNAME IS SPECIFIED
; IN FIELD 'ARG' OF A STRUCTURE OF TYPE 'RMSTRUC'.  'RBASE' POINTS
; TO THE START OF THE DATA STRUCTURE FOR THIS RECURSION LEVEL.
;
RMIT    PROC    NEAR
        MOV     BX,WORD PTR RBASE    	;GET BASE ADDR. (POINTER TO RMINFO)
        ADDR    DX,DTA
        MOV     AH,SET_DTA
        INT     DOS                     ;SET DTA
        ADDR    CX,SPATH
        ADDR    DX,SPREFIX
        PUSH    BX
        ADD     BX,ARG
        CALL    FIXPATH                 ;PARSE PATHNAME
        POP     BX
        CMP     AX,TYPE_UNK
        JNE     GOODPATH                ;IF APPARENTLY OK
        ADDR    DX,ARG
        CALL    ERRORMSG
        ERROR   2                       ;NONEXISTENT PATHNAME, COMPLAIN
        RET
GOODPATH:
        MOV     WORD PTR [BX].PATHTYPE,AX     ;SAVE TYPE OF ARGUMENT
	IF	GOOFPROOF
	CMP	AX,TYPE_DSP		;DIRECTORY SPEC?
	JE	CHECKI			;YES, MAY NEED OK
	CMP	AX,TYPE_DRV		;DRIVE SPEC?
	JNE	NOTDRV			;NO
CHECKI:
	CMP	BYTE PTR IFLAG,TRUE	;I FLAG SET?
	JNE	GETOK			;NO, GET OK
	JMP	NOARGERR		;ELSE GO AHEAD
	ENDIF
NOTDRV:
	CMP	AX,TYPE_DIR		;DIRECTORY?
	JNE	NOTDIR
	CMP	BYTE PTR RFLAG,TRUE	;R FLAG SET?
	JNE	DIRERR			;NO, ERROR
	JMP	NOARGERR		;YES, OK
DIRERR:
	ADDR	DX,ARG
	CALL	ERRORMSG		;SHOW ARGUMENT
	ERROR	3			;COMPLAIN THAT IT'S A DIRECTORY
	RET
NOTDIR:
	IF	GOOFPROOF
	CMP	AX,TYPE_AFN
	JNE	NOARGERR		;IF NOT AFN
	CMP	BYTE PTR LEVEL,0
	JG	NOARGERR		;IF LEVEL>0, DON'T CHECK FOR *.*
	CMP	BYTE PTR IFLAG,TRUE
	JE	NOARGERR		;DITTO IF I SWITCH SPECIFIED
	CALL	CHECKWILD		;CHECK FOR *.*
	JNC	NOARGERR		;IF SOMETHING ELSE
GETOK:
;	User typed something like *.* or *foo.* or A: or A:\FOO\
;	In all cases make the end of the path = *.* and ask for
;	confirmation.
	ADDR	SI,SPATH
	MOV	WORD PTR FNAME_START, SI
FINDFNAME:
	MOV	AL, BYTE PTR [SI]
	CMP	AL, ':'
	JE	FOUNDPATH
	CMP	AL, '\'
	JE	FOUNDPATH
	CMP	AL, 0
	JE	ENDOFNAME
	INC	SI
	JMP	FINDFNAME
FOUNDPATH:
	INC	SI
	MOV	WORD PTR FNAME_START, SI
	JMP	FINDFNAME
ENDOFNAME:
	MOV	DI, WORD PTR FNAME_START
	MOV	AL, '*'
	STOSB
	MOV	AL, '.'
	STOSB
	MOV	AL, '*'
	STOSB
	XOR	AL, AL
	STOSB
	ADDR	DX,SPATH
	CALL	ERRORMSG		;SHOW SEARCH PATH
	ERROR	13
	ERROR	6A			;USER TYPED *.*, ASK FOR CONFIRMATION
	CALL	GETYORN
	CMP	AL,'Y'
	JE	NOARGERR		;GO AHEAD IF 'Y' TYPED
	MOV	AL,13			;SET EXIT CODE
	JMP	EXIT2
	ENDIF
;******************************************
; SEE IF ANYTHING MATCHES THE SEARCH SPEC *
;******************************************
NOARGERR:
        ADDR    DX,SPATH                ;POINT TO SEARCH PATH
        MOV     CX,31H                  ;SET SEARCH ATTRIBUTES
        MOV     AH,FIND_FIRST
        INT     DOS                     ;SEARCH FOR 1ST MATCH
        JNC     GOTONE                  ;OK IF SOMETHING FOUND
        ADDR    DX,ARG
        CALL    ERRORMSG                ;ELSE SHOW ARGUMENT
        ERROR   2                       ;SAY IT DOESN'T EXIST
	RET
;**************
; TOP OF LOOP *
;**************
GOTONE:
        CALL    BUILD_NAME              ;MAKE UNAMBIG. FILE NAME
        JNC     NOSKIP
        JMP     NEXTFILE                ;IF "FILE" IS "." OR ".."
NOSKIP:
        TEST    [BX].DTA+21,BIT$DIR     ;IS THIS A DIRECTORY?
        JNZ	GOTADIR			;YES.
	JMP	RMFILE			;NO, IT'S A FILE
;*************************************************************
; IF ARG IS A DIRECTORY, AND R FLAG SET, DELETE ITS CONTENTS *
;*************************************************************
GOTADIR:
	CMP	BYTE PTR RFLAG,TRUE	;R FLAG SET?
	JE	RSET			;YES, OK TO DELETE DIRECTORY
	JMP	NEXTFILE		;NO, JUST SKIP OVER DIRECTORY
RSET:
	CMP	BYTE PTR IFLAG,TRUE	;I FLAG SET?
	JNE	INOTSET			;NO, GO INTO DIRECTORY
	ADDR	DX,FULLNAME
	CALL	ERRORMSG		;SHOW DIRECTORY NAME
	ERROR	11			;ASK IF USER WANTS TO ENTER IT
	CALL	GETYORN			;GET RESPONSE
	CMP	AL,'N'
	JE	NEXTFILE		;IF N TYPED
INOTSET:
        CMP     WORD PTR LEVEL,MAXLEVEL-1 ;ARE WE AT MAX LEVEL?
        JL      NOTMAX                  ;NO
        ERROR   7                       ;YES, TOO DEEP
	MOV	AL,7			;SET EXIT CODE
        JMP     EXIT2
NOTMAX:
        COPYPATH FULLNAME,(ARG+RMSTRUCSIZE)  ;COPY DIRECTORY TO ARG AT NEXT LEVEL
        PUSH    BX                      ;SAVE POINTER TO BASE
        INC     WORD PTR LEVEL          ;INCREMENT RECURSION LEVEL
        ADD     WORD PTR RBASE,RMSTRUCSIZE ;ADVANCE BASE POINTER
        CALL    RMIT                    ;REMOVE THE DIRECTORY CONTENTS
        POP     BX                      ;RESTORE BASE POINTER
        DEC     WORD PTR LEVEL       	;DECREMENT RECURSION LEVEL
        SUB     WORD PTR RBASE,RMSTRUCSIZE  ;RESTORE BASE TO PREVIOUS LEVEL
        ADDR    DX,DTA
        MOV     AH,SET_DTA
        INT     DOS                     ;RESET DTA
        JMP     SHORT NEXTFILE          ;DO NEXT FILE
;**************************
; FOUND A FILE, REMOVE IT *
;**************************
RMFILE:
        CMP     BYTE PTR VFLAG,TRUE  	;CHECK VERBOSE FLAG
        JNE     QUIET                   ;IF QUIET MODE
        ADDR    DX,FULLNAME             ;NOT QUIET, SHOW VERBIAGE
        CALL    ERRORMSG
        CALL    CRLF
QUIET:
        CALL    RM_FILE
	JNC	NEXTFILE
	ADDR	DX,FULLNAME		;IF ERROR, SHOW NAME
	ERROR	5			;AND ERROR MSG.
;*****************
; BOTTOM OF LOOP *
;*****************
NEXTFILE:
        MOV     AH,FIND_NEXT
        INT     DOS                     ;FIND NEXT MATCH, IF ANY
        JC      NOMORE                  ;IF NONE
        JMP     GOTONE                  ;GOT ONE, BACK TO TOP OF LOOP
;*************************************************
; NO MORE FILES, DELETE DIRECTORY (IF NECESSARY) *
; ************************************************
NOMORE:
	CMP	WORD PTR [BX].PATHTYPE,TYPE_DIR	;WAS THIS ARG A DIRECTORY?
	JNE	DONE			;NO, DONE
	CMP	BYTE PTR RFLAG,TRUE	;RFLAG SET?
	JNE	DONE			;NO, DONE
	ADDR	DX,ARG			;POINT TO DIRECTORY NAME
	CMP	BYTE PTR IFLAG,TRUE	;I FLAG SET?
	JNE	NOI
	CALL	ERRORMSG		;DISPLAY DIRECTORY NAME
	ERROR	12			;ASK FOR OK TO DELETE
	CALL	GETYORN			;GET Y OR N RESPONSE
	CMP	AL,'N'
	JE	DONE			;IF N, DON'T DELETE IT
NOI:
	ADDR	DX,ARG
	MOV	AH,RMDIR
	INT	DOS			;REMOVE DIRECTORY (IT SHOULD BE EMPTY NOW)
	JNC	DONE			;IF OK
	ADDR	DX,ARG
	CALL	ERRORMSG		;ELSE SHOW DIRECTORY NAME
	ERROR	5			;AND ERROR MSG.
DONE:
        RET                             ;ALL DONE
RMIT    ENDP

;**************************************************
; CHECKWILD = CHECK ARG FOR *.* ERASURE
; RETURNS 'C' = 1 IF *.* ON WHOLE DIRECTORY OR DRIVE

CHECKWILD PROC	NEAR
	ADDR	SI,ARG			;GET ARG
	MOV	WORD PTR FNAME_START, SI
FINDFN:					;FIND THE FILE NAME PORTION
	CMP	BYTE PTR [SI],0
        JE	GOTEND
	CMP	BYTE PTR [SI],'\'
	JE	PATH_CHAR
	CMP	BYTE PTR [SI],':'
	JE	PATH_CHAR
	INC	SI
	JMP	SHORT FINDFN
PATH_CHAR:
	INC	SI
	CMP	BYTE PTR [SI], 0	; IF ARG ENDS W. \ OR :
	JE	WIPEOUT			; TREAT AS WILDCARD
	MOV	WORD PTR FNAME_START, SI
	JMP	SHORT FINDFN
GOTEND:
	MOV	SI, WORD PTR FNAME_START ; GET START OF FILE NAME
        CMP	BYTE PTR [SI], '*'	; IS IT STAR?
	JNE	NOTWIPE			; J/NO
FINDEXT:
	INC	SI
	CMP	BYTE PTR [SI], '.'
	JE	FOUND_EXT
	CMP	BYTE PTR [SI], 0
	JE	NOTWIPE
	JMP	FINDEXT
FOUND_EXT:
	INC	SI
	CMP	BYTE PTR [SI], '*'
	JNE	NOTWIPE
WIPEOUT:
	STC
	RET
NOTWIPE:
	CLC
	RET

CHECKWILD ENDP

        PAGE +
;*****************************************************************************
; THIS ROUTINE TAKES THE INFO ON A FILE IN THE DTA AND USES IT TO GENERATE A
; FULL, UNAMBIGUOUS FILE NAME BY ADDING THE NECESSARY DIRECTORY PREFIXES TO IT.
; IT SETS THE CARRY FLAG IF THE "FILE" IS "." OR "..".
;
BUILD_NAME  PROC  NEAR
        COPYPATH SPREFIX,FULLNAME       ;GET SOURCE PREFIX, COPY TO FULL NAME
        DEC     DI                      ;BACK UP OVER NULL
        ADDR    SI,(DTA+30)             ;POINT TO FILE NAME WE FOUND
        CMP     [SI],BYTE PTR '.'       ;DOES IT START WITH .? -
        JNE     NOTDOT                  ;- NO
        STC                             ;- YES, SET CARRY TO SKIP IT
        RET
NOTDOT:
        CALL    CPYCNT                  ;ADD IT TO SOURCE PREFIX
	CLC				;CLEAR CARRY
        RET
BUILD_NAME ENDP

;*************************************************************************
; REMOVE ONE FILE (NAME IS IN FIELD 'FULLNAME' OF THE 'RMSTRUC' POINTED TO
; BY BX).  RETURNS WITH CARRY SET IF ERROR.
;
RM_FILE	PROC	NEAR
        CMP     BYTE PTR IFLAG,TRUE  	;IS FLAG SET TO CONFIRM?
        JNE     COK                     ;NO.
        ADDR    DX,FULLNAME
        CALL    ERRORMSG                ;SHOW FILE
        ERROR   4                       ;ASK FOR CONFIRMATION
	CALL	GETYORN			;GET Y OR N RESPONSE
        CMP     AL,'Y'                  ;IS IT YES?
        JNE     OKEXIT                  ;NO, RETURN W/O ERROR
COK:
TRYAGAIN:
        ADDR    DX,FULLNAME
        MOV     AH,UNLINK
        INT     DOS                     ;UNLINK THAT SUCKER
        JNC     OKEXIT                  ;IF NO PROBLEM
        CMP     AX,5                    ;WAS FILE R/O? (THIS OUGHT TO BE
                                        ; THE ONLY POSSIBLE ERROR)
        JNE     ABEXIT                  ;EXIT IF SOME OTHER ERROR
	CMP	BYTE PTR FFLAG,TRUE	;CHECK F FLAG
	JE	NOWARN			;IF SET, DON'T WARN ABOUT R/O FILE
	ADDR	DX,FULLNAME
	CALL	ERRORMSG		;SHOW FILE NAME
        ERROR   6                       ;R/O FILE, QUERY USER AGAIN
	CALL	GETYORN			;GET Y OR N RESPONSE
        CMP     AL,'Y'                  ;TEST FOR 'Y' RESPONSE
        JNE     OKEXIT                  ;QUIT (WITH NO ERROR) IF USER SAYS 'N'
NOWARN:
        MOV     CX,0                    ;'Y' TYPED CLEAR ALL ATTRIBUTES
        ADDR    DX,FULLNAME             ;POINT TO FILE NAME
        MOV     AH,CHMOD
        MOV     AL,1                    ;CHANGE ATTRIBUTES
        INT     DOS                     ;TRY MAKING IT R/W
        JMP     TRYAGAIN                ;TRY TO ERASE IT AGAIN
ABEXIT: STC
        RET
OKEXIT: CLC
        RET
RM_FILE	ENDP

EXIT2:
        MOV     AH,EXIT
        INT     DOS                     ;EXIT TO DOS

        END     ENTRY

SHOW.ASM

        TITLE SHOW - MSDOS FILE DISPLAY PROGRAM
        PAGE 55,132
;****************************************************************************
; BIDIRECTIONAL FILE DISPLAY PROGRAM FOR MSDOS 2
;
;       BY: JON DART
;           3012 HAWTHORN ST.,
;           SAN DIEGO, CA 92104
;
; VERSION 2.2, 01-Nov-87
;
; THIS IS A MODIFIED VERSION OF THE CP/M PROGRAM 'BISHOW' ON SIG/M V. 145.
; I ADDED SOME MODS OF MY OWN TO THE CP/M VERSION, AND THEN TRANSLATED
; IT TO MSDOS, USING THE PROGRAM 'ICT.COM' FROM THE HEATH USERS' GROUP.
; (A CP/M-86 VERSION OF THE PROGRAM EXISTS, BUT I DIDN'T MAKE USE OF IT
; IN PREPARING THIS VERSION).
;
; FURTHER ENHANCEMENTS WERE ADDED TO THE MSDOS VERSION, AS FOLLOWS:
;
; 1. TAB CHARACTERS ARE EXPANDED AND LINES ARE TRUNCATED AFTER 80 COLS.
;
; 2. FOR COMPATIBILITY WITH WORD PERFECT (tm) FILES, LINE FEEDS WITHOUT
; CARRIAGE RETURNS AND CARRIAGE RETURNS W/O LINE FEEDS ARE TREATED THE
; SAME AS CR + LF. (WORDSTAR (tm) FILES ARE ALSO HANDLED CORRECTLY, SINCE
; THE HI BITS ARE STRIPPED OFF.)
;
; 3. 'B' GOES TO BEGINNING OF FILE, 'E' SKIPS TO END.
;****************************************************
; TO BUILD SHOW.EXE:
;	MASM SHOW,SHOW,NUL,NUL
;	LINK SHOW,SHOW,NUL,ASM
;
;****************************************************
; ORIGINAL COMMENTS FROM THE CP/M VERSION:
;
;A buffered, bidirectional version of SHOW.ASM.
;Ver 1.0, 23 Aug 82
;Phil Cary, 748 Kenilworth Parkway, Baton Rouge, LA  70808
;Ver 1.1, 30 Mar 83 added BDOS function 6 W.F.McGee
;Ver 1.2, 22 May 83 added hi-bit mask for WordStar files
;         and Wyse-100/200 SCRL keys-changed messages
;         also made CP/M-86 ver using XLT86 see A86 file
;         H.M. Van Tassell
;modified for H89 by Jon Dart 04/13/84 -- uses H89 up and down arrow keys.
;Also cleaned up code, made pgrm. return to CCP instead of warm-booting.
;
;BISHOW is a buffered, bidirectional version of SHOW.ASM which first
;appeared in Interface Age, November, 1981.  That program could
;only scroll forward in a file, and read sectors from disk one at a
;time as they were sent to the console.  I used SHOW frequently to take
;a quick look at a file without loading a big text editor, and to examine
;another file with the RUN command while in Wordstar.  TYPE does not work
;since it is not a file that Wordstar can load and run.
;
;It was annoying when I went past the point I was looking for in a file
;with SHOW, and could not go backwards.  Thus, this bidirectional version
;which uses random access reads. In addition, buffering was added so that
;the number of disk reads would be reduced, and moving back and forth
;in a moderate sized file would be speeded up.  There is a trade off
;between the size of the buffer and the length of time it takes to refill
;the buffer which should be set to the user's preference.
;
;Just a small contribution to the public domain software as partial
;payment for the many fine and educational programs the system has
;given me.  Phil Cary.

	DOSSEG
	.MODEL	SMALL

;*************
; BEGIN CODE *
;*************
        .XLIST
        INCLUDE MSDOS2.DEF
        INCLUDE ASCII.DEF
	INCLUDE MACROS.DEF
        .LIST

MAXSEC  EQU     64                      ;BUFFER SIZE IN 128-BYTE RECORDS
FULBUF  EQU     (MAXSEC*128)            ;OFFSET TO END OF BUFFER
EOF     EQU     CTRL$Z                  ;END OF FILE CHAR.
TRUE    EQU     1
FALSE   EQU     1-TRUE
IBM     EQU     TRUE                    ;TRUE IF IBM-COMPAT AT BIOS LEVEL
BIOS    EQU     10H                     ;IBM INTERRUPT FOR BIOS VIDEO SERVICES

; MEMORY ALLOCATION

	.DATA
JMPTBL  DW      OFFSET _TEXT:BCMD        ;JUMP TABLE TO COMMAND ROUTINES
        DW      OFFSET _TEXT:ECMD        ;(PUT HERE BECAUSE MUST BE DEFINED ON PASS 1)
        DW      OFFSET _TEXT:XCMD
CMDLST  DB      'BEX',0                 ;VALID COMMANDS
LINMAX  DB      24                      ;NUMBER OF TO WRITE LINES ON CONSOLE
LINCNT  DB      0                       ;LINE NUMBER ON WRITE OR MOVE BACK IN BUFFER
COLCNT  DB      1                       ;COUNT OF COLUMNS WRITTEN TO DISPLAY
AMTREAD DW      0                       ;AMOUNT READ ON LAST READ OP
FILEPOS DQ      0                       ;OFFSET FROM START OF FILE
INHANDLE DW     0                       ;INPUT FILE HANDLE

	.DATA?
VMODE   DB      1 DUP (?)               ;VIDEO MODE (IBM ONLY)
VPAGE   DB      1 DUP (?)               ;VIDEO PAGE (IBM ONLY)
SCRSIZE DB      1 DUP (?)               ;PHYSICAL SCREEN WIDTH, COLUMNS
BUFFER	DB	FULBUF DUP (?)

	.STACK
        DB      512 DUP (?)

	.CODE
EXTRN   SKIPSP:NEAR,TYPTX:NEAR,UC:NEAR,CMDSRC:NEAR
EXTRN   CLRCO:NEAR,CIN:NEAR,COUT:NEAR

ENTRY:  JMP     SHORT START             ;SKIP OVER NEXT SUBROUTINE

;       ROUTINE TO CLEAR SCREEN, HOME CURSOR:
;       (MAY HAVE TO CHANGE FOR NON-IBM MACHINES)

CLRSCR  PROC    NEAR
        PUSH    BX
        IF      IBM
        PUSH    DS
        MOV     AX,DGROUP
        MOV     DS,AX
        MOV     DX,0
        CALL    MOVEXY                  ;MOVE TO HOME POSITION
        MOV     AL,BYTE PTR DGROUP:VMODE  ;GET CURRENT VIDEO MODE
        MOV     AH,0
        INT     BIOS                    ;RESET MODE (CLEARS SCREEN)
        POP     DS
        ELSE
        MOV     AL,FF                   ;USE FF (WILL WORK ON MOST TERMINALS)
        CALL    COUT                    ;(OR ADD YOUR CLEAR SCREEN FN. HERE)
        ENDIF
        POP     BX
        RET
CLRSCR  ENDP

MOVEXY  PROC    NEAR                    ;MOVE TO ROW (DH) AND COLUMN (DL)
        MOV     AH,2
        MOV     BH,BYTE PTR DGROUP:VPAGE  ;VIDEO PAGE #
        MOV     BL,0                    ;COLOR (DON'T CHANGE)
        INT     BIOS                    ;MOVE CURSOR
        RET
MOVEXY  ENDP

START:  TEST_DOS2                       ;TEST FOR DOS 2.0
        PUSH    DS                      ;SAVE DATA SEG (POINTS TO PSP)
        MOV     AX,DGROUP
        MOV     DS,AX                   ;SET DATA SEG.
        IF      IBM
        MOV     AH,15
        INT     BIOS                    ;GET VIDEO MODE
        MOV     BYTE PTR VPAGE,BH       ;STORE VIDEO PAGE #
        MOV     BYTE PTR SCRSIZE,AH     ;STORE CHARS/LINE
        MOV     BYTE PTR VMODE,AL       ;STORE DISPLAY MODE
        ELSE
        MOV     BYTE PTR SCRSIZE,80     ;ASSUME 80 COLUMN DISPLAY (MODIFY IF NEEDED)
        ENDIF

        POP     DS                      ;RESTORE INITIAL DATA SEG.
        CALL    CLRSCR                  ;CLEAR SCREEN, HOME CURSOR
MEMOK:  MOV     AX,DGROUP		;GET DATA SEG
	MOV	ES,AX			;SET EXTRA SEG TO POINT TO BUFFER
        MOV     BX,(80H)         	;GET BYTE COUNT FOR COMMAND LINE
        MOV     AL,[BX]                 ;GET BYTE COUNT
        CMP     AL,0                    ;TEST IT
        JNE     GOTFN                   ;IF GOT SOMETHING
        JMP     HELP                    ;NOTHING, GIVE HELP
GOTFN:  MOV     AH,0
        INC     BX
        PUSH    BX
        ADD     BX,AX
        MOV     [BX],BYTE PTR 0         ;MAKE SURE COMMAND LINE ENDS W. 0
        POP     BX
        CALL    SKIPSP                  ;SKIP BLANKS AND TABS
        JNB     START2                  ;IF GOT SOMETHING
        MOV     AX,CS
        MOV     DS,AX
        JMP     HELP                    ;OTHERWISE GIVE HELP
START2: MOV     DX,BX
        MOV     AL,READ_ACCESS
        MOV     AH,DOS2_OPEN
        INT     DOS                     ;TRY TO OPEN FILE
        PUSHF                           ;SAVE STATUS
        PUSH    AX
        MOV     AX,DGROUP
        MOV     DS,AX                   ;SET DATA SEG
        CALL    CLRSCR                  ;CLEAR SCREEN
        POP     AX
        POPF                            ;RESTORE STATUS FROM FILE OPEN
        JNB     OPENOK                  ;IF OK
        JMP     BADOPEN                 ;NOT OK, GIVE ERROR MSG.
OPENOK:
        MOV     WORD PTR INHANDLE,AX    ;SAVE FILE HANDLE
        CALL    FILBUF                  ;FILL THE DISK BUFFER

BEGIN:
        MOV     BYTE PTR LINCNT,0       ;INIT LINE COUNT
        MOV     BYTE PTR COLCNT,1       ;AND COLUMN COUNT TO 1
        MOV     BYTE PTR LINMAX,24      ;SET MAX LINE COUNT
WRTFWD: MOV     BX,OFFSET BUFFER        ;POINT TO START OF BUFFER
WRTFWD1:MOV     AL,BYTE PTR [BX]     	;GET A CHARACTER
        CMP     AL,EOF                  ;IS IT EOF?
        JNE     L_2                     ;IF NOT
        JMP     GETCMD                  ;EOF, GO WAIT FOR COMMAND ENTRY
L_2:
        PUSH    AX                      ;SAVE CHAR.
        CALL    CTYPE                   ;PUT IT ON CONSOLE
        POP     AX                      ;GET CHAR.
        AND     AL,7FH                  ;STRIP HI BIT
        CMP     AL,CR                   ;SEE IF END OF LINE
        JE      SAWCR                   ;IF GOT CR, CHECK FOR LF
        CMP     AL,LF                   ;CHECK FOR LF
        JNE     WRTFWD2                 ;IF NOT LF
        DEC     BX                      ;POINT TO PREV. CHARACTER
        CMP     [BX],BYTE PTR CR     	;WAS PREVIOUS CHARACTER CR?
        PUSHF                           ;SAVE FLAGS
        INC     BX                      ;BUMP POINTER
        POPF                            ;GET FLAGS - 'Z'=1 IF LAST CHAR. CR
        JE      WRTFWD2                 ;IF CR, IGNORE IT
        MOV     AL,CR                   ;GOT LF ALONE, MAP TO CR
        CALL    CTYPE                   ;SHOW CR
        JMP     SHORT FWDCNT            ;COUNT LF AS END OF LINE CHAR.
WRTFWD2:INC     BX                      ;BUMP BUFFER POINTER
        MOV     DX,OFFSET BUFFER+FULBUF  ;GET END OF BUFFER OFFSET
        CMP     BX,DX                   ;COMPARE W. BUFFER PTR.
        JNE     WRTFWD1                 ;IF NOT EQUAL, CONTINUE
        CALL    FILBUF                  ;ELSE FILL BUFFER AGAIN
        JMP     WRTFWD                  ;..AND START AT BEGINNING OF BUFFER
SAWCR:  MOV     AL,BYTE PTR [BX+1]      ;SAW CR, GET NEXT CHAR.
        CMP     AL,LF                   ;IS IT LF?
        JE      FWDCNT                  ;YES, DON'T WORRY
        MOV     AL,LF                   ;NO LF AFTER CR,
        CALL    CTYPE                   ;SHOW LF ANYWAY

FWDCNT: MOV     SI,OFFSET LINCNT
        MOV     AL,BYTE PTR LINMAX      ;GET MAX. LINE COUNT
        CMP     BYTE PTR [SI],AL        ;COMPARE LINE COUNT WITH LIMIT
        JNE     WRTFWD2                 ;IF NOT THERE, CONTINUE
        MOV     BYTE PTR [SI],0         ;RE-INIT LINE COUNT

GETCMD:                                 ;WAIT FOR COMMAND
        IF      IBM
        PUSH    BX
        MOV     DH,24
        MOV     DL,0
        CALL    MOVEXY                  ;MOVE TO BOTTOM OF SCREEN
        POP     BX
        ENDIF
        CALL    CIN                     ;GET CHAR. FROM CONSOLE
        CALL    UC                      ;MAKE IT UPPER CASE
        CMP     AL,0                    ;IS IT NULL (FUNCTION KEY LEADIN)? -
        JE      FNKEY                   ;YES.
        PUSH    BX
        MOV     BX,OFFSET CMDLST        ;POINT TO COMMAND LIST
        CALL    CMDSRC                  ;SEARCH FOR MATCH
        POP     BX
        DEC     AL                      ;-1 SO INDEX STARTS AT 0
        JS      BADCMD                  ;IF NO MATCH
        SHL     AL,1                    ;DOUBLE INDEX
        MOV     AH,0                    ;HI BYTE 0
        MOV     BX,AX                   ;INDEX IN INDEX REGISTER
        JMP     JMPTBL[BX]              ;JUMP TO ROUTINE

FNKEY:
        CALL    CIN                     ;FUNCTION KEY PRESSED, GET NEXT CHAR.
        CMP     AL,49H                  ;PAGE UP?
        JE      GOBACK                  ;YES, GO BACK
        CMP     AL,51H                  ;PAGE DOWN?
        JE      GOFWD                   ;YES, GO FWD
BADCMD: CALL    TYPTX                   ;..A WRONG CHOICE SO GIVE MESSAGE
        DB      CR,LF,LF
        DB      'Commands: PG DN, PG UP, B(eginning), E(nd), X(exit)'
        DB      CR,BEL+80H
        JMP     GETCMD                  ;TRY AGAIN FOR COMMAND

GOFWD:
        CMP     BYTE PTR [BX],EOF    	;AT EOF?
        JE      GETCMD                  ;YES, IGNORE 'FORWARD' COMMAND
        MOV     BYTE PTR LINMAX,24      ;24 LINES 2 WRITE
        CALL    CLRSCR                  ;CLEAR SCREEN
        JMP     WRTFWD1                 ;SCROLL FORWARD
GOBACK:
        MOV     BYTE PTR LINMAX,24      ;24 LINES 2 WRITE
WRTBAK:
        MOV     BYTE PTR LINCNT,44      ;LINES TO MOVE BACK
        CALL    CLRSCR                  ;CLEAR THE SCREEN
WRTBAK1:
        CMP     BX,OFFSET BUFFER        ;AT START OF BUFFER?
        JE      FILBAK                  ;IF AT START OF BUFFER
WRTBAK2:MOV     AL,BYTE PTR [BX]     	;GET A CHARACTER
        DEC     BX                      ;DECREMENT BUFFER
        AND     AL,7FH                  ;STRIP HI BIT
        CMP     AL,CR                   ;SEE IF END OF LINE
        JE      BAKCNT                  ;IF CR
        CMP     AL,LF
        JNE     WRTBAK1                 ;IF NOT LF
        MOV     AL,BYTE PTR [BX]        ;GET CHAR. BEFORE LF
        CMP     AL,CR                   ;IS CR?
        JE      WRTBAK1                 ;YES, IGNORE LF

BAKCNT: DEC     BYTE PTR LINCNT         ;ELSE, DECR. NUMBER OF LINES TO MOVE BACK
        JNZ     WRTBAK1                 ;..AND LOOP IF NOT THERE
        INC     BX                      ;ELSE BUMP POINTER TO ACCOUNT FOR LF WITH CR
        JMP     WRTFWD1                 ;..AND GO WRITE A SCREEN

FILBAK: MOV     DX,FULBUF 		;GET THE BUFFER SIZE
        MOV     BX,WORD PTR AMTREAD	;AND THE # OF BYTES LAST READ
        ADD     BX,DX                   ;ADD THEM
        MOV     DX,WORD PTR FILEPOS
        MOV     CX,WORD PTR FILEPOS+2   ;LOAD FILEPOS INTO CX:DX
        SUB     DX,BX                   ;SUBTRACT AMT. TO MOVE BACK
        JNB     NOBORROW                ;IF NO BORROW
        SUB     CX,1                    ;CX <- CX-1 (AFFECTS BORROW FLAG)
NOBORROW:
        JB      FILBEG                  ;IF BEYOND BEGINNING OF FILE
        MOV     WORD PTR FILEPOS,DX     ;STORE LO WORD OF FILE POS
        MOV     WORD PTR FILEPOS+2,CX   ;AND HI WORD OF FILE POS
        MOV     BX,WORD PTR INHANDLE
        MOV     AL,0                    ;POSITION FROM START
        MOV     AH,LSEEK
        INT     DOS                     ;MOVE FILE POINTER
        CALL    FILBUF                  ;FILL THE BUFFER
        MOV     BX,OFFSET BUFFER+FULBUF  ;..AND POINT TO END OF BUFFER
        CALL    CLRSCR                  ;CLEAR THE SCREEN
        JMP     WRTBAK2                 ;CONTINUE MOVING BACK IN FILE

ECMD:                                   ;COME HERE TO MOVE TO END OF FILE
        CALL    CLRSCR                  ;CLEAR SCREEN
        MOV     AH,LSEEK                ;PREPARE TO MOVE FILE POINTER
        MOV     BX,WORD PTR INHANDLE    ;GET FILE HANDLE
        MOV     AL,2                    ;POSITION AT END
        MOV     CX,0                    ;OFFSET, MSW
        MOV     DX,0                    ;OFFSET, LSW
        INT     DOS                     ;MOVE FILE POINTER
        SUB     AX,FULBUF               ;SUBTRACT BUFFER SIZE
        JNB     NOBORR                  ;IF NO BORROW
        SUB     DX,1
        JNB     NOBORR                  ;IF FILE BIGGER THAN BUFFER
        MOV     AX,0
        MOV     DX,0                    ;FILE SMALLER THAN BUFFER
        JMP     SHORT SMALL             ;FILL BUFFER STARTING FROM BEGINNING
NOBORR:
        MOV     CX,DX                   ;MSW OF OFFSET IN CX
        MOV     DX,AX                   ;LSW OF OFFSET IN DX
SMALL:
        MOV     AH,LSEEK                ;GOING TO MOVE POINTER AGAIN
        MOV     BX,WORD PTR INHANDLE    ;GET HANDLE
        MOV     AL,0                    ;POSITION FROM START
        INT     DOS                     ;MOVE FILE POINTER
        MOV     WORD PTR FILEPOS,AX     ;UPDATE FILE POSITION
        MOV     WORD PTR FILEPOS+2,DX   ;HI WORD TOO
        CALL    FILBUF                  ;FILL BUFFER
        MOV     BX,OFFSET BUFFER        ;LOAD BUFFER OFFSET
        ADD     BX,WORD PTR AMTREAD     ;COMPUTE ADDR. OF LAST VALID BYTE
        MOV     BYTE PTR LINMAX,24      ;24 LINES TO DISPLAY
        MOV     BYTE PTR LINCNT,24      ;24 LINES TO MOVE BACK
        JMP     WRTBAK1                 ;GO BACK FROM END

BCMD:                                   ;MOVE TO BEGINNING OF FILE
        CALL    CLRSCR                  ;CLEAR SCREEN
FILBEG: MOV     WORD PTR FILEPOS,0      ;ZERO FILE POS
        MOV     WORD PTR FILEPOS+2,0
        MOV     CX,0
        MOV     DX,0
        MOV     BX,WORD PTR INHANDLE
        MOV     AL,0
        MOV     AH,LSEEK
        INT     DOS                     ;SEEK TO START OF FILE
        CALL    FILBUF                  ;FILL BUFFER
        JMP     BEGIN                   ;GO WRITE IT

FILBUF  PROC    NEAR                    ;FILL BUFFER FROM FILE
        PUSH    DX                      ;SAVE REGS.
        PUSH    BX
        MOV     BX,WORD PTR INHANDLE    ;LOAD FILE HANDLE
        MOV     DX,OFFSET BUFFER        ;LOAD OFFSET TO DISK BUFFER
        MOV     CX,FULBUF               ;SIZE OF BUFFER
        MOV     AH,READ
        INT     DOS                     ;READ FROM DISK
        JB      RDERR                   ;IF ERROR
        MOV     WORD PTR AMTREAD,AX     ;SAVE AMT. READ
        CMP     AX,CX                   ;IS BUFFER FULL?
        JE      FULL                    ;YES, IT IS
        MOV     BX,AX                   ;NO, MAKE BX POINT TO LAST BYTE+1
	ADD	BX,OFFSET BUFFER
        MOV     BYTE PTR [BX],EOF    	;STICK IN EOF CHAR.
FULL:
        ADD     WORD PTR FILEPOS,AX     ;UPDATE FILE POINTER
        JNC     NOCARRY
        INC     WORD PTR FILEPOS+2
NOCARRY:                                ;READ OK
        POP     BX
        POP     DX
        RET
FILBUF  ENDP

RDERR:  CALL    TYPTX                   ;READ ERROR
        DB      'Disk read error.',CR,LF+80h
        POP     BX                      ;CLEAR STACK
        POP     DX
        POP     DX
        JMP     EXIT0                   ;GO EXIT

BADOPEN:                                ;COME HERE WHEN CAN'T OPEN FILE
        CALL    TYPTX                   ;GIVE ERROR MSG AND QUIT
        DB      CR,LF,LF,'Requested file is not on this disk.',CR,LF
        DB      'Please check your spelling or use DIR.',cr,lf,lf,bel+80H
        JMP     EXIT1                   ;LEAVE MSG ON SCREEN ON EXIT

HELP:                                   ;COME HERE WHEN NO FILE NAME GIVEN
        MOV     AX,DGROUP
        MOV     DS,AX
        CALL    CLRSCR
        CALL    TYPTX
        DB      'SHOW V. 2.2 by Jon Dart (01-Nov-87)',CR,LF,CR,LF
        DB      'Correct usage of SHOW is --',cr,lf,lf
        DB      '    A>SHOW filename  ',cr,lf,lf
        DB      'After first page is displayed, press the PG DN key to go'
        DB      CR,LF
        DB      "forward, and the PG UP key to go backward.  'B' moves to"
        DB      CR,LF
        DB      'the beginning of the file, and E moves to the end.  Type'
        DB      CR,LF
        DB      'X to exit.',bel,cr,lf,lf+80h

        JMP     EXIT1

;       OUTPUT 1 CHAR. - EXPANDS TABS, SHOWS CONTROL CHARS. IN VISIBLE FORM.
;       ENTRY:  AL = CHAR.
;
CTYPE   PROC    NEAR
        PUSH    CX                      ;SAVE THE REGISTERS
        PUSH    DX                      ;..FROM
        PUSH    BX                      ;..CLOBBER
        AND     AL,7FH                  ;STRIP HI BIT
        JZ      QUIT                    ;IF NULL
        CMP     AL,SPACE                ;CONTROL CHAR?
        JGE     NOTCTL                  ;NOPE.
        CMP     AL,CR                   ;GOT CONTROL CHAR.
        JE      NOTCTL                  ;IF CR, JUST PRINT IT
        CMP     AL,LF
        JE      NOTCTL                  ;IF LF, JUST PRINT IT
        CMP     AL,TAB
        JE      GOTTAB                  ;IF TAB, EXPAND
        ADD     AL,'A'-1                ;ELSE CONVERT TO LETTER
        PUSH    AX
        MOV     AL,'^'
        CALL    CTYPE1                  ;SHOW UP-ARROW
        POP     AX
NOTCTL: CALL    CTYPE1                  ;DISPLAY CHAR.
        JMP     SHORT QUIT
GOTTAB: MOV     AL,COLCNT               ;GET COLUMN COUNT
        MOV     BL,AL
        CALL    NEXTTAB                 ;COMPUTE NEXT TAB STOP
        SUB     AL,BL                   ;GET DISTANCE TO IT
        MOV     CL,AL
        MOV     CH,0
GOTAB:  MOV     AL,SPACE                ;PRINT SPACES TO NEXT TAB STOP
        PUSH    CX
        CALL    CTYPE1
        POP     CX
        LOOP    GOTAB
QUIT:   POP     BX                      ;RESTORE
        POP     DX                      ;..THE REGISTERS
        POP     CX
        RET
CTYPE   ENDP

;       SHOW 1 CHAR. (IN AL)
;       DOESN'T SHOW PAST 80TH COLUMN
;       ENTRY:  AL = CHAR.

CTYPE1  PROC    NEAR
        IF      IBM
        CMP     AL,SPACE
        JGE     NOTCTRL                 ;IF NOT CONTROL CHAR. (CR OR LF)
CTRL:   CALL    UPDATE                  ;CR OR LF, UPDATE LINE & COL. COUNTS
        JMP     SHORT MOVE              ;JUST MOVE CURSOR
NOTCTRL:
        MOV     BL,BYTE PTR COLCNT
        CMP     BL,BYTE PTR SCRSIZE
        JG      UPDATE                  ;IF PAST LAST COL., DON'T SHOW CHAR.
        PUSH    AX
        MOV     AH,10                   ;BIOS FUNCTION #
        MOV     BH,BYTE PTR VPAGE       ;PAGE #
        MOV     CX,1                    ;# CHARS. TO WRITE
        INT     BIOS                    ;WRITE USING BIOS
        POP     AX
        CALL    UPDATE                  ;UPDATE LINE AND COL. COUNTS
MOVE:
        PUSH    AX
        MOV     DH,BYTE PTR LINCNT      ;LINE COUNT IN DH
        MOV     DL,BYTE PTR COLCNT      ;COLUMN COUNT IN DL
        DEC     DL                      ;-1 CAUSE COLUMNS START AT 1
        CALL    MOVEXY                  ;MOVE CURSOR USING BIOS
        POP     AX
        RET
        ELSE
        CMP     BYTE PTR COLCNT,80      ;REACHED 80TH COL?
        JG      UPDATE                  ;YES, DON'T SHOW CHAR.
        CALL    COUT                    ;DISPLAY CHAR. USING DOS
        ENDIF
UPDATE:
        CMP     AL,CR                   ;IF CHAR. = CR,
        JE      ZEROCOL                 ;ZERO COL. COUNT
        CMP     AL,LF                   ;DON'T COUNT LF
        JE      SKIPINC                 ;AS PRINTING CHAR.
        INC     BYTE PTR COLCNT         ;PRINTING CHAR., BUMP COLUMN COUNT
        RET
SKIPINC:
        INC     BYTE PTR LINCNT         ;GOT LINE FEED, BUMP LINE COUNT
        RET
ZEROCOL:
        MOV     BYTE PTR COLCNT,1
        RET
CTYPE1  ENDP

;       NEXTTAB = COMPUTE NEXT STANDARD TAB STOP
;
;       ENTRY:  (AL) = CURSOR POSITION
;       EXIT:   (AL) = POSITION OF NEXT TAB
;       USES:   AL,FLAGS
;
NEXTTAB PROC    NEAR
        PUSH    CX
        DEC     AL
        MOV     CH,AL
        AND     AL,00000111B
        MOV     CL,8
        SUB     CL,AL
        ADD     CH,CL
        MOV     AL,CH
        INC     AL
        POP     CX
        RET
NEXTTAB ENDP

XCMD:
EXIT0:  CALL    CLRSCR                  ;CLEAR THE SCREEN
EXIT1:  CALL    CLRCO                   ;CLEAR ANY TYPE-AHEAD
EXIT2:  MOV     AH,EXIT
        INT     DOS                     ;RETURN TO DOS
ENDCODE EQU     $

        END     ENTRY

TAIL.ASM

        TITLE TAIL - A FILTER FOR MSDOS2
        PAGE 55,132
;**********************************************************************
;       THIS PROGRAM PRINTS THE LAST FEW LINES OF A TEXT FILE.
;
;       USAGE:  TAIL -n filename
;               -n is optional, limits output to n lines (20 default)
;
;       BY: JON DART
;           3012 HAWTHORN ST.,
;           SAN DIEGO, CA 92104
;
;	VERSION 1.8: 02-OCT-87 ASSEMBLES UNDER MASM 5.0
;	VERSION 1.7: 10-JAN-87 HANDLES AMBIG. FILE NAMES, MULTIPLE FILES
;	VERSION 1.6: 14-AUG-86 HANDLES -n UP TO 65535, NOW BUFFERS OUTPUT
;	VERSION 1.5: 06-JUL-86 CHANGES TO MEMORY ALLOC, + NOW ASSEMBLES UNDER
;		     MASM 4.0
;       VERSION 1.4: 04-FEB-86 FIXED BUG, NOW READS FROM STDIN CORRECTLY
;       VERSION 1.3: 16-JAN-86 MORE KOSHER MEMORY USAGE, NOW .EXE FILE
;       VERSION 1.2: 27-SEP-85 USES STD. INPUT IF NO FILE NAME GIVEN
;
;       CONVERTED FROM CP/M-80 TO MSDOS, 16-SEP-85
;
;       VERSION 1.1: 15-MAR-85
;       VERSION 1.0: 15-JAN-85
;
;   TO BUILD TAIL.EXE:
;	MASM TAIL,TAIL,NUL,NUL
;	LINK TAIL,TAIL,NUL,ASM

INBUFSIZE EQU   8192                    ;SIZE OF INPUT BUFFER
OUTBUFSIZE EQU	512			;SIZE OF OUTPUT BUFFER
MAXARGS	EQU	40			;MAX. # COMMAND LINE ARGUMENTS
PRGSIZE	EQU 	800H			;MAX. SIZE OF PROGRAM + FIXED DATA

        .XLIST
        INCLUDE ASCII.DEF
        INCLUDE MSDOS2.DEF
	INCLUDE MACROS.DEF
        .LIST

ERROR   MACRO   ERRNUM                  ;SHOW ERROR MESSAGE
	PUSH	DS
	MOV	AX,DGROUP
	MOV	DS,AX
        MOV     DX,OFFSET MSG&ERRNUM
	CALL	ERRORMSG
	POP	DS
        ENDM

	DOSSEG
	.MODEL 	SMALL

;       MEMORY DEFINITIONS:
;
	.DATA
ENDTXT  DW      1 DUP (?)               ;OFFSET TO END OF TEXT IN INPUT BUFFER
NREAD   DW      1 DUP (?)               ;NUMBER OF BYTES READ
SIZEHI  DW      1 DUP (?)               ;HI WORD OF FILE SIZE
SIZELO  DW      1 DUP (?)               ;LO WORD OF FILE SIZE
POSHI	DW	1 DUP (?)		;HI WORD OF FILE POSITION
POSLO	DW	1 DUP (?)		;LO WORD OF FILE POSITION
NUMARGS	DW	1 DUP (?)		;NUMBER OF COMMAND LINE ARGUMENTS
INHANDLE DW     0                       ;INPUT FILE HANDLE
                                        ;(STD. INPUT BY DEFAULT)
NUMLIN  DW      20                      ;NUMBER OF LINES TO LIST
NUMCNT	DW	1 DUP (?)		;COUNTS NUMBER OF LINES TO GO
ONEFILE	DB	1			;FLAG, SET 0 WHEN >1 FILE
ATSTART DB	0			;FLAG, SET <>0 WHEN AT START OF FILE
AMTTOREAD DW	INBUFSIZE		;AMOUNT TO READ ON NEXT READ OP
OUTNDX	DW	0			;INDEX TO NEXT FREE SLOT IN OUTPUT BUFFER
MSG1    DB      CR,LF,"tail: can't open: ",0
MSG1E   DB      CR,LF,0
MSG2    DB      CR,LF,"tail: read error.",CR,LF,0
MSG4	DB	CR,LF,"tail usage: tail -n file1 file2 ...",CR,LF,0
MSG5	DB	CR,LF,"tail: too many command line arguments",CR,LF,0
MSG6	DB	CR,LF,"tail: unknown pathname ",0

	.STACK
        DB      512 DUP (?)

;	UNINITIALIZED DATA:

	.DATA?
INBUF	DB      INBUFSIZE DUP (?)	;INPUT BUFFER
OUTBUF	DB      OUTBUFSIZE DUP (?)	;OUTPUT BUFFER
PREFIX	DB      65 DUP (?)         	;DRIVE/DIRECTORY PREFIX
SPATH	DB      65 DUP (?)		;SEARCH PATH
FILENAME DB     65 DUP (?)		;FILE NAME
ARGPTRS DB 	(2*MAXARGS) DUP (?)	;POINTERS TO COMMAND LINE ARGUMENTS
ARGBUF	DB 	300 DUP (?)  		;BUFFER FOR COMMAND LINE ARGUMENTS
DTA	DB      128 DUP (?)		;DATA TRANSFER AREA FOR DOS
MAXMEM	EQU	DTA+128

	.CODE
        EXTRN   SKIPSP:NEAR,DTOBIN:NEAR,CPYCNT:NEAR
        EXTRN   ERRORMSG:NEAR,TYPTX:NEAR,COUT:NEAR
	EXTRN	FIXPATH:NEAR,GETARGS:NEAR,TYPE_UFN:ABS,TYPE_UNK:ABS

ENTRY:
        TEST_DOS2                       ;TEST FOR DOS 2.0, EXIT IF DOS 1
MEMOK:
	MOV	AX,DGROUP
	MOV	ES,AX 		 	;SET EXTRA SEG TO POINT TO DATA
        MOV     BX,(80H)         	;GET BYTE COUNT FOR COMMAND LINE
        CMP     BYTE PTR [BX],0         ;TEST IT
        JNE     L_1                     ;IF NOT 0
	MOV	DS,AX
        JMP     START			;NO ARGUMENTS, USE STDIN
L_1:
        PUSH    BX
        MOV     DL,BYTE PTR [BX]
        MOV     DH,0
        ADD     BX,DX
        INC     BX
        MOV     BYTE PTR [BX],0         ;PUT 0 BYTE AT END OF COMMAND LINE
        POP     BX
        INC     BX			;POINT TO START OF COMMAND LINE
	MOV	SI,OFFSET ARGPTRS
	MOV	DI,OFFSET ARGBUF
	MOV	CX,MAXARGS
	CALL	GETARGS			;COLLECT COMMAND LINE ARGUMENTS
	JNB	SHORT L_2		;IF OK
        ERROR   5	                ;TOO MANY ARGUMENTS
        JMP     EXIT2
L_2:
	MOV	AX,DGROUP
	MOV	DS,AX			;SET DATA SEG
	MOV	WORD PTR NUMARGS,CX	;SAVE # OF ARGUMENTS
	CMP	CX,0
	JNE	SHORT L_2A		;IF SOME ARGUMENT
	JMP	START			;NO ARGUMENTS, USE STDIN
L_2A:
	MOV	SI,OFFSET ARGPTRS
	MOV	BX,WORD PTR [SI]	;POINT TO 1ST ARGUMENT
	MOV	AL,BYTE PTR [BX]	;GET 1ST CHAR.
        CMP     AL,'-'
        JE      L_3                     ;IF SWITCH SPECIFIED
	MOV	SI,OFFSET ARGPTRS
	MOV	CX,WORD PTR NUMARGS	;GET # OF ARGUMENTS
        JMP     GETFILES		;IF NO SWITCH
L_3:
        INC     BX
        MOV     CL,0
        PUSH    BX
CD:     MOV     AL,BYTE PTR [BX]     	;HAVE NUMBER, COUNT NUMBER OF DIGITS
        CMP     AL,'0'
        JC      NODGT
        CMP     AL,'9'+1
        JNC     NODGT
        INC     BX
        INC     CL
        JMP     SHORT CD
NODGT:
        POP     BX
        MOV     CH,0                    ;CL HOLDS DIGIT COUNT
        CALL    DTOBIN                  ;CONVERT NUMBER TO BINARY
        JNC     L_4                     ;IF OK
	JMP	SHORT USE
L_4:
        CMP     AX,0                    ;IS NUMBER 0?
        JNE     NOT0                    ;NO.
        JMP     EXIT2                   ;YES, IT IS, JUST EXIT
NOT0:
        MOV     WORD PTR NUMLIN,AX      ;STORE # LINES
	MOV	SI,OFFSET ARGPTRS+2
	MOV	CX,WORD PTR NUMARGS	;GET # OF ARGUMENTS
	DEC	CX			;-1 CAUSE OF SWITCH
	JMP	SHORT GETFILES
USE:
        ERROR   4	                ;TELL USER HOW TO USE
        JMP     EXIT2
START:
	CALL	TAIL			;SHOW THE "TAIL"
	JMP	EXIT2
GETFILES:
	MOV	AX,DGROUP
	MOV	DS,AX
	CMP	CX,0			;NO FILE ARGUMENTS?
	JNE	GOT1
	JMP	START			;NO, JUST USE STDIN
GOT1:
	CMP	CX,1			;>1 ARG?
	JE	ONLY1			;NOPE
	MOV	BYTE PTR ONEFILE,0	;>1 ARG
ONLY1:
ARGLUP:	MOV	BX,WORD PTR [SI]	;BX POINTS TO FILE NAME
	PUSH	CX
	PUSH	SI
	CALL	DOARG
	POP	SI
	POP	CX
SKIPSW:
	ADD	SI,2
	LOOP	ARGLUP
	JMP	EXIT2

;	DOARG = PROCESS 1 ARGUMENT
;	ES:BX POINTS TO IT

DOARG	PROC 	NEAR
	MOV	AH,SET_DTA
	MOV	DX,OFFSET DTA
	INT	DOS			;SET DTA
        MOV	CX,OFFSET SPATH
        MOV	DX,OFFSET PREFIX
        PUSH    BX
        CALL    FIXPATH                 ;PARSE PATHNAME
	POP	BX
	CMP	AL,TYPE_UNK
	JE	BADNAME
	CMP	AL,TYPE_UFN
	JE	GOTNAME			;IF UNAMBIG. FILE NAME
	MOV	BYTE PTR ONEFILE,0	;AMBIG. FILE NAME OR DIR, FLAG IT
	JMP	SHORT GOTNAME
BADNAME:
	MOV	DX,OFFSET MSG6
	CALL	ERRORMSG		;BAD FILE NAME, SHOW MSG.
	MOV	DX,BX
	CALL	ERRORMSG		;SHOW FILE THAT CAUSED IT
	RET
GOTNAME:
        MOV     DX,OFFSET SPATH        	;POINT TO SEARCH PATH
        MOV     CX,31H                  ;SET SEARCH ATTRIBUTES
        MOV     AH,FIND_FIRST
        INT     DOS                     ;SEARCH FOR 1ST MATCH
	JB	BADNAME			;IF NOTHING FOUND
GOTFILE:
	MOV	SI,OFFSET PREFIX
	MOV	DI,OFFSET FILENAME
	MOV	CX,65
	CALL	CPYCNT			;COPY PREFIX TO FILE NAME AREA
        DEC     DI                      ;BACK UP OVER NULL
        MOV     SI,(OFFSET DTA)+30     	;POINT TO FILE NAME WE FOUND
        CMP     [SI],BYTE PTR '.'       ;DOES IT START WITH .? -
        JNE     NOTDOT                  ;- NO
	JMP	SHORT SKIPFILE		;YES, JUST SKIP IT
NOTDOT:
	CALL	CPYCNT			;COPY FILE WE FOUND AFTER PREFIX
	CALL	DOFILE			;DO 1 FILE
SKIPFILE:
        MOV     AH,FIND_NEXT
        INT     DOS                     ;FIND NEXT MATCH, IF ANY
        JC      NOMORE                  ;IF NONE
        JMP     GOTFILE                 ;GOT ONE, BACK TO TOP OF LOOP
NOMORE:
	RET
DOARG	ENDP

DOFILE  PROC	NEAR
        MOV     DX,OFFSET FILENAME      ;DX POINTS TO FILE NAME
        MOV     AL,READ_ACCESS
        MOV     AH,DOS2_OPEN
        INT     DOS                     ;TRY TO OPEN FILE
	JB	BADOPN			;IF ERROR
	MOV	WORD PTR INHANDLE,AX	;SAVE FILE HANDLE
	CMP	BYTE PTR ONEFILE,1	;ONE FILE ONLY?
	JE	NOSHOW			;YES, DON'T SHOW NAME
	CALL	SHOWFILE		;SHOW FILE NAME
NOSHOW:
	CALL	TAIL			;SHOW TAIL OF FILE
	RET
BADOPN:
        MOV     DX,OFFSET MSG1
        CALL    ERRORMSG		;CAN'T OPEN FILE
	MOV	DX,OFFSET FILENAME
	CALL	ERRORMSG		;SHOW NAME
        MOV     DX,OFFSET MSG1E
        CALL    ERRORMSG		;SHOW CR/LF
	RET
DOFILE	ENDP

;	SHOW FILE NAME

SHOWFILE PROC	NEAR
	MOV	AX,"="
	MOV	CX,5
BARS:
	CALL	WRITEBYTE
	LOOP	BARS
	MOV	AX,SPACE
	CALL	WRITEBYTE
	MOV	BX,OFFSET FILENAME
SHOWF:	MOV	AL,BYTE PTR [BX]
	CMP	AL,0
	JE	ENDF
	CALL	WRITEBYTE
	INC	BX
	JMP	SHOWF
ENDF:	MOV	AL,SPACE
	CALL	WRITEBYTE
	MOV	AL,'='
	MOV	CX,5
BARS2:
	CALL	WRITEBYTE
	LOOP	BARS2
	MOV	AL,CR
	CALL	WRITEBYTE
	MOV	AL,LF
	CALL	WRITEBYTE
	RET
SHOWFILE ENDP

;	AFTER A FILE HAS BEEN OPENED, THIS PROCEDURE OUTPUTS ITS "TAIL"

TAIL	PROC	NEAR
	MOV	BYTE PTR ATSTART,0	;CLEAR "AT START" FLAG
	MOV	WORD PTR AMTTOREAD,INBUFSIZE  ;SET INITIAL AMOUNT TO READ
	MOV	AX,WORD PTR NUMLIN	;GET # LINES TO SHOW
	MOV	WORD PTR NUMCNT,AX	;INIT COUNT
        MOV     BX,WORD PTR INHANDLE    ;GET FILE HANDLE
        MOV     DX,0
        MOV     CX,0
        MOV     AL,2
        MOV     AH,LSEEK
        INT     DOS                     ;SEEK TO END OF FILE
        MOV     WORD PTR SIZEHI,DX
        MOV     WORD PTR SIZELO,AX      ;SAVE FILE SIZE
        CMP     DX,0
        JNE     BIG                     ;IF HI WORD >0
        CMP     AX,0
        JNE     L_11
        JMP     CLOSEFILE               ;IF FILE SIZE 0, JUST EXIT
L_11:
        CMP     AX,INBUFSIZE
        JC      SMALL                   ;IF FILE SIZE < BUFFER SIZE
BIG:
        SUB     AX,INBUFSIZE
        JNB     NOBORROW
        DEC     DX
NOBORROW:
        MOV     CX,DX                   ;CX = MSW OF OFFSET
        MOV     DX,AX                   ;DX = LSW OF OFFSET
        MOV     BX,WORD PTR INHANDLE
        MOV     AL,0
        MOV     AH,LSEEK
        INT     DOS                     ;MOVE FILE POINTER BACK BY BUFSIZ
	MOV	WORD PTR POSHI,DX	;SAVE HI WORD OF POSITION
	MOV	WORD PTR POSLO,AX	;SAVE LO WORD OF POSITION
        JMP     FILLBUF
SMALL:
        MOV     CX,0
        MOV     DX,0
        MOV     AL,0
        MOV     BX,WORD PTR INHANDLE
        MOV     AH,LSEEK
        INT     DOS                     ;REWIND TO START OF FILE
	MOV	BYTE PTR ATSTART,1	;SET "AT START" FLAG
FILLBUF:
        MOV     CX,WORD PTR AMTTOREAD   ;CX = # BYTES TO READ
        MOV     BX,WORD PTR INHANDLE    ;BX = FILE HANDLE
        MOV     DX,OFFSET INBUF        	;OFFSET TO BUFFER
        MOV     AH,READ
        INT     DOS                     ;FILL BUFFER FROM FILE
        JNB	READOK			;IF NO ERROR
	JMP	BADREAD			;IF READ ERROR
READOK:
        MOV     WORD PTR NREAD,AX       ;SAVE # BYTES READ
        MOV     CX,AX
        MOV     BX,OFFSET INBUF
SRCEND:
        MOV     AL,BYTE PTR [BX]
        CMP     AL,CTRL$Z
        JE      FOUND                   ;IF ^Z FOUND
        INC     BX
        LOOP    SRCEND
FOUND:
        MOV     WORD PTR ENDTXT,BX      ;SAVE ADDRESS OF END OF TEXT
                                        ;(ACTUALLY, OFFSET FR. DATA SEGMENT)
	JMP	SHORT SRC1
SRC:	DEC	BX
SRC1:
        CMP     BX,OFFSET INBUF         ;AT START OF BUFFER?
        JE      NEWBUF			;YES, BACK UP FURTHER (IF POSSIBLE)
        MOV     AL,BYTE PTR [BX]     	;GET BYTE
        CMP     AL,CR			;IF NOT CR,
	JNE	SRC			;LOOP

	MOV	AX,1			;FOUND CR,
	SUB	WORD PTR NUMCNT,AX	;COUNT 1 LINE
	JNB	SRC			;LOOP IF COUNT STILL >0
        ADD	BX,2                    ;START OF RIGHT LINE FOUND, SKIP CR/LF
	JMP	BEGIN			;BEGIN OUTPUT

NEWBUF: CMP	BYTE PTR ATSTART,0	;SEE IF WE CAN BACK UP MORE
	JNE	BEGIN			;NO, WE CAN'T
	MOV	AX,WORD PTR POSLO	;GET LO WORD OF FILE POSITION
	SUB	AX,INBUFSIZE		;SUBTRACT BUFFER SIZE
	MOV	BX,WORD PTR POSHI	;GET HI WORD OF FILE POSITION
	SBB	BX,0			;IF 'C' FLAG SET, SUBTRACT 1
	JB	TOOFAR			;IF <1 BUFFER LEFT TO GO
	MOV	CX,BX
	MOV	DX,AX			;OFFSET IN CX:DX
	MOV	BX,WORD PTR INHANDLE
	MOV	AL,0
	MOV	AH,LSEEK
	INT	DOS			;MOVE FILE POINTER BACK
	MOV	WORD PTR POSHI,DX
	MOV	WORD PTR POSLO,AX	;SAVE NEW POSITION
	JMP	FILLBUF			;GO FILL BUFFER AGAIN

;	COME HERE WHEN <1 FULL BUFFER REMAINS TO BE READ

TOOFAR:	MOV	AX,WORD PTR POSLO
	MOV	WORD PTR AMTTOREAD,AX	;SET AMT TO READ = AMOUNT UNREAD
	MOV	CX,0
	MOV	DX,0			;MOVE TO START OF FILE
	MOV	BX,WORD PTR INHANDLE
	MOV	AL,0
	MOV	AH,LSEEK
	INT	DOS			;SEEK TO START
	MOV	BYTE PTR ATSTART,1	;SET "AT START" FLAG
	JMP	FILLBUF			;GO FILL BUFFER

;	READY TO OUTPUT LINE(S)

BEGIN:
        MOV     DX,WORD PTR ENDTXT      ;GET END OF TEXT IN DX
SHOW:   CMP     DX,BX                   ;DISPLAY TEXT - TOP OF LOOP
        JE	ENDBUF 			;UNTIL END OF BUFFER
        MOV     AL,BYTE PTR [BX]
        CALL    WRITEBYTE
	CMP	AL,CTRL$Z		;QUIT IF ^Z WRITTEN
	JE	DONE
        INC     BX
        JMP     SHOW
ENDBUF:
        MOV     CX,INBUFSIZE   		;CX = # BYTES TO READ
        MOV     BX,WORD PTR INHANDLE    ;BX = FILE HANDLE
        MOV     DX,0                    ;OFFSET TO BUFFER FROM ES
        MOV     AH,READ
        INT     DOS                     ;FILL BUFFER FROM FILE
        JB	BADREAD			;IF ERROR
	MOV	DX,AX			;SAVE BYTES READ
	CMP	AX,0
	JE	DONE			;DONE IF 0
	MOV	BX,0			;BX = OFFSET TO START OF BUFFER
	JMP	SHOW			;OUTPUT SOME MORE

BADREAD:
        MOV     DX,OFFSET MSG2
        CALL    ERRORMSG
DONE:
CLOSEFILE:
        MOV     BX,WORD PTR INHANDLE
        MOV     AH,DOS2_CLOSE
        INT     DOS                     ;CLOSE FILE
	CALL	FLUSH			;FLUSH ANYTHING IN OUTPUT BUFFER
	RET
TAIL	ENDP

EXIT2:
        MOV     AH,EXIT
        INT     DOS                     ;EXIT TO DOS

; 	WRITE A BYTE TO THE OUTPUT
;
WRITEBYTE PROC 	NEAR
	PUSH	BX
	PUSH	CX
	PUSH	DX
	MOV	BX,WORD PTR OUTNDX
	MOV	BYTE PTR [BX+OUTBUF],AL
	INC	BX
	MOV	WORD PTR OUTNDX,BX
	CMP	BX,OUTBUFSIZE
	JL	NOTFUL
	CALL	FLUSH
NOTFUL:
	POP	DX
	POP	CX
	POP	BX
	RET
WRITEBYTE ENDP

;	FLUSH - WRITE OUTPUT BUFFER

FLUSH	PROC	NEAR
	PUSH	AX
	MOV	CX,WORD PTR OUTNDX
	MOV	DX,OFFSET OUTBUF
	MOV	BX,1
	MOV	AH,WRITE
	INT	DOS		;WRITE TO OUTPUT
	POP	AX
	MOV	WORD PTR OUTNDX,0
	RET
FLUSH	ENDP

        END     ENTRY


WC.ASM

        TITLE WC - WORD COUNT UTILITY
        PAGE 55,132
;***********************************************************************
;       THIS PROGRAM COUNTS BYTES, WORDS AND LINES IN A TEXT FILE.
;
;       USAGE:  WC file1 file2 ...
;               defaults to standard input if no file name given.
;		file names can be ambiguous
;
;       REQUIRES MSDOS 2 OR HIGHER
;
;       BY: JON DART
;           DEPARTMENT OF ANTHROPOLOGY
;           UCSD C-001
;	    LA JOLLA, CA  92093
;
;	VERSION 1.5, 08-May-88 BUG FIX IN BYTE COUNT
;	VERSION 1.4, 24-OCT-87 ASSEMBLES UNDER MASM 5.0
;	VERSION 1.3, 10-JAN-87 ADDS WILDCARDS, MULTIPLE FILENAMES
;	VERSION 1.2, 06-JUL-86
;       VERSION 1.1, 16-JAN-86
;       VERSION 1.0, 08-NOV-85
;
;	TO BUILD WC.EXE:
;	    MASM WC,WC,NUL,NUL
;	    LINK WC,WC,NUL,ASM

TRUE	EQU	1
FALSE	EQU	0
SILENT	EQU	FALSE			;"TRUE" FOR UNLABELED OUTPUT (LIKE UNIX)
BUFSIZE EQU     8192                    ;SIZE OF FILE BUFFER
MAXARGS	EQU	40			;MAX. # OF COMMAND LINE ARGUMENTS
M       EQU     BYTE PTR 0[BX]

        .XLIST
        INCLUDE ASCII.DEF
        INCLUDE MSDOS2.DEF
	INCLUDE MACROS.DEF
        .LIST

	DOSSEG
	.MODEL SMALL

	.STACK
        DB 512 DUP (?)

;       INITIALIZED DATA AREA:
;
	.DATA
NUMARGS DW	0			;NUMBER OF COMMAND LINE ARGUMENTS
INHANDLE DW	0                       ;INPUT FILE HANDLE
                                        ;(DEFAULTS TO STD. INPUT)
NUMLINE DD      0 			;NUMBER OF LINES
NUMWORD DD      0                       ;NUMBER OF WORDS
NUMBYTE DD      0                       ;NUMBER OF BYTES
ONEFILE	DB	1			;SET 1 IF ONE UNAMBIG. ARGUMENT
PREV    DB      0                       ;PREVIOUS CHAR., USED IN WORD COUNT
TXT1    DB      ' bytes, ',0
TXT2    DB      ' words, ',0
TXT3    DB      ' lines.',0
MSG1    DB      CR,LF,"wc: can't open: ",0
MSG1E   DB      CR,LF,0
MSG2    DB      CR,LF,"wc: read error.",CR,LF,0
MSG4	DB	CR,LF,"wc: unknown pathname: ",0
MSG5	DB	CR,LF,"wc: too many arguments.",CR,LF,0

;	UNINITIALIZED DATA AREA:

	.DATA?
BUFFER  DB      BUFSIZE DUP (?)         ;OFFSET TO BUFFER (FROM ES)
ARGPTRS DB	(MAXARGS*2) DUP (?)	;POINTERS TO COMMAND LINE ARGS
ARGBUF	DB	512 DUP (?)		;BUFFER FOR COMMAND LINE ARGS
SPATH	DB	65 DUP (?)		;SEARCH PATH
PREFIX	DB	65 DUP (?)		;SEARCH PREFIX
FILENAME DB	65 DUP (?)		;COMPOSITE FILE NAME (PREFIX + NAME)
DTA	DB	128 DUP (?)		;DISK TRANSFER ADDRESS
BUFEND	EQU	DTA + 128		;END OF BUFFER SPACE

	.CODE
        EXTRN   PRTSTR:NEAR,COUT:NEAR,ERRORMSG:NEAR,CRLF:NEAR
        EXTRN   TYPTX:NEAR,SKIPSP:NEAR,CPYCNT:NEAR,ZERO:NEAR
        EXTRN   PRINTDD:NEAR
	EXTRN	GETARGS:NEAR
	EXTRN	FIXPATH:NEAR
	EXTRN	TYPE_UFN:ABS, TYPE_UNK:ABS

ENTRY:
        TEST_DOS2                       ;TEST FOR DOS 2.0, EXIT IF DOS 1
	MOV     AX,DGROUP		;GET DATA SEG
	MOV	ES,AX			;SET EXTRA SEG TO POINT TO DATA
        MOV     BX,80H          	;GET BYTE COUNT FOR COMMAND LINE
        CMP     BYTE PTR [BX],0
        JNE     L_1                     ;IF NOT 0
        MOV     AX,DGROUP
        MOV     DS,AX
        CALL    WC                      ;NO COMMAND LINE INPUT, USE STD. INPUT
	JMP	EXIT2
L_1:
        PUSH    BX
        MOV     DL,M
        MOV     DH,0
        ADD     BX,DX
        INC     BX
        MOV     M,0                     ;PUT 0 BYTE AT END OF COMMAND LINE
        POP     BX
        INC     BX
	MOV	CX,MAXARGS
	MOV	SI,OFFSET ARGPTRS
	MOV	DI,OFFSET ARGBUF
	CALL	GETARGS			;GET COMMAND LINE ARGS
	JB	TOOMANY			;IF TOO MANY ARGS
	MOV	AX,DGROUP
	MOV	DS,AX
	CMP	CX,0			;NO ARGUMENTS?
	JNE	L_2			;YES, HAVE SOME
	CALL	WC			;NO ARGS, JUST READ STDIN
	JMP	EXIT2
TOOMANY:
	MOV	AX,DGROUP		;TOO MANY ARGUMENTS
	MOV	DS,AX
	MOV	DX,OFFSET MSG5
	CALL	ERRORMSG		;SHOW MSG
	JMP	EXIT2			;QUIT
L_2:
	MOV	AX,DGROUP
	MOV	DS,AX
	CMP	CX,1
	JE	GETFILES		;IF JUST ONE ARGUMENT
	MOV	BYTE PTR ONEFILE,0	;CLEAR "ONE ARG" FLAG
GETFILES:
	MOV	SI,OFFSET ARGPTRS
ARGLOOP:
	MOV	BX,WORD PTR [SI]	;DS:BX POINTS TO NEXT ARG
	PUSH	CX
	PUSH	SI
	CALL	DOARG			;DO WC FOR ONE ARGUMENT
	POP	SI
	POP	CX
SKIPSW:
	ADD	SI,2			;ADVANCE TO NEXT ARG
	LOOP	ARGLOOP
	JMP	EXIT2			;ALL DONE

DOARG   PROC	NEAR
	MOV	AH,SET_DTA
	MOV	DX,OFFSET DTA
	INT	DOS			;SET DTA
        MOV	CX,OFFSET SPATH
        MOV	DX,OFFSET PREFIX
        PUSH    BX
        CALL    FIXPATH                 ;PARSE PATHNAME
	POP	BX
	CMP	AL,TYPE_UNK
	JE	BADNAME
	CMP	AL,TYPE_UFN
	JE	GOTNAME			;IF UNAMBIG. FILE NAME
	MOV	BYTE PTR ONEFILE,0	;AMBIG. FILE NAME OR DIR, FLAG IT
	JMP	SHORT GOTNAME
BADNAME:
	MOV	DX,OFFSET MSG4
	CALL	ERRORMSG		;BAD FILE NAME, SHOW MSG.
	MOV	DX,BX
	CALL	ERRORMSG		;SHOW FILE THAT CAUSED IT
	RET
GOTNAME:
        MOV     DX,OFFSET SPATH         ;POINT TO SEARCH PATH
        MOV     CX,31H                  ;SET SEARCH ATTRIBUTES
        MOV     AH,FIND_FIRST
        INT     DOS                     ;SEARCH FOR 1ST MATCH
	JB	BADNAME			;IF NOTHING FOUND
GOTFILE:
	MOV	SI,OFFSET PREFIX
	MOV	DI,OFFSET FILENAME
	MOV	CX,65
	CALL	CPYCNT			;COPY PREFIX TO FILE NAME AREA
        DEC     DI                      ;BACK UP OVER NULL
        MOV     SI,OFFSET (DTA+30)      ;POINT TO FILE NAME WE FOUND
        CMP     [SI],BYTE PTR '.'       ;DOES IT START WITH .? -
        JNE     NOTDOT                  ;- NO
	JMP     SHORT SKIPFILE 		;YES, JUST SKIP IT
NOTDOT:
	CALL	CPYCNT			;COPY FILE WE FOUND AFTER PREFIX
	CALL	DOFILE			;DO 1 FILE
SKIPFILE:
        MOV     AH,FIND_NEXT
        INT     DOS                     ;FIND NEXT MATCH, IF ANY
        JC      NOMORE                  ;IF NONE
        JMP     GOTFILE                 ;GOT ONE, BACK TO TOP OF LOOP
NOMORE:
	RET
DOARG	ENDP

;	DOFILE = PROCESS 1 FILE (NAME IN ES:FILENAME)

DOFILE  PROC	NEAR
        MOV     DX,OFFSET FILENAME      ;DX POINTS TO FILE NAME
        MOV     AL,READ_ACCESS
        MOV     AH,DOS2_OPEN
        INT     DOS                     ;TRY TO OPEN FILE
        JNC     NAMEOK                  ;IF OK
        JMP     SHORT BADOPN
NAMEOK:
        MOV     WORD PTR INHANDLE,AX    ;SAVE FILE HANDLE
	CMP	BYTE PTR ONEFILE,1
	JE	NOSHOW
	CALL	SHOWFILE		;SHOW FILE NAME (ONLY IF >1 SPECIFIED)
NOSHOW:
	CALL	WC			;SHOW BYTE, WORD, LINE COUNTS FOR FILE
	RET
BADOPN:
        MOV     DX,OFFSET MSG1
        CALL    ERRORMSG                ;BAD OPEN, SHOW MSG.
        MOV     DX,OFFSET FILENAME      ;DX POINTS TO FILE NAME
        CALL    ERRORMSG                ;SHOW IT
	RET
DOFILE	ENDP

;	SHOW FILE NAME

SHOWFILE PROC	NEAR
	MOV	AX,"="
	MOV	CX,5
BARS:
	CALL	COUT
	LOOP	BARS
	MOV	AX,SPACE
	CALL	COUT
	MOV	BX,OFFSET FILENAME
SHOWF:	MOV	AL,BYTE PTR ES:[BX]
	CMP	AL,0
	JE	ENDF
	CALL	COUT
	INC	BX
	JMP	SHOWF
ENDF:	MOV	AL,SPACE
	CALL	COUT
	MOV	AL,'='
	MOV	CX,5
BARS2:
	CALL	COUT
	LOOP	BARS2
	CALL	CRLF
	RET
SHOWFILE ENDP

WC 	PROC NEAR
	MOV	BYTE PTR PREV,0		;CLEAR "PREVIOUS CHAR"
	MOV	DI,OFFSET NUMLINE
	MOV	CX,12
	CALL	ZERO			;CLEAR COUNTS
FILLBUF:
        MOV     CX,BUFSIZE              ;CX = # BYTES TO READ
        MOV     BX,WORD PTR INHANDLE    ;BX = FILE HANDLE
        MOV     DX,OFFSET BUFFER
        MOV     AH,READ
        INT     DOS                     ;FILL BUFFER FROM FILE
        JC      BADREAD                 ;IF ERROR
        MOV     BX,OFFSET BUFFER
        CMP     AX,0
        JNE     GOTTEXT                 ;IF SOMETHING READ
        JMP     DONE

BADREAD:
        MOV     DX,OFFSET MSG2
        CALL    ERRORMSG
        JMP     CLOSEFILE

GOTTEXT:
	PUSH	AX
        ADD     AX,BX                   ;COMPUTE ADDRESS OF END OF BUFFER
        MOV     DX,AX                   ;SAVE IN DX
	POP	AX
	MOV	SI,OFFSET NUMBYTE
        ADD     WORD PTR [SI],AX        ;UPDATE BYTE COUNT
        JNC     NOCARRY
        INC     WORD PTR [SI+2]         ;IF CARRY
NOCARRY:
        MOV     DI,OFFSET NUMLINE       ;NUMBER OF LINES
        MOV     SI,OFFSET NUMWORD	;NUMBER OF WORDS
BEGIN:
COUNT:  CMP     DX,BX                   ;COUNT BYTES AND LINES
        JNE     L_12                    ;UNTIL END OF BUFFER
        JMP     FILLBUF                 ;GO FILL BUFFER AGAIN
L_12:
        MOV     AL,BYTE PTR [BX]     	;GET BYTE FROM BUFFER
        AND     AL,7FH                  ;STRIP HI BIT
        INC     BX
        CMP     AL,SPACE                ;IS SPACE?
        JE      WCOUNT                  ;YES, POSSIBLE END OF WORD
        CMP     AL,CR                   ;IS CR?
        JNE     COUNT2                  ;NO, IGNORE CHAR.
COUNTLINE:
        INC     WORD PTR [DI]        	;COUNT A LINE
        JNC     WCOUNT
        INC     WORD PTR [DI+2]		;IF CARRY, INCREMENT HI WORD
WCOUNT: CMP     BYTE PTR PREV,0         ;SAW A SPACE OR CR, WAS PREV. CHAR. NULL?
        JE      COUNT2
        CMP     BYTE PTR PREV,CR        ;OR CR?
        JE      COUNT2
        CMP     BYTE PTR PREV,LF        ;OR LF?
        JE      COUNT2
        CMP     BYTE PTR PREV,SPACE     ;OR SPACE?
        JE      COUNT2
        CMP     BYTE PTR PREV,TAB       ;OR TAB?
        JE      COUNT2
        INC     WORD PTR [SI]           ;NO, ASSUME END OF WORD
        JNC     COUNT2
        INC     WORD PTR [SI+2]
COUNT2:
        MOV     BYTE PTR PREV,AL
        JMP     COUNT
DONE:
        MOV     AX,WORD PTR NUMBYTE
        MOV     SI,AX
        MOV     AX,WORD PTR NUMBYTE+2
        MOV     DI,AX
        MOV     AL,0
        CALL    PRINTDD
	IF	SILENT
	MOV	AL,SPACE
	CALL	COUT
	ELSE
	MOV	DX,OFFSET TXT1
	CALL	PRTSTR
	ENDIF
        MOV     AX,WORD PTR NUMWORD
        MOV     SI,AX
        MOV     AX,WORD PTR NUMWORD+2
        MOV     DI,AX
        MOV     AL,0
        CALL    PRINTDD
	IF 	SILENT
	MOV	AL,SPACE
	CALL	COUT
	ELSE
	MOV	DX,OFFSET TXT2
	CALL	PRTSTR
	ENDIF
        MOV     AX,WORD PTR NUMLINE
        MOV     SI,AX
        MOV     AX,WORD PTR NUMLINE+2
        MOV     DI,AX
        MOV     AL,0
        CALL    PRINTDD
	IF	NOT SILENT
	MOV	DX,OFFSET TXT3
	CALL	PRTSTR
	ENDIF
	CALL	TYPTX
	DB	CR,LF+200Q
CLOSEFILE:
        MOV     BX,WORD PTR INHANDLE
        CMP     BX,0                    ;WERE WE READING STD. INPUT?
        JE      NOCLOSE                 ;YES, NO NEED TO CLOSE
        MOV     AH,DOS2_CLOSE
        INT     DOS                     ;CLOSE FILE
NOCLOSE:
	RET
WC	ENDP

EXIT2:
        MOV     AH,EXIT
        INT     DOS                     ;EXIT TO DOS

        END     ENTRY

UTIL.DOC

     MSDOS UTILITIES BY JON DART - Version 3.3 of 30-Mar-89

       This  disk  contains  a  collection  of  utility programs, including
     several which mimic the operation of unix (tm) commands.

       All of these programs require MSDOS or PCDOS version 2 or above.  To
     run SHOW  and  MODEM, your computer must be IBM-compatible at the BIOS
     level (SHOW can be reassembled to produce a "generic MSDOS"  version).
     LS is supplied  in  both generic and PC-dependent versions.  The other
     programs are not at all machine-specific and should run on  any  MSDOS
     system.

       LS displays  a  sorted directory with file sizes.  There are several
     such programs in the public domain, but all of the ones  I  have  seen
     have deficiencies--they  are  too  big,  too slow, don't show the disk
     free space, can't display system files, and/or use tricks that  render
     them unusable on my computer, which is NOT a perfect IBM clone.  LS is
     designed to be easily adaptable to any "generic MSDOS" system, with at
     most minor modifications.  There are two executable files on the disk:
     LS.EXE and  LS-PC.EXE.  LS.EXE is "generic" and will run on most MSDOS
     computers, while LS-PC will run on IBM PCs, ATs and close clones only.

       LS is used like the DIR command.    To  see  the  contents  of  your
     current directory, just  type  LS.   If you follow LS with one or more
     file names, it will show only files matching those  names  (the  names
     can contain wildcards).    You  can  also  insert  one  or more of the
     following options on the command line:

       -A  displays system and hidden files
       -D  displays information on a directory, not its contents
       -L  displays a "long" listing with file sizes in bytes, times and
           dates
       -N  produces a non-sorted output (with files in the same order they
           are in the directory)
       -R  reverses order of sort
       -S  displays file sizes in kilobytes
       -T  sort by time rather than name of file
       -1  forces single-column output with no paging (like DIR)

       HEAD and TAIL are simple filters that display a few lines  from  the
     start or the end of a file, respectively. The general syntax for these
     programs is:

       HEAD -n file1 file2 ...

       n is the  number  of lines to display: e.g.  "HEAD -50 FOO" displays
     the first 50 lines of "FOO".  Wildcards can be  used  in  file  names.
     The default  number  of lines is 20; the maximum that can be specified
     is 65535.  If no input file is specified, HEAD reads from the standard
     input, so it can be hooked to another program's  output  via  a  pipe.
     TAIL is used similarly.

       WC is a  utility  to count bytes, words and lines in a file.  To use
     it, just type WC followed by the name(s) of the file(s)  you  want  to
     get counts for.    Like  HEAD  and  TAIL,  WC  defaults to reading the
     standard input device if no input file is  specified  on  the  command
     line.

       SHOW  is my adaptation of a CP/M bidirectional file display program.
     It shows a file on the screen, and allows you to  scroll  forward  and
     backwards.  If  all you want to do is see the file, this is often more
     convenient than calling up your editor. Unlike TYPE, SHOW will display
     WordStar (tm) and WordPerfect (tm) files  in  more  or  less  readable
     format. Type SHOW with no arguments to get instructions.

       MV  is  a  general-purpose  program  to  move  files and directories
     between disks and directories.  It is similar to the UNIX (tm) utility
     of the same  name.    With  the  R  switch,  MV   treats   directories
     recursively: that  is,  if  it  is  told  to move a directory, it will
     transfer all files found in that  directory,  AND  all  files  in  any
     subdirectories of it.  Type MV with no arguments to get instructions.

       CP  is  virtually  the  same  as  MV, except that it copies files or
     directories, without  deleting  the  originals.    Type  CP  with   no
     arguments to get instructions.

       RM deletes files and/or directories.  It is similar to the MSDOS DEL
     command,  but  allows  multiple  files to be specified, and supports a
     number of options.  Type RM with no  arguments  to  get  instructions.
     CAUTION: the  F and R switches are potentially dangerous.  Be sure you
     know what you are doing before you use them; if in  doubt,  experiment
     on a "junk" disk first.

       RO  is  a utility to make one or more file read-only. Files that are
     set "read-only" cannot be deleted by the ordinary  MSDOS  DEL  command
     (it will  respond  "File  not  found"  if you try this). They are also
     protected from  deletion by the RM program (unless  the  F  switch  is
     used).  Type RO to get instructions.

       RW  is  a  utility  to  restore one or more files that have been set
     read-only back  to   ordinary   read-write   status.   Type   RW   for
     instructions.

       CRC computes  a cyclic redundancy code for one or more files.  It is
     a good idea to run CRC on your valuable files and record the  results.
     Then  if  you ever suspect that a file has been corrupted in some way,
     re-run CRC and compare  the  results  to  what  you  recorded.    Most
     possible errors  in the file will alter the CRC for the file, enabling
     you to detect that an error has occurred.

       The CRC program accepts one or more file names and writes their  CRC
     codes to the standard output. The output can be redirected into a file
     if you so desire.

       By  default,  CRC  computes  Cyclic  Redundancy Codes using the same
     algorithm used by XMODEM and other communications programs.    If  you
     add  the  -a  switch  to  the  command  line,  it  will  compute codes
     compatible with those computed by archiving utilities such as ARC  and
     PKARC.

       UPDATE is similar to the unix "touch" utility.  It sets the time and
     date of  one  or more files to the current system time and date.  Type
     UPDATE with no arguments for instructions.

       FDUMP is a simple utility for viewing and patching files  in  ASCII,
     hex, decimal or  octal.  It examines one logical sector (128 bytes) at
     a time.  When you run the program, you will be asked for a  file  name
     and then  presented with a menu of commands (you can also invoke FDUMP
     with the name of a file, e.g.  "FDUMP FDUMP.EXE." In  this  case,  you
     don't get the file prompt).  The "dump" commands (A, H, D, and O) show
     the contents of  the current file sector.  The edit command (E) allows
     you to alter the sector contents, and  the  W  command  will  put  the
     changed sector  back  on  the disk, overwriting its previous contents.
     The C command allows you to copy one or more sectors from the file you
     are looking at to a new file.  This command can  be  used  to  recover
     usable data from  files  that  have damaged sectors.  The "+", "-" and
     "S" commands are used to select a new sector to  look  at  or  change.
     The "F" command closes the current file and allows you to select a new
     file.  To exit the program, type X.

       CLEAN, FIXLINES  and DETAB are filter programs.  Like HEAD and TAIL,
     they read from a file (if specified) or from the standard  input,  and
     write to  the  standard  output  device.  To save the output in a text
     file, you must use the redirection operator (>).  CLEAN strips off the
     high bits in a file, and removes all  control  characters  except  for
     carriage return  (^M),  line  feed  (^J), backspace (^H), tab (^I) and
     form feed (^L).  FIXLINES converts files with  lines  ending  in  only
     carriage returns  or  line  feeds  to files with lines ending in CR/LF
     pairs (which is the DOS standard text file  format).    DETAB  expands
     tabs in  a file into spaces; it assumes that tab stops are set every 8
     spaces.  Since these programs are filters, they can be combined  in  a
     pipe, e.g.:

        CLEAN foo | DETAB | FIXLINES >foo2

        processes file  "foo" through all 3 programs and writes the results
     to file "foo2".  I use this combination to convert  the  brain-damaged
     output files created by SPSS-PC into something I can print.

       MODEM is  a simple communications program loosely modelled after the
     classic "MODEM7" series for CP/M.  Far fancier programs are available,
     but this one may be of interest because it includes  source.    Unlike
     most modem  programs,  it  uses BIOS calls to access the COM port, and
     will thus work with semi-compatible computers such as the Tandy  2000.
     However, since  BIOS  calls are slower than directly fiddling with the
     hardware, transfers  at  speeds  above  1200  bps  will  probably   be
     unreliable.  MODEM supports both CRC and Checksum transfers, but there
     is no ASCII transfer mode.

       All  of the programs are supplied "ready to use" in the form of .EXE
     files. It is not necessary to re-assemble or re-compile any of them in
     order to use them.  However, the source code is also provided for  the
     use of anyone who wishes to make custom modifications to the programs.
     The following  information  concerns  the  source code, and will be of
     interest to programmers only.

       The source code for all programs is contained in the  archive  named
     SOURCE.ARC.  Any of the available archiving utilities for the PC (such
     as ARC,  PKARC  and  NARC)  can be used to extract the files from this
     archive.

       LS, HEAD, TAIL, MV, CP, RM and WC are written in  assembly  language
     and were assembled  with  MASM  5.0.    All  of these programs must be
     linked with the utility library ASM.LIB.   The  source  code  for  the
     modules in  ASM.LIB  is in the archive ASM.ARC, which is stored within
     the SOURCE archive.  ASM.ARC also contains some include  files  needed
     for assembling the programs.

       RO,  RW,  CRC,  UPDATE, FDUMP, CLEAN, FIXLINES, DETAB, and MODEM are
     written in Aztec C, and were compiled in the "small model" mode  using
     version 3.40a  of  the  optimizing  compiler.  If you modify them, you
     must recompile and then link them  with  the  library  B.LIB  and  the
     standard Aztec library  C.LIB  (in  that  order).    B.LIB  is a small
     utility library that is provided within the source archive. The source
     for the modules in B.LIB is in B.ARC.  B.ARC also contains some header
     files used by the c programs.

       These programs are PUBLIC DOMAIN and may  be  freely  copied,  used,
     modified,  etc.  for  ANY  purpose  (the  one thing you probably can't
     legally do is to copyright them  yourself).    I  don't  ask  for  any
     donation or contribution  for  them.  The best "donation" you can give
     me is to donate something of your  own  to  the  public  domain,  thus
     improving the pool of available free software.

        I don't  offer  any  guarantee of support for these programs, but I
     will generally fix bugs that  are  reported  to  me.    Any  comments,
     complaints, bug  reports or suggestions for improvement should be sent
     to: Jon Dart, 3012 Hawthorn St., San Diego CA 92104.



Directory of PC-SIG Library Disk #0564

 Volume in drive A has no label
 Directory of A:\

SOURCE   ARC    137544   4-02-89   9:40a
UTIL     DOC     11624   4-02-89   8:00p
CLEAN    EXE      5312  11-16-86   9:50p
CP       EXE      4209   4-02-89   9:34a
CRC      EXE      7712   9-03-88  10:25p
DETAB    EXE      5392  11-16-86   9:55p
FDUMP    EXE     11984  10-04-86   8:48p
FIXLINES EXE      4992  11-16-86   8:33p
HEAD     EXE      2060   1-19-89   7:15p
LS-PC    EXE      3779   4-02-89   9:09a
LS       EXE      3747   4-02-89   9:08a
MODEM    EXE     18864   4-23-87  11:21p
MV       EXE      4323   4-02-89   9:33a
RM       EXE      3231   2-04-89   1:09p
RO       EXE      6176   4-23-87  11:11p
RW       EXE      6192   4-23-87  11:05p
SHOW     EXE      2036  11-21-87  10:02p
TAIL     EXE      2808   1-19-89   7:16p
UPDATE   EXE      6496   4-10-86   9:38p
WC       EXE      2042   1-19-89   7:20p
GO       BAT        38   6-01-88   9:55a
FILES564 TXT      1559   6-01-88  10:08a
GO       TXT       617   5-10-89   2:26p
       23 file(s)     252737 bytes
                       56320 bytes free