home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 December
/
simtel1292_SIMTEL_1292_Walnut_Creek.iso
/
msdos
/
pcmag
/
vol6n11.arc
/
REPEATS.ASM
next >
Wrap
Assembly Source File
|
1987-03-23
|
25KB
|
532 lines
; REPEATS.ASM
; Format: REPEATS [d:][/P]
; Returns with duplicate file names.
CODE SEGMENT ;*************************
ASSUME CS:CODE,DS:CODE ;* *
ORG 100H ;* REMEMBER TO EXE2BIN *
;* *
START: JMP BEGINNING ;*************************
; DATA AREA
; ---------
COPYRIGHT DB 'Copyright 1987 Ziff-Davis Publishing Co.',1AH
PROGRAMMER DB 'Michael J. Mefford'
GLOBAL DB '*.*',0
PARENT DB '..',0
ROOT DB '\',0
DIR_OFFSET DW 0
CURRENT_DRIVE DB ?
DIR_COUNT DW 0
FILE_COUNT DW 0
FILE_START DW ?
END_OF_SPACE DW ?
PRINT_FLAG DB 0
MATCH_FLAG DB 0
DISPLAY_FLAG DB 0
SOURCE_FLAG DB 0
SEARCHING DB 13,10,'Searching ',0
FOR DB ' for duplicate file names.',13,10
DB 'Loading directories...patience please',13,10,0
LOADING_FILE DB 'Loading file names...',0
DIRECTORY DB 13,10,' Directory ',0
NO_MATCHES DB 7,13,10,'No duplicates found.',13,10,0
DONE DB 7,13,10,13,10,'Search sucessfully completed.',13,10,0
NOT_ENOUGH DB 'Not enough memory',0
DIR_ATTRIBUTE EQU 10H
ALL_FILES EQU 6H
;-------------------------------------------------------------;
; First allocate 64K and move the stack to the end of code. ;
; Then get the current drive so we can restore upon exit. ;
; Next check for a drive request and/or printer echo switch. ;
; Save current directory. Display search request. And finally ;
; initialize the directory level subscripts to one. ;
;-------------------------------------------------------------;
BEGINNING: CLD ;All string instructions forward.
MOV BX,1000H ;64K (1000h paragraphs).
MOV AH,4AH ;Modify allocated memory.
INT 21H
MOV SI,OFFSET NOT_ENOUGH ;Exit if 64K not available.
JNC GOT_64K
JMP PRINT_DONE
GOT_64K: MOV SP,OFFSET STACK_SPACE+1FFH ;Move stack to end of code.
MOV AH,19H ;Get the current drive
INT 21H
MOV CURRENT_DRIVE,AL ; and save.
MOV CL,DS:[80H] ;Get the length of the argument.
CMP CL,0 ;If it's zero then no parameters.
JZ NO_PARA
XOR CH,CH ;Else, zero in high half.
MOV SI,81H ;Point to parameter.
NEXT_PARA: CMP BYTE PTR [SI],':' ;Is it colon?
JNZ CK_SWITCH ;If no, check switch.
MOV DL,[SI-1] ;Else, get drive request.
AND DL,5FH ;Capitalize.
SUB DL,'A' ;Convert to DOS format.
MOV AH,0EH ;And change drive.
INT 21H
CK_SWITCH: CMP BYTE PTR [SI],'/' ;Is it switch request?
JNZ LOOP_PARA ;If no, next parameter.
MOV DL,[SI+1] ;Else, get request.
AND DL,5FH ;Capitalize.
CMP DL,'P' ;Is it printer request?
JNZ LOOP_PARA ;If no, next parameter.
MOV PRINT_FLAG,1 ;Else, flag printer echo.
LOOP_PARA: INC SI ;Point to next byte
LOOP NEXT_PARA ; and get next byte.
NO_PARA: MOV BYTE PTR CURRENT_DIR,'\'
MOV SI,OFFSET CURRENT_DIR+1 ;Save the current directory.
CALL GET_DIR
MOV SI,OFFSET SEARCHING ;Print "Searching ".
CALL WRITE_STRING
MOV AH,19H ;Get current drive.
INT 21H
MOV DL,AL
ADD DL,'A' ;Convert to ASCII
CALL DISPLAY ; and display.
MOV DL,':' ;Display the colon.
CALL DISPLAY
MOV SI,OFFSET FOR ;Display balance of intro.
CALL WRITE_STRING
CLD ;All string instructions will be
MOV AL,1 ; done in a forward direction.
MOV CX,100 ;Initialize directory subscripts
MOV DI,OFFSET SUBSCRIPTS ;to one.
REP STOSB
MOV DX,OFFSET DTA ;Point the disk transfer address
CALL CHANGE_DTA ; to the end of code.
;------------------------------------------------------------;
; This routine will systematically change directories up and ;
; down the directory tree, and store the directory names. ;
;------------------------------------------------------------;
MOV SI,OFFSET DIRECTORIES+1 ;Point to storage.
MOV BP,OFFSET SUBSCRIPTS ;Point to subscripts.
MOV DX,OFFSET ROOT ;Point to root.
STORE_DIR: MOV AH,0BH ;Check for Ctrl Break.
INT 21H
CALL CHANGE_DIR
INC DIR_COUNT
MOV BYTE PTR [SI-1],'\' ;DOS fails to preface with "\"
CALL GET_DIR ; so we have to.
ADD SI,65 ;Point to next storage.
ADD DIR_OFFSET,65 ;Update the offset as well.
JMP SHORT FIRST_DIR
PARENT_DIR: CMP BP,OFFSET SUBSCRIPTS ;When we try to return to parent
JZ FILENAMES ; directory and are in root, we
MOV DX,OFFSET PARENT ; are done. Otherwise, change
CALL CHANGE_DIR ; to parent directory.
MOV BYTE PTR DS:[BP],1 ;Put one back in previous level
DEC BP ; and point to parent level.
FIRST_DIR: XOR BL,BL ;BL as pointer to directory no.
MOV DX,OFFSET GLOBAL
MOV CX,DIR_ATTRIBUTE ;Ask for global match.
CALL FIND_FIRST ;Find first matching.
JC PARENT_DIR
CK_DIR: CMP BYTE PTR DTA+21,10H ;If not a directory get next.
JNZ NEXT_DIR
CMP BYTE PTR DTA+30,'.' ;If a dot directory get next.
JZ NEXT_DIR
INC BL ;Increment position in directory.
CMP BL,DS:[BP] ;Continue until new directory.
JNZ NEXT_DIR
INC BYTE PTR DS:[BP] ;Update variables.
MOV DX,OFFSET DTA+30
CALL CHANGE_DIR
INC BP
JMP SHORT STORE_DIR
NEXT_DIR: CALL FIND_NEXT ;Get all subdirectories in current
JC PARENT_DIR ; directory then go to parent.
JMP SHORT CK_DIR
;-----------------------------------------------;
; This routine finds and stores all file names. ;
;-----------------------------------------------;
FILENAMES: CMP DIR_COUNT,1 ;If only one directory
JNZ ALLOCATE ; then can't be any duplicates.
JMP EXIT
ALLOCATE: ADD SI,15 ;Else, adjust for truncation.
MOV CL,4 ;Convert end of current storage
SHR SI,CL ; into segment address.
MOV BX,SI
MOV AH,4AH ;Deallocate memory.
INT 21H
MOV BX,0FFFFH ;See how much memory is left.
MOV AH,48H
INT 21H
MOV AH,4AH ;Ask for it all.
INT 21H
MOV CX,ES ;Get extra segment.
ADD CX,SI ;Change to end of storage.
MOV ES,CX
MOV FILE_START,CX ;Store as start of file storage.
ADD BX,CX ;Add allocated memory.
MOV END_OF_SPACE,BX ;Store end of workspace.
MOV SI,OFFSET LOADING_FILE ;Tell user, loading file names.
CALL WRITE_STRING
MOV DIR_OFFSET,OFFSET DIRECTORIES ;Store directory start.
XOR BP,BP ;BP as directory counter.
FIRST_FILE: MOV AH,0BH ;Check for control break.
INT 21H
INC BP ;Increment directory count.
CMP BP,DIR_COUNT ;Done all directories?
JA DUPLICATES ;If yes, search for duplicates.
MOV DX,DIR_OFFSET ;Else, change directory.
CALL CHANGE_DIR
ADD DIR_OFFSET,65 ;Point to next directory.
MOV DX,OFFSET GLOBAL
MOV CX,ALL_FILES
CALL FIND_FIRST ;Find first matching file.
JNC STORE_FILE ;If empty, next directory.
JMP SHORT FIRST_FILE ;Else, store data.
NEXT_FILE: MOV AX,ES ;Retrieve data segment.
ADD AX,2 ;Point to next storage.
MOV ES,AX
MOV SI,OFFSET NOT_ENOUGH ;Assume out of memory.
CMP AX,END_OF_SPACE ;Is it true?
JB CONTINUE ;If no, continue.
JMP EXIT ;Else, exit.
CONTINUE: CALL FIND_NEXT ;Find next matching.
JC FIRST_FILE ;If done, next directory.
STORE_FILE: INC FILE_COUNT ;Else, increment file count.
MOV SI,OFFSET DTA+22 ;Store filename data.
XOR DI,DI ;Point to first byte.
MOV AX,BP
STOSW ;Store directory number.
MOV CX,4 ;Store file date and size data.
REP MOVSW
MOV CX,12 ;Store 12 bytes of filename.
NEXT_STORE: LODSB ;Get a byte.
CMP AL,0 ;End of filename?
JZ END_STORE ;If yes, finish with blanks.
CMP AL,'.' ;Is it the period?
JNZ STORE_BYTE ;If no, store.
SUB CX,3 ;Else store 3 spaces.
MOV AL,32
REP STOSB
ADD CX,3
JMP SHORT NEXT_STORE ;Get next byte.
STORE_BYTE: STOSB ;Store byte.
LOOP NEXT_STORE ;Get next byte.
END_STORE: MOV AL,32 ;Pad balance with spaces.
REP STOSB
MOV AL,0 ;Tack on a zero for display
STOSB ; control.
JMP SHORT NEXT_FILE ;Get next filename.
;-------------------------------------------------------------;
; This routine does the work of finding duplicate file names. ;
;-------------------------------------------------------------;
DUPLICATES: MOV WORD PTR ES:[0],0FFFFH ;Tack on -1 signature.
MOV SI,OFFSET SEARCHING ;Tell user searching now.
CALL WRITE_STRING
MOV AX,FILE_START ;Retrieve start of file names.
JMP SHORT STORE_SOURCE
NEXT_SOURCE: MOV AH,0BH ;Check for Ctrl Break.
INT 21H
MOV CS:SOURCE_FLAG,0 ;Reset source display flag.
MOV AX,ES
ADD AX,2 ;Increment to next file name.
STORE_SOURCE: MOV DS,AX
MOV ES,AX
CMP WORD PTR DS:[32],0FFFFH ;End of file name storage?
JZ EXIT ;If yes, we are done.
MOV BP,ES:[0] ;Retrieve directory number.
CMP BP,0 ;Have we matched this one before?
JZ NEXT_SOURCE ;If yes, get next.
NEXT_MATCH: MOV AX,DS
ADD AX,2 ;Point to next target file name.
MOV DS,AX
MOV SI,0
LODSW ;Retrieve directory number.
CMP AX,0FFFFH ;End of file storage?
JZ NEXT_SOURCE ;If yes, get next file name.
CMP AX,BP ;In same directory?
JZ NEXT_MATCH ;If yes, get next file name.
MOV SI,10 ;Else, point to source and
MOV DI,10 ; and target file names.
MOV CX,6
REPZ CMPSW ;Are they duplicates?
JNZ NEXT_MATCH ;If no, next file name.
CALL PRINT_MATCH ;Else, print match.
MOV WORD PTR DS:[0],0 ;Flag as matched.
JMP SHORT NEXT_MATCH ;Get next file name.
;------------------------------------------------;
; Exit routine. Restore default drive and ;
; directories. Display appropriate exit message. ;
;------------------------------------------------;
EXIT: PUSH CS ;Restore data segment.
POP DS
MOV DX,OFFSET CURRENT_DIR ;Restore default drive
CALL CHANGE_DIR ; directory
MOV DL,CURRENT_DRIVE ; and drive.
MOV AH,0EH
INT 21H
MOV SI,OFFSET DONE
CMP MATCH_FLAG,1 ;If match was found, display
JZ PRINT_DONE ;"successful"
MOV SI,OFFSET NO_MATCHES ;else, display "none found".
PRINT_DONE: PUSH CS
POP DS
CALL WRITE_STRING
INT 20H ;Terminate.
;*************;
; SUBROUTINES ;
;*************;
;-------------------------------------------;
; These subroutines are DOS function calls. ;
;-------------------------------------------;
GET_DIR: MOV DL,0
MOV AH,47H ;Get current directory.
INT 21H
RET
CHANGE_DIR: MOV AH,3BH ;Change current directory.
INT 21H
RET
CHANGE_DTA: MOV AH,1AH ;Set disk transfer address.
INT 21H
RET
FIND_FIRST: MOV AH,4EH ;Find first matching file.
INT 21H
RET
FIND_NEXT: MOV AH,4FH ;Find next matching file.
INT 21H
RET
;------------------------------------------------------;
; Two subroutines: One adds carriage return; linefeed. ;
; The other pads the file name display with spaces. ;
;------------------------------------------------------;
CR_LF: MOV DL,13
CALL DISPLAY
MOV DL,10
CALL DISPLAY
RET
SPACES: MOV DL,32
CALL DISPLAY
MOV DL,32
CALL DISPLAY
RET
;-----------------------------------------------------;
; This subroutine handles all the output. If /P was ;
; included, output will be echoed to the printer. ;
;-----------------------------------------------------;
DISPLAY: CMP CS:PRINT_FLAG,1 ;If /P, echo to the printer.
JNZ NOT_PRINTER
MOV AH,5 ;Printer output.
INT 21H
NOT_PRINTER: MOV AH,2 ;Display output.
INT 21H
RET
WRITE_IT: MOV DL,AL
CALL DISPLAY
WRITE_STRING: LODSB
CMP AL,0 ;Zero marks end of string.
JNZ WRITE_IT
RET
;------------------------------------------------------------------;
; This subroutine will print the duplicate file name and its path. ;
;------------------------------------------------------------------;
PRINT_MATCH: PUSH DS
MOV CS:MATCH_FLAG,1 ;Flag that we have a duplicate.
CMP CS:SOURCE_FLAG,1 ;Have we displayed source file?
JZ SKIP_SOURCE ;If yes, skip.
CALL CR_LF
MOV CX,39 ;Separate duplicates with line.
PRINT_LINE: MOV DL,'-'
CALL DISPLAY
LOOP PRINT_LINE
MOV CS:SOURCE_FLAG,1 ;Flag displaying source.
MOV AX,ES:[0] ;Retrieve directory number.
CALL PRINT_DIR
MOV SI,ES ;Point to file name
CALL WRITE_FILE ;Display the file name and specs.
SKIP_SOURCE: MOV AX,DS:[0] ;Retrieve directory number.
CALL PRINT_DIR
MOV SI,DS ;Point to file name.
CALL WRITE_FILE
POP DS
RET
;----------------------------------------------;
; This subroutine displays the directory name. ;
;----------------------------------------------;
PRINT_DIR: DEC AX ;Adjust as a pointer.
MOV BL,65 ;Each directory record is 65.
MUL BL
MOV DI,AX
PUSH DS
PUSH CS ;Restore our data segment.
POP DS
MOV SI,OFFSET DIRECTORY ;Point to "Directory".
CALL WRITE_STRING
MOV SI,OFFSET DIRECTORIES ;Point to directory name.
ADD SI,DI
CALL WRITE_STRING
CALL CR_LF ;Add carriage return linefeed.
POP DS
RET
;-------------------------------------------------------------------------;
; This subroutine handles displaying the file name, bytes, date and time. ;
;-------------------------------------------------------------------------;
WRITE_FILE: PUSH DS
MOV DS,SI ;Point to file name segment.
MOV SI,10 ;Point to file name.
CALL WRITE_STRING
FILE_SIZE: MOV DX,DS:[8] ;Get high word of size
MOV AX,DS:[6] ; and low word of size.
MOV CX,10000 ;Divide by 10,000.
DIV CX
PUSH DX ;Save remainder.
MOV BX,AX
CALL NUMBERS ;Display ten thousands.
POP BX ;Retrieve remainder.
CALL THOUSANDS ;Display thousands.
DATE: CALL SPACES ;Format to column 23.
MOV SI,DS:[4] ;Retrieve date.
MOV BX,SI ;Get month four bits.
MOV CL,5
ROR BX,CL
AND BX,15
MOV CS:DISPLAY_FLAG,0 ;And display.
CALL TENS
MOV DL,'-' ;Add hyphen.
CALL DISPLAY
MOV BX,SI
AND BX,31 ;Retrieve day five bits.
CALL TENS ;And display.
MOV DL,'-' ;Add hyphen.
CALL DISPLAY
MOV BX,SI
MOV CL,9 ;Retrieve year seven bits.
ROR BX,CL
AND BX,127
ADD BX,80
CALL TENS ;And display.
TIME: CALL SPACES ;Format to column 33.
MOV SI,DS:[2] ;Get time.
MOV BX,SI
MOV CL,11
ROR BX,CL
AND BX,31 ;Use hour five bits.
PUSH BX
CMP BX,12 ;Past noon?
JBE MERIDIAN
SUB BX,12 ;If yes, subtract 12.
MERIDIAN: CMP BX,0 ;Is it midnight?
JNZ NOT_MIDNIGHT
MOV BX,12 ;If yes, display 12.
NOT_MIDNIGHT: MOV CS:DISPLAY_FLAG,0
CALL TENS ;And display.
MOV DL,':' ;Add colon.
CALL DISPLAY
MOV BX,SI ;Retrieve minutes 6 bits.
MOV CL,5
ROR BX,CL
AND BX,63
CALL TENS ;And display.
MOV DL,'p' ;Assume pm.
POP BX
CMP BX,12 ;Is it noon or later?
JAE PM ;If yes, display "p".
MOV DL,'a' ;Else, display "a".
PM: CALL DISPLAY
POP DS
RET
;----------------------------------------------------------------------------;
; This subroutine displays each decimal position, suppressing leading zeros. ;
;----------------------------------------------------------------------------;
NUMBERS: MOV CS:DISPLAY_FLAG,0 ;Reset the display flag.
MOV CX,10000 ;Get ten thousands by dividing.
CALL DIVIDE
THOUSANDS: MOV CX,1000 ;Get thousands by dividing.
CALL DIVIDE
MOV CX,100 ;Get hundreds by dividing.
CALL DIVIDE
TENS: MOV CX,10 ;Get tens by dividing.
CALL DIVIDE
MOV CX,1 ;Get ones by dividing.
CALL DIVIDE
RET
DIVIDE: MOV AX,BX ;Number in AX
XOR DX,DX ; and zero in DX
DIV CX ; divide by CX
MOV BX,DX ; remainder into BX
MOV DL,AL ; and quotient into DL.
OR CS:DISPLAY_FLAG,AL ;If non zero indicate by flag.
FLAG: CMP CS:DISPLAY_FLAG,0 ;Has there been a display?
JNZ DISP_NUMBER ;If yes, display.
MOV DL,-10H ;Else, display space.
DISP_NUMBER: ADD DL,30H ;Convert hexadecimal to decimal
CALL DISPLAY ;And display.
END_LINE: RET
STACK_SPACE:
DTA EQU STACK_SPACE+200H
SUBSCRIPTS EQU DTA+65
CURRENT_DIR EQU SUBSCRIPTS+100
STORAGE EQU CURRENT_DIR+65
DIRECTORIES EQU STORAGE+44
CODE ENDS
END START