home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
assemblr
/
library
/
sampler0
/
njmove.asm
< prev
next >
Wrap
Assembly Source File
|
1989-01-16
|
46KB
|
2,047 lines
page 60,150
title NJMOVE - Nifty James' MOVE Utility
subttl (C) 1989 Blaszczak
;
; Nifty James' Famous Move Utility
; Version 1.00 of 16 January 1989
; (C) Copyright 1989 By Mike Blaszczak
;
;
; This file is written for the Microsoft Macro Assembler,
; Version 5.10 or higher. The program will not properly
; operate under any DOS version less than 2.00, but will
; work with any PC-DOS or MS-DOS Versions 2.00 and above.
; This is an .EXE file, and should not be run through
; EXE2BIN. The executable file should be packed, or the
; LINK option /EXEPACK should be used.
;
; ----------------------------------------------------------------------------
; ASCII Equates
eos equ 000h ; end-of-string character
bell equ 007h ; bell beeper
bs equ 008h ; back-space
tab equ 009h ; tab
lf equ 00Ah ; line-feed character
cr equ 00Dh ; carriage return
ctrlz equ 01Ah ; control zee (aka, EOF)
eof equ ctrlz
space equ 020h ; blank space
; ----------------------------------------------------------------------------
; DOS Function Calls and constants
INTDOS equ 021h
STDIN equ 0 ; standard output file handle
STDOUT equ 1 ; standard input file handle
NormalAttrib equ 000h ; normal attribute byte (nothing set)
SubDirAttrib equ 010h ; attribute for a directory
PutChar equ 002h ; put character to stdout
GetChar equ 007h ; get char from stdin, with no echo
GetDefDisk equ 019h ; get current default disk drive
SetDTA equ 01Ah ; set the DOS DTA address
FreeSpace equ 036h ; get disk free space
CreateHandle equ 03Ch ; create a file handle
OpenHandle equ 03Dh ; open a file handle
CloseHandle equ 03Eh ; close a file handle
ReadHandle equ 03Fh ; read from a file or device
WriteHandle equ 040h ; write to a file or device
DeleteFile equ 041h ; delete a file name
GetAttrib equ 04300h ; get file attributes
SetAttrib equ 04301h ; set file attributes
IOCTL equ 044h ; I/O control/status call
GetDefDir equ 047h ; get current default directory
Terminate equ 04Ch ; terminate this program
FindFirst equ 04Eh ; find first file matching wildcard
FindNext equ 04Fh ; find next file matching wildcard
RenameFile equ 056h ; rename a file
GetDateTime equ 05700h ; get date or time
SetDateTime equ 05701h ; set date or time
NormalizePath equ 060h ; normalize pathname
GetPSP equ 062h ; get the programs PSP
; note that NormalizePath is an undocumented call. DS:SI points to a
; path specification which may contain a drive specifier. ES:DI points
; to a buffer for the normalized pathname. The file simply fixes the
; "." and ".." references in the file, making the pathname as short as
; possible. This expansion is simply used so the user knows *exactly*
; where the file is coming from. Microsoft, in their infinite (heh)
; wisdom (hah) did not document this very useful function call.
; ----------------------------------------------------------------------------
; ERRORLEVEL values that we return
ERR_None equ 0 ; none!
ERR_Usage equ 1 ; problem on command line
ERR_NotFound equ 2 ; file not found
ERR_BadFile equ 3 ; given a device, or an unparsable
ERR_CopyProblem equ 4 ; problem while copying
ERR_Renaming equ 5 ; problem while renaming
; These are the data structures used in this program. The first is used
; internally to describe the way a file name is broken down into its
; components.
FileDriveLen equ 3
FilePathLen equ 65
FileNameLen equ 9
FileExtLen equ 5
FileNameS struc
drive db FileDriveLen dup(?) ; drive B:
path db FilePathLen dup(?) ; path \bin\booger
fname db FileNameLen dup(?) ; filename njmove
fext db FileExtLen dup(?) ; extension asm
FileNameS ends
; This data structure is the one that DOS uses to store the DTA. When
; we call FINDFIRST and FINDNEXT, this information will be set up to contain
; the rest of the file that we are moving.
DOSDTAS struc
_dta_reserved db 21 dup (?) ; reserved space
dta_attribute db ? ; attribute byte
dta_time dw ? ; file modification time
dta_date dw ? ; file modification date
dta_size dd ? ; file size (doubleword!)
dta_fname db 13 dup (?) ; file name (ASCIIZ)
DOSDTAS ends
; ============================================================================
.model small
.stack 0100h ; get a *real* stack
; ============================================================================
; Our data area follows. This data area contains both initialized and
; uninitialized data.
.data
Banner db "Nifty James' Famous Move Utility",cr,lf
db "Version 1.00 of 16 January 1989",cr,lf
db "(C)Copyright 1989 by Mike Blaszczak",cr,lf
db lf,eos
NoSpace db "NJMOVE: Destination device ran out of space"
CRLFSet db cr,lf,eos
NoSelf db "NJMOVE: A File can not be moved into itself"
db cr,lf,eos
CantWrite db "NJMOVE: Couldn't write to destination device"
db cr,lf,eos
CantRead db "NJMOVE: Couldn't read from source device"
db cr,lf,eos
CantSDevice db "NJMOVE: Can't move from a device"
db cr,lf,eos
CantDDevice db "NJMOVE: Can't move to a device"
db cr,lf,eos
BadSource db "NJMOVE: Bad Source File Specification"
db cr,lf,eos
BadDest db "NJMOVE: Bad Destination File Specification"
db cr,lf,eos
CouldntRename db "NJMOVE: Could not perform rename operation"
db cr,lf,eos
NameCollide db "NJMOVE: File naming collision; file already exists"
db cr,lf,eos
CouldntDIR db "NJMOVE: Couldn't get default directory of drive "
db eos
NoMatches db "NJMOVE: No files matched ",eos
UAborted db cr,lf,"NJMOVE: User Aborted",cr,lf,lf,eos
Usage db "Usage:",cr,lf
db tab,"NJMOVE <source> <dest> [/C]",cr,lf
db lf
db tab,"<source>",tab,"source file name",cr,lf
db tab,"<dest>",tab,tab,"destination file name",cr,lf
db tab,tab," both file names may "
db "contain wildcards",cr,lf
db tab,"[/C]",tab,"specifies optional confirmation"
db cr,lf,lf,eos
MovingMess db "Moving ",eos
MoveCMess db "Move ",eos
CopyingMess db "Copying ",eos
CopyCMess db "Copy ",eos
ToMess db " to ",eos
BackSlash db "\",eos
DotStar db "."
Star db "*",eos
ConfirmPrompt db "? [No] ",eos
confirmmode db 0 ; set if the /C was to be found
copymode db 0 ; set to one if we must copy
; from disk to disk instead of
; just renaming the files
even
sourcespec dw 0 ; source file specification
destspec dw 0 ; destination spec
sourcewildf db 0 ; set if sourcef has wildcard name
sourcewilde db 0 ; set if sourcef has wildcard ext
sourcehase db 0 ; set if sourcef has extension
sourcef FileNameS <> ; source description
sourcefile db 64 dup (0) ; total file name
destwildf db 0 ; set if destf has wildcard name
destwilde db 0 ; set if destf has wildcard ext
desthase db 0 ; set if destf has extension
destf FileNameS <> ; destination description
destfile db 64 dup (0) ; total file name
mydta DOSDTAS <> ; a place for DOS to store its stuff
; during FINDFIRST + FINDNEXT
; These variables are used by the copyfile procedure
sourceh dw ? ; handle for source file
desth dw ? ; destination file handle
; ----------------------------------------------------------------------------
; These areas are all data buffers that are not initialized, generally.
even
argvectors dw 64 dup (0) ; pointers to each argumnet
argcount dw 0 ; count of arguments
argbuff db 128 dup (0) ; buffer to hold args for DS access
CopyBufferLen equ 16384
copybuffer db CopyBufferLen dup (?)
; this buffer is used while copying
temppath db FilePathLen dup(?)
; area used by check4path and
; normalizepath
fixedpath db FilePathLen dup(?)
; area used by normalize path, too.
; This area is used by the confirm procedure
confirmreply db ?
; ============================================================================
; This is the program code. The execution of the program starts at the
; very first instruction here.
.code
; first, put the banner of our program on the screen
mov ax,@data
mov ds,ax
mov si,offset Banner
call putstr
; set the DTA to point to our area
mov ah,SetDTA
mov dx,offset mydta
int INTDOS
; then, parse the command line
call parseline
; convert all of the arguments to upper case
mov ax,@data
mov ds,ax
mov di,offset argvectors
arguploop: mov si,[di] ; get the next arg
add di,2
and si,si ; is it 0000?
je doneargup
call strupr
jmp short arguploop
doneargup:
; then, find the source file and dest file (maybe),
; and check for options, too
mov di,offset argvectors
checkargsloop:
mov si,[di] ; get the next arg
add di,2
and si,si ; end of list?
je donecheckargs
mov al,[si] ; nope! check it
cmp al,'/'
je gotoption
cmp al,'-'
jne notoption
gotoption: inc si ; get the next character of option
mov al,[si]
cmp al,'C' ; confirm?
jne showusage ; nope! error!
mov al,1
mov confirmmode,al ; confirmation mode
jmp short checkargsloop
notoption: mov ax,sourcespec ; do we have a source file?
and ax,ax
jne gotsource
mov sourcespec,si ; nope! this is our new source spec
jmp short checkargsloop
gotsource: mov ax,destspec ; how about a dest?
and ax,ax
jne showusage
mov destspec,si ; nope! this is the dest spec
jmp short checkargsloop
; now, see if we have enough arugments
donecheckargs:
mov ax,argcount ; did we get any args?
and ax,ax
jne noshowusage ; if not, we must
showusage:
mov si,offset Usage ; display the usage
call putstr
mov ah,Terminate ; and then
mov al,ERR_Usage ; terminate with error status
call exit
noshowusage:
cmp ax,1
ja gotdest ; was there a destination filespec?
nogotdest:
mov si,offset Star ; nope. asume dest filespec was
mov di,offset destf.fname ; *.*
call strcpy
mov si,offset DotStar
mov di,offset destf.fext
call strcpy
jmp short destsetup
gotdest: ; yes! call splitfilename to handle
mov si,destspec ; the parse of the filename
mov di,offset destf ; into the destf area
call splitfilename
destsetup:
; before we goof with anything, see if the
; destination has a file extension
mov si,offset destf.fext
call strlen
and cx,cx ; something there?
je destno
mov al,1
mov desthase,al ; yes! flag it!
destno: mov si,offset destf.drive
call strlen ; was there a drive name given?
and cx,cx
jne gotdestdrive
mov ah,GetDefDisk ; nope-get default from DOS
int INTDOS
add al,'A' ; make it ASCII
mov [si],al
inc si ; store it
mov byte ptr [si],':' ; add a colon
inc si
mov byte ptr [si],eos ; and a zilcher
gotdestdrive:
mov si,offset destf.path
call strlen ; was there a file path?
and cx,cx
jne gotdestpath
; put the default file path into the destf area
; first, put a backslash in there
mov di,offset destf.path
mov si,offset BackSlash
call strcpy
; then ask DOS to put the default path into the
; copybuffer area
mov si,offset copybuffer
mov ah,GetDefDir
mov dl,[destf.drive] ; whats the drive?
sub dl,('A'-1)
int INTDOS
jnc nodestdirproblem
call newline ; print error message
mov si,offset CouldntDIR
call putstr
add dl,('A'-1) ; for that drive
mov ah,PutChar
int INTDOS
call newline
mov al,ERR_BadFile
call exit
nodestdirproblem: ; now, concatenate it together. we will end up with
; \path\place
mov si,offset copybuffer
mov di,offset destf.path
call strcat
; if the path is not just \, add a trailing \ to it
mov si,offset copybuffer
call strlen
cmp cx,1
jb gotdestpath
mov di,offset destf.path
mov si,offset BackSlash
call strcat
gotdestpath:
; now, we'll see if the filename is there
mov si,offset destf.fname
call strlen
mov bx,cx
mov si,offset destf.fext
call strlen ; now, cx = strlen(fext)
; and bx = strlen(fname)
and cx,cx ; is cx zero?
je destnoproblem
and bx,bx
jne destnoproblem
; we've found that there is no filename but there is an extension.
; that's not too good.
mov si,offset BadDest
call putstr
mov al,ERR_BadFile
call exit
destnoproblem:
and bx,bx ; does filename exist?
jne desthasfn
mov si,offset Star ; nope ... copy a star there
mov di,offset destf.fname
call strcpy
desthasfn:
and cx,cx ; okay, does the extension exist
jne destfixed
mov si,offset DotStar; nope, copy ".*" into it
mov di,offset destf.fext
call strcpy
destfixed:
; now, check to see if there's a wildcard in the filename
mov si,offset destf.fname
mov ah,'?'
call strchr
and ax,ax ; is it?
jne desthaswildf
mov si,offset destf.fname
mov ah,'*' ; how about a star?
call strchr
and ax,ax
je destnowildf
desthaswildf:
mov al,1
mov destwildf,al ; set the flag
destnowildf:
; see if the destination extension has a wildcard
mov si,offset destf.fext
mov ah,'?'
call strchr
and ax,ax ; is it?
jne desthaswilde
mov si,offset destf.fext
mov ah,'*'
call strchr
and ax,ax
je destdone
desthaswilde:
mov al,1
mov destwilde,al
destdone: ; normalize the path for the destf
mov si,offset destf
call normpath
; does the filename have a wildcard?
mov al,destwildf
and al,al
jne destcompleted
; does it have an extension?
mov al,desthase
and al,al
jne destcompleted
; if not, check to see if the filename is a directory
mov si,offset destf
call check4dir
and ax,ax ; did it work?
je destcompleted
mov destwilde,al ; yes! extension and filename
mov destwildf,al ; are wildcards now
destcompleted: ; the destination file is all set up!
; now, we can parse the source file name
mov si,sourcespec
mov di,offset sourcef
call splitfilename
; before we touch anything, see if we were given
; a file extension
mov si,offset sourcef.fext
call strlen
and cx,cx ; anything there?
je sourceno
mov al,1
mov sourcehase,al ; yes! flag it
sourceno: ; check 'n' see if there is a drive there
mov si,offset sourcef.drive
call strlen
and cx,cx
jne gotsourcedrive
mov ah,GetDefDisk ; nope-get default from DOS
int INTDOS
add al,'A'
mov [si],al
inc si
mov byte ptr [si],':' ; and store a colon after it
inc si
mov byte ptr [si],eos ; and a zilcher around it
gotsourcedrive:
mov si,offset sourcef.path
call strlen ; was there a file path?
and cx,cx
jne gotsourcepath
; put the default file path into the sourcef area
; first, put a backslash in there
mov si,offset BackSlash
mov di,offset sourcef.path
call strcpy
; then ask DOS to put the default path into the
; copy buffer area
mov si,offset copybuffer
mov ah,GetDefDir
mov dl,[sourcef.drive] ; whats the drive?
sub dl,('A'-1) ; make into A=1, B=2 ... for DOS
int INTDOS
jnc nosourcedirprob
call newline ; print error message
mov si,offset CouldntDIR
call putstr
add dl,('A'-1) ; for that drive
mov ah,PutChar
int INTDOS
call newline
mov al,ERR_BadFile
call exit
nosourcedirprob: ; now, concatenate it togethre
mov si,offset copybuffer
mov di,offset sourcef.path
call strcat
; if the path is not just \, add a trailing \ to it
mov si,offset copybuffer
call strlen
cmp cx,1
jb gotsourcepath
mov di,offset sourcef.path
mov si,offset BackSlash
call strcat
gotsourcepath:
; make sure there's a filename, even if its a wildcard.
; check that it was a valid filename, too.
mov si,offset sourcef.fname
call strlen
mov bx,cx
mov si,offset sourcef.fext
call strlen ; now, cx = strlen(fext)
; and bx = strlen(fname)
and cx,cx
je sourcenoproblem
and bx,bx
jne sourcenoproblem
; we've found that there is no filename but there is an extension
; that's not good
mov si,offset BadSource
call putstr
mov al,ERR_BadFile
call exit
sourcenoproblem:
and bx,bx ; does filename exist?
jne sourcehasf
mov si,offset Star ; nope ... copy a astar in there
mov di,offset sourcef.fname
call strcpy
sourcehasf:
and cx,cx ; does extension exist?
jne sourcedone
mov si,offset DotStar
mov di,offset sourcef.fext
call strcpy
; now, check to see if there's a wildcard in the filename
mov si,offset sourcef.fname
mov ah,'?'
call strchr
and ax,ax ; is it?
jne sourcehaswildf
mov si,offset sourcef.fname
mov ah,'*' ; how about a star?
call strchr
and ax,ax
je sourcenowildf
sourcehaswildf:
mov al,1
mov sourcewildf,al ; set the flag
sourcenowildf:
; see if the sourceination extension has a wildcard
mov si,offset sourcef.fext
mov ah,'?'
call strchr
and ax,ax ; is it?
jne sourcehaswilde
mov si,offset sourcef.fext
mov ah,'*'
call strchr
and ax,ax
je sourcedone
sourcehaswilde:
mov al,1
mov sourcewilde,al
sourcedone: ; last, normalize the source file path
mov si,offset sourcef
call normpath
mov al,sourcewildf
and al,al
jne sourcecompleted
mov al,sourcehase
and al,al
jne sourcecompleted
mov si,offset sourcef
call check4dir
and ax,ax ; did it work?
je sourcecompleted
mov sourcewildf,al ; yes! filename and
mov sourcewilde,al ; extension are now wildcards!
sourcecompleted: ; filenames are all set!
; okay, almost there! check to see if the drives
; are the same. if they are, we're renaming. if
; they're not, we're copying and deleting.
mov si,offset sourcef.drive
mov al,[si]
mov si,offset destf.drive
cmp al,[si]
je workhorse
mov al,1 ; set copying flag
mov copymode,al
; right! everything is ready. we'll make the source
; file name into something meaningful
workhorse: mov si,offset sourcef
mov di,offset sourcefile
call makefilename
; just for kicks, we'll try to open that file.
mov ah,OpenHandle
mov al,0
mov dx,offset sourcefile
int INTDOS
jc dothefindfirst
mov bx,ax
mov ah,IOCTL
mov al,0 ; see if it is a device
int INTDOS
and dx,0080h ; check ISDEV bit
je notdevice
deviceblowout: mov si,offset CantSDevice
call putstr
mov ax,ERR_BadFile
call exit
notdevice: mov ah,CloseHandle
int INTDOS
; we'll try to find the first matching file
dothefindfirst: mov ah,FindFirst
mov dx,offset sourcefile
xor cx,cx
int INTDOS
jnc foundone
notfound: mov si,offset NoMatches
call putstr
mov si,offset sourcefile
call putstr
call newline
mov ax,ERR_NotFound
call exit
; this loop is the main thing. we've called findfirst,
; and the dta block is set up with the file we found.
; we'll figure out the destination file name, and
; then move or copy the file.
; at the bottom of the loop, we'll call findnext and
; see if there are more matching files. if its there,
; we can loop back here and process that next file.
foundone: mov si,offset mydta
mov di,offset sourcef
call popfound
mov si,offset sourcef
mov di,offset sourcefile
call makefilename
; if the source filename has a wildcard *and* the
; dest filename has a wild card, copy source filename
; into the destination filename
mov al,destwildf
and al,al
je notwildf
mov si,offset sourcef.fname
mov di,offset destf.fname
call strcpy
notwildf: ; if the source file extension has a wildcard, and
; the destination extension has a wildcard, copy
; the source extension into the destination extension
mov al,destwilde
and al,al
je notwilde
mov si,offset sourcef.fext
mov di,offset destf.fext
call strcpy
notwilde: ; create the destination file into an ASCIIZ string
mov si,offset destf
mov di,offset destfile
call makefilename
; print a message for what we are going to do
mov al,copymode
mov ah,confirmmode
and al,al
je wearemoving
mov si,offset CopyingMess ; we're copying
and ah,ah
je printsource ; without confirmation
mov si,offset CopyCMess
jne printsource
wearemoving: mov si,offset MovingMess ; we're moving
and ah,ah
je printsource ; without confirmation?
mov si,offset MoveCMess
printsource: call putstr
; either
; "Moving sourcefile to destfile"
; or
; "Copying sourcefile to destfile"
mov si,offset sourcefile
call putstr
whereto: ; print " to "
mov si,offset ToMess
call putstr
mov si,offset destfile
call putstr
; before we actually do it, call confirmation, if need be
and ah,ah
jne askfirst ; no confirmation
call newline
jmp short goahead
askfirst: call getyesno ; do confirmation
call newline
cmp ax,0 ; no?
je loopbottom ; skip it, then
cmp ax,1 ; yes?
je goahead ; go right ahead
xor ax,ax ; all of them!
mov confirmmode,al ; (forget about confirming)
; actually do it
goahead: mov al,copymode
and al,al
jne doacopy
doamove:
mov dx,offset sourcefile
mov di,offset destfile
mov ah,RenameFile
int INTDOS
jnc loopbottom
; decide which error happened. AX=0005 is bad name,
; anything else is different.
mov bx,ax
mov al,ERR_Renaming
mov si,offset NameCollide
cmp bx,5
je renamingcollision
mov si,offset CouldntRename
renamingcollision: call newline
call putstr
mov ah,ERR_Renaming
call exit
doacopy: call copyfile
loopbottom: mov ah,FindNext
int INTDOS
jc cleanexit ; relative jnc out of range
jmp foundone
cleanexit:
mov al,ERR_None ; return with no error
call exit
; ============================================================================
; These subroutines are used by the programs' main routine
; ----------------------------------------------------------------------------
; exit
; On entry, AL contains the error level that the program should return.
;
; This routine will terminate to DOS. It does no housekeeping functions.
exit proc near
mov ah,Terminate ; make a Terminate call
int INTDOS
exit endp
; ----------------------------------------------------------------------------
; strlen
; On entry, DS:SI points to a string.
;
; On return, CX will contain the length of the string.
strlen proc near
push ax
push si
xor cx,cx ; zero the length count
sl_c: mov al,[si] ; get a character
inc si
inc cx ; increment pointer and count
and al,al ; is it end of string?
jne sl_c ; loop back if it isn't
dec cx ; adjust the count down
pop si
pop ax
ret
strlen endp
; ----------------------------------------------------------------------------
; newline
; This routine prints a carriage return and a linefeed. We will destroy
; no registers.
newline proc near
push si
push ds ; save the DS:SI
push ax
mov ax,@data
mov ds,ax
mov si,offset CRLFSet
call putstr
pop ax ; restore the registers
pop ds
pop si
ret
newline endp
; ----------------------------------------------------------------------------
; putstr
; On entry, DS:SI points to a string.
;
; This routine prints the passed string to STDOUT, and does nothing else.
; The string is not altered in any way.
putstr proc near
push dx
push cx
push bx
push ax ; save the regs we use
push si
xor cx,cx ; zero the length count
ps_l: mov al,[si] ; get a character
inc si
inc cx ; increment pointer and count
and al,al ; is it end of string?
jne ps_l ; loop back if it isn't
dec cx ; adjust the count down
mov ah,WriteHandle ; ask DOS to write to the stdout
mov bx,STDOUT
pop si ; restore the SI
mov dx,si ; and put it into dx for DOS
int INTDOS
pop ax
pop bx
pop cx
pop dx
ret
putstr endp
; ----------------------------------------------------------------------------
; strcat
; On entry, DS:SI points to the concatenant, and ES:DI points to the
; concatenand:
;
; DS:SI -> "Have Antlers"
; ES:DI -> "Mooses "
; call strcat
; ES:DI -> "Mooses Have Antlers"
strcat proc near
push ax
push si ; save the registers
push di
; first, find the end
sc_next: mov al,es:[di] ; get the char
inc di
and al,al ; is it zilcher?
jne sc_next ; nope ... keep looping
dec di ; point to the zilcher
; now, we can just call strcpy to move the concatenant
; over the end of the concatenand.
call strcpy
pop di
pop si
pop ax
ret
strcat endp
; ----------------------------------------------------------------------------
; strcpy
; On entry, DS:SI points to a string and ES:DI points to storage space.
;
; This routine will copy the string from DS:SI to ES:DI.
strcpy proc near
push di ; save the registers we use
push si
push ax
sc_nextc: mov al,[si] ; get another character
inc si
mov es:[di],al ; store it
inc di
and al,al ; if that was the 0 character, quit
jne sc_nextc ; else keep working on the string
pop ax ; restore the registers we used
pop si
pop di
ret
strcpy endp
; ----------------------------------------------------------------------------
; strchr
; On entry, DS:SI points to a string, and ah contains a character to match.
; The routine will work forward through the string until it finds the end
; of the string or matches the character. On return, AX will be 0000 if
; there was no match, or DS:AX will point to the character in the string
; if the character wasn't found.
strchr proc near
push si ; save si
strchrnc: mov al,[si] ; get a character
inc si ; and increment pointer
cmp al,ah ; is it a match?
je strchrhit
and al,al ; nope. was it '\0'?
jne strchrnc ; if not, go do more
xor ax,ax ; yes, the string ended
je strchrexit ; so make AX null for failure
strchrhit: mov ax,si ; got the character!
dec ax ; put pointer into ax
strchrexit: pop si ; restore si
ret ; and go home
strchr endp
; ----------------------------------------------------------------------------
; strrchr
; On entry, DS:SI points to a string, and ah contains a character to match.
; The routine will step backward, starting at the end of the string, and
; work until it finds a match. If the program makes it through the whole
; string without finding the character, it will return with AX set to 0000.
; Otherwise, DS:AX points the the last occurrence of the character in the
; string.
strrchr proc near
push cx ; save the registers we use
push si
xor cx,cx ; zero cx since we use it to count
strrchrfe: mov al,[si] ; get the next character
inc si ; increment the pointer
inc cx ; increment the count of chars
and al,al ; is this the end of the string?
jne strrchrfe
cmp cx,0 ; is the string empty?
je strrchrfail ; if so, automatic failure
strrchrloop: dec si ; point to the null
mov al,[si] ; is that the character?
cmp ah,al
je strrchrhit ; if so, we've hit!
dec cx
jne strrchrloop ; if not, loop more
strrchrfail: xor ax,ax
je strrchrexit ; return AX as 0000
strrchrhit: mov ax,si ; got the character! move pointer
; into ax
strrchrexit: pop si
pop cx
ret
strrchr endp
; ----------------------------------------------------------------------------
; strupr
; On entry, DS:SI points to a string and ES:DI points to storage space.
;
; This routine will convert the entire content of the string to uppercase.
; It won't alter any registers, nor will it change non-alphabetic characters
; in the str.
strupr proc near
cld
push ax
push si
struprloop: lodsb ; get the next character
and al,al ; is it zilcho?
je struprdone
cmp al,'a' ; is it less than 'a'?
jl struprloop ; yep ... keep looping
cmp al,'z' ; is it bigger than 'z'?
jg struprloop ; yes; don't modify it
sub al,('a' - 'A')
mov [si-1],al ; restore that character
jmp short struprloop
struprdone: pop si
pop ax
ret
strupr endp
; ----------------------------------------------------------------------------
; parseline
; This routine tries to parse the command line. It sets up the variables
; in the data segment (argcount and arglist), as well as copying the
; information into a local area in the data segment. The routine will
; not preserve any of the registers it uses.
parseline proc near
mov ah,GetPSP ; get the PSP from DOS
int INTDOS
mov ds,bx ; move the PSP into ds
mov si,0081h ; address the command line
xor dx,dx
mov dl,[si-1] ; get the length of the line
add dl,081h
mov ax,@data ; now, set up ES to point to our
mov es,ax ; own data areas
mov di,offset argbuff
nextarg:
eatwhite:
mov al,[si]
inc si ; get the next character
cmp al,space ; is it a space?
je eatwhite
cmp al,tab ; or a tab?
je eatwhite ; yes ... skip over it
dec si ; point si at the first non-white
push si ; and save a copy of it for later
eatarg: mov al,[si] ; get the last character
inc si
cmp al,space ; is it a whitespace?
jle gotarg
jmp short eatarg ; nope... keep skipping
gotarg:
dec si
mov byte ptr [si],0 ; make the argument a string
pop ax ; get the pointer to this arg
xchg ax,si ; put si into DS:SI
call strcpy ; store in our data segment
call strlen ; get the length of the string
push cx ; save it
xchg si,ax
and cx,cx ; if this string has no length,
je added ; don't add it to the table
; here, DS:AX points to the end of the string and
; ES:DI points to the place where the string was stored.
; DS:SI still points to the beginning of the string.
add2table: mov ax,es:argcount
shl ax,1 ; get offset into array
add ax,offset argvectors
xchg ax,si
mov es:[si],di ; store the offset of this arg
xchg si,ax
inc es:argcount ; increment the count of args
added:
; adjust ES:DI
pop cx
add di,cx ; get the length and fix it in
inc di ; make DS:SI point to the next
inc si ; free spot
; see if we are done
cmp dx,si
ja nextarg
wearedone:
ret
parseline endp
; ----------------------------------------------------------------------------
; splitfilename
; this procedure accepts ds:si as a pointer to a full file name. it also
; expects that es:di points to a FileNameS. The routine will copy
; information from the full file name into the FileNameS structure.
splitfilename proc near
push cx
push si ; save the regs we use
push di
; to get started, check to see if there's a drivespec
mov al,[si+1] ; is there a colon for the
cmp al,':' ; second character?
jne sfn_nodrive ; nope! don't worry about it
sfn_gotdrive: mov es:[di+1].drive,al ; store the colon
mov al,[si] ; get the drive letter
mov es:[di].drive,al
add di,2 ; make es:di point after it
add si,2 ; and make si point after drive
sfn_nodrive: ; since there's no drive spec, just zero the drive.
xor ax,ax
mov es:[di].drive,al ; stick it
sfn_drivedone: ; now that drive is done, point es:di to the path
pop di ; point to the path area
push di
add di,path
; see if there was a path in the filename we got
mov al,[si] ; get the first char
cmp al,'.'
jne sfn_notrelpath
je sfn_hasrelpath
sfn_notrelpath: mov ah,'\' ; the path character
call strchr
and ax,ax ; is AX == 0000?
jne sfn_haspath
sfn_hasrelpath: mov ax,si ; yes -- point to the start of
; the string
sfn_haspath: ; there's a path! now, try to find the *last*
; character of the path.
mov dx,ax ; save AX for now
mov ah,'\'
call strrchr ; find the last '\'
xchg ax,dx
and dx,dx
jne sfn_hasend
; hmph! there is no '\', either way you look
; does the path begin with '.'?
cmp byte ptr [si],'.'
jne sfn_nopath ; nope! no path name
; yes! relative path name!
sfn_relpath: call strlen ; if no '\', point to end of
mov dx,si ; string
add dx,cx
cmp ax,dx ; is our end and start equal?
je sfn_nopath ; no path here, mon!
; now, copy the string from DS:AX to DS:DX into ES:DI.
sfn_hasend: mov si,ax ; start with ax
mov cx,FilePathLen-2
sfn_movepath: mov al,[si]
inc si ; get this character
mov es:[di],al ; and store it in path
inc di
dec cx ; decrement max char count.
jne sfn_movingpath ; if too many, truncate
mov al,'\'
mov es:[di],al ; truncate with a backslash
inc di
mov si,dx ; update the pointer so's it
inc si ; points to the filename now
jmp short sfn_movedpath
sfn_movingpath:
cmp dx,si ; is si>dx? (work to dx)
jae sfn_movepath ; no ... keep looping
sfn_movedpath:
sfn_nopath: xor ax,ax ; set up the ax so we can
mov es:[di],al ; store a null in .path area
; now, we have the path done. mess with moving
; the file name and the extension. DS:SI already
; points to the file name.
sfn_pathdone: pop di ; make es:di point to filename
push di
add di,fname
mov cx,FileNameLen
sfn_movefn: mov al,[si] ; get the next character
cmp al,'.' ; end of filename?
je sfn_gotext
dec cx ; too many characters?
jne sfn_moveingfn ; yep! truncate.
mov al,0
mov es:[di],al ; terminate the filename
sfn_blowoff: mov al,[si] ; skip ahead until we find eos or
cmp al,'.' ; a period
je sfn_gotext
inc si
and al,al ; is it a zilcher?
jne sfn_blowoff
je sfn_movedfn
sfn_moveingfn:
mov es:[di],al ; store this character
inc si
inc di
and al,al ; did we just write an eos?
jne sfn_movefn
sfn_movedfn: xor ax,ax
pop di ; make addressing to strucutre
push di
mov es:[di].fext,al
jmp short sfn_exit ; and split this joint
sfn_gotext: mov ah,0 ; write an end-of-string for
mov es:[di],ah ; the filename, first
mov cx,FileExtLen
pop di
push di
add di,fext ; make es:di point to filename
sfn_moveext: mov es:[di],al ; store the character
inc si
inc di ; increment the pointers
and al,al ; did we just write a '\0'?
je sfn_exit
cmp cx,0 ; is it zero yet?
jne snf_movingext
mov al,0
dec di
mov es:[di],al
jmp short sfn_exit
snf_movingext: dec cx
mov al,[si] ; get next character
jmp short sfn_moveext
sfn_exit:
; on the way out, we will check to see if the path
; name was a relative file. if so, we will make
; sure a slash is appended to it.
pop di
push di
add di,path
mov al,[di]
cmp al,'.' ; not a relative, no problem
jne sfn_nofix
; fix relative path
mov si,di
call strlen
add si,cx
dec si ; point to last character
mov al,[si] ; get that character
cmp al,'\' ; slash?
je sfn_nofix ; no problem
inc si ; make it a slash
mov byte ptr [si],'\'
inc si
mov byte ptr [si],eos
sfn_nofix: ; restore the regs we saved
pop di
pop si
pop cx
ret
splitfilename endp
; ----------------------------------------------------------------------------
; makefilename
; On entry, DS:SI points to a FileNameS, and ES:DI points to a storage
; buffer for a file name. The routine will string all of the elements
; of the strucutre together to make a real full file name.
makefilename proc near
push ax
push si
push di
mov ax,si ; put base pointer into ax
; first copy the drive specifier
mov si,ax
add si,drive ; copy the drive
call strcpy
; then concatenate the path
mov si,ax
add si,path
call strcat
; then concatenate the filename
mov si,ax
add si,fname
call strcat
; now concatenate the extension
mov si,ax
add si,fext
call strcat
; that's all!
pop di
pop si ; restore the registers and return home
pop ax
ret
makefilename endp
; ----------------------------------------------------------------------------
; popfound
; This routine accepts a pointer to a DOSDTAS in DS:DI, and a pointer to
; a FileNameS in ES:DI. It takes the filename and extension fields from
; the DOSDTAS and puts them into the fname and fext portions of the
; FileNameS structure.
popfound proc
push ax
push cx
push dx
push si ; save the regs we use
push di
mov dx,si ; and make a copy of the parms
mov cx,di ; to easily address the structs
; first, work on moving the filename
mov si,dx
add si,dta_fname
mov di,cx
add di,fname
popfoundf: mov al,[si] ; get this char
cmp al,'.' ; period yet?
je popfounde
mov es:[di],al ; store it
and al,al ; is it a zilcher?
jne popfoundfing
mov di,cx ; yep! zilch out extension too
add di,fext
mov es:[di],al
jmp short popfounddone
popfoundfing: inc di
inc si
jmp short popfoundf ; loop for more
popfounde: mov ah,0
mov es:[di],ah ; terminate the filename with 0
mov di,cx
add di,fext ; point to the extension
popfoundw: mov es:[di],al
inc di
inc si
and al,al ; is it zero?
je popfounddone ; yes, quit!
mov al,[si] ;(get next char)
jne popfoundw ; nope ... keep working
popfounddone:
pop di
pop si ; restore registers
pop dx
pop cx
pop ax
ret
popfound endp
; ----------------------------------------------------------------------------
; copyfile
; this procedure completely copies a file. it copies the file in
; sourcefile to the file in destfile. If there is an error during
; the copy, the destfile is automatically deleted and the program
; terminates via exit. If the copy is successful, the source file
; is deleted. The dest file will inherit the same file time and
; date stamp as the source file.
copyfile proc near
; first, try to open the dest file for reading
mov ah,OpenHandle ; open the
mov dx,offset destfile ; dest for
mov al,0 ; reading
int INTDOS
jc cf_nameokay
; we could read the source file! the file already
; exists, so we must abort.
mov bx,ax
mov ah,CloseHandle ; close the handle and
call newline
mov si,offset NameCollide ; print an error!
call putstr
mov ah,ERR_CopyProblem
call exit
cf_nameokay: ; try to open the source file
mov ah,OpenHandle ; open the
mov dx,offset sourcefile ; source for
mov al,0 ; for reading
int INTDOS
; was there an error?
jnc cf_noopenerr
cf_cantread: call newline ; couldn't open; print
mov si,offset CantRead ; an error message
call putstr
mov ah,ERR_CopyProblem
call exit ; and terminate
cf_noopenerr: mov sourceh,ax ; save the handle
mov ah,CreateHandle ; open
mov dx,offset destfile ; desination
xor cx,cx ; no attribute
int INTDOS
jnc cf_nowriteopen
cf_cantwrite: call newline ; couldn't write; print
mov si,offset CantWrite ; an error message
call putstr
mov ah,ERR_CopyProblem
call exit ; and terminate!
cf_nowriteopen: mov desth,ax
cf_nextbunch: mov ah,ReadHandle ; read
mov cx,CopyBufferLen ; all we can
mov bx,sourceh ; from the source
mov dx,offset copybuffer ; into the buffer
int INTDOS
jnc cf_canread ; no read error?
mov dx,offset destfile ; delete the dest file
mov ah,DeleteFile
int INTDOS
jmp short cf_cantread ; print error and quit
cf_canread:
and ax,ax ; (Was it zero?)
je cf_closeup ; yes! go close the files
mov cx,ax ; take what we read
mov ah,WriteHandle ; and write it
mov dx,offset copybuffer ; from the buffer
mov bx,desth ; to the destination
int INTDOS
jnc cf_canwrite ; write error!
mov dx,offset destfile ; delete the dest file
mov ah,DeleteFile
int INTDOS
jmp short cf_cantwrite ; print error and quit
cf_canwrite:
cmp cx,ax ; did we write 'em all?
jne cf_cantwrite ; no? that's a problem!
je cf_nextbunch
cf_closeup:
mov ax,GetDateTime ; get the date
mov bx,sourceh ; from the source file
int INTDOS
mov ax,SetDateTime ; set the date
mov bx,desth ; to the dest file
int INTDOS
mov bx,desth ; close dest file
mov ah,CloseHandle
int INTDOS
mov bx,sourceh ; close source file
mov ah,CloseHandle
int INTDOS
; get the source file's attribute byte
mov dx,offset sourcefile
mov ax,GetAttrib
int INTDOS
; set the dest file's attribute byte
mov dx,offset destfile
mov ax,SetAttrib ; CX is already set
or cx,020h ; but set archive bit
int INTDOS
; set the source file's attribute byte to normal
mov dx,offset sourcefile
mov ax,SetAttrib
mov cx,NormalAttrib ; reset source file
; so we can now
mov dx,offset sourcefile ; delete the source file
mov ah,DeleteFile
int INTDOS
ret
copyfile endp
; ----------------------------------------------------------------------------
; getyesno
; This procedure prints a yes/no prompt on the screen and gets an answer.
; the routine accepts "y", "1", and "t" as affirmative responses. the
; routine accepts "n", "0", and "f" as negative responses. If the routine
; is given an affirmative response, it will return AX as 0001. If it is
; given a negative reply, it will send back 0000 in AX. If the routine
; receives "a" in reply to its prompt, it will return a 0002 in AX.
getyesno proc near
mov si,offset ConfirmPrompt ; print prompt
call putstr
gyn_delete: xor ax,ax
mov confirmreply,al
gyn_loop: mov ah,GetChar ; get one character
int INTDOS
cmp al,'a'
jl gyn_noup
cmp al,'z'
jg gyn_noup
sub al,('a'-'A') ; make it uppercase
gyn_noup: mov ah,confirmreply
and ah,ah
jne gyn_checkcr ; we got a letter, is this one
; a carriage return?
cmp al,cr ; carriage return?
je gyn_negative ; yep, default is no!
cmp al,ctrlz ; control zee?
jne gyn_notzee
; if CTRL+Z is pressed, we must quit here.
mov si,offset UAborted
call putstr ; make a message about it
mov al,ERR_None ; exit with no errors
call exit
gyn_notzee: cmp al,space ; is this character lower than ' '?
jle gyn_loop ; yep, ignore it
cmp al,'Y' ; filter out unwanted chars
je gyn_okay ;
cmp al,'N'
je gyn_okay
cmp al,'A'
je gyn_okay
cmp al,'0'
je gyn_okay
cmp al,'1'
je gyn_okay
cmp al,'T'
je gyn_okay
cmp al,'F'
jne gyn_loop
gyn_okay:
mov confirmreply,al ; store the character
mov ah,PutChar ; and echo it to the screen
mov dl,al
int INTDOS
jmp short gyn_loop ; go loop back for more
gyn_checkcr: cmp al,cr ; is this one a carriage return?
je gyn_replied ; yes! process the reply
cmp al,bs ; is this one a backspace?
jne gyn_loop ; no! bad char, ignore it
mov ah,PutChar ; put a backspace
mov dl,bs
int INTDOS
mov ah,PutChar ; put a space to whiteout
mov dl,space ; the character typed
int INTDOS
mov ah,PutChar ; and another BS to fix cursor
mov dl,bs
int INTDOS
jmp gyn_delete
gyn_replied: mov al,confirmreply ; get the reply, and decide what
; to do
cmp al,'A'
je gyn_everything
cmp al,'0'
je gyn_negative
cmp al,'F'
je gyn_negative
cmp al,'N'
je gyn_negative
cmp al,'Y'
je gyn_affirm
cmp al,'1'
je gyn_affirm
cmp al,'T'
je gyn_affirm
jmp gyn_loop ; somehow a bad char got in
gyn_everything: mov ax,2 ; reply "all"
ret
gyn_negative: mov ax,0 ; negative reply
ret
gyn_affirm: mov ax,1 ; affirmative reply
ret
getyesno endp
; ----------------------------------------------------------------------------
; check4dir
; this routine accepts DI as a pointer to a FileNameS. It will check
; to see if the file name section of the file name is actually a directory.
; if it is, it will move the file name to the end of the path field and
; make the file name null. if not, it simply returns without changing
; the structure. We have to do this because DOS won't understand us if
; we specify a subdirectory without tacking \*.* onto the end of it.
; If we end up changing anything, AX is set to one. If not, AX is zilch.
;
check4dir proc near
push si ; save the pointer
mov di,offset temppath
call strcpy ; move drive to temppath
mov di,offset temppath
add si,path ; get the path
call strcat
mov di,offset temppath
pop si
push si
add si,fname
call strcat ; and finally add the filename
mov ah,FindFirst
mov dx,offset temppath
mov cx,SubDirAttrib ; get directories only
int INTDOS
jnc c4d_worked
; return with al set to zero to indicate that
; we didn't change the filename
xor ax,ax ; make a zilcher out of ax
pop si
ret
; it *is* a subdir!
; concatenate the file to the path
c4d_worked: pop si
push si
mov di,si
add di,path
add si,fname
call strcat ; add filename to path
mov si,offset BackSlash
call strcat ; add backslash to path
mov si,offset Star ; put a star into the filename
pop di
push di
add di,fname
call strcpy
mov si,offset DotStar
pop di
push di
add di,fext ; and a ".*" into extension
call strcpy
c4d_end: mov ax,1 ; ax=1, success!
pop si ; pop register and return!
ret
check4dir endp
; ----------------------------------------------------------------------------
; normpath
; this proc accepts a pointer to a FileNameS in DS:SI. The proc will
; call DOS to have it normalize the path -- this simply means that the
; procedure will resolve any relative (ie, including "." or "..")
; file path references. Note that the function call used here is
; undocumented.
normpath proc near
push di ; save the registers
push si
mov di,offset temppath
call strcpy ; copy drive and path to
add si,path ; a temporary area
call strcat
mov ah,NormalizePath
mov si,offset temppath
mov di,offset fixedpath
int INTDOS
pop si
mov di,si ; copy the path back
add di,path
mov si,(offset fixedpath)+2
call strcpy
pop di ; restore
ret ; and return
normpath endp
; ----------------------------------------------------------------------------
; That's a wrap. Written under the influence of Tom Petty, Rush, and REM.
end