home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 June
/
SIMTEL_0692.cdr
/
msdos
/
zip
/
zdir16b.arc
/
ZDIR16B.ASM
next >
Wrap
Assembly Source File
|
1989-09-28
|
51KB
|
1,939 lines
;ZDIR .ZIP directory utility
;From a disassembly and hack of ADIR.EXE
;v1.6b Back at Toad Hall ... (28 Sep 89)
; - Removed old commented-out code (via v1.5, v1.6, v1.6a changes).
; - NICE ideas in v1.6a .. attaboy, Gisbert!
; - No significant changes .. just cleaned up, making a "formal"
; release from Toad Hall.
; - Suppose we oughtta put in yet ANOTHER cmdline switch (-c or
; something) for those who actually wanna pipe names (paths'n'all)
; to StdOut. (The -c means omit ZIP comments.)
;v1.6a GWS (yeah, hacking away in West Germany's capital village) strikes again
; - Cleaning up verbose display:
; Some columns were one off. Also, change name alignment slightly.
; - Added /m ('monosyllabic') command line switch:
; This will show full names, paths'n'all, as stored in the Zip,
; one per line, and nothing else. May be nice for piping into
; other programmes. - Beware of the header lines, though.
;v1.6 - v1.4a Changes:
; Gisbert Selke (all the way from Germany, I think) tweaked the earlier
; v1.4 (see v1.4a comments).
; I kept his changes (that I hadn't already added in v1.5 myself),
; incorporating them into the v1.5 code. (Of course, just HAD to tweak
; his changes as well!)
; - Member filenames:
; Still considering shifting filenames to the right hand side of
; the screen output. That would permit long (path) filenames to be
; displayed/output (with screen wraparound handling the weird,
; extra-long ones). I believe that's why the PKZIP (Katz) author
; formatted his display that way. But the member names on the right
; is NOT convenient to me! Leaving it the original way (member file
; names on the left).
;v1.5 Updating for PKZIP v1.01
; - Added "implode" to file compression style table
; (plus other tests to insure we catch illegal values).
; - Adding an "E" before the compression style
; (to indicate if file is encrypted) (verbose only).
; - Found bug in testing for ZIP member filename paths:
; PKZIP uses the "/" char for subdir separator instead of "\".
; Fixed.
; - Tightened up code for path-testing,
; extended the "+" path flag display for verbose as well as
; nonverbose displays.
; No, we aren't displaying paths yet.
; - If using wildcard member option (in verbose mode),
; we don't display verbose header until we find a qualifying
; file.
; - Total and total total displays: More than one qualifying member
; file must be found before the figures are displayed. (This is
; for verbose mode only.)
; - Totals (qualifying member files, each ZIP)
; - Total totals (qualifying member files, ALL qualifying ZIPs)
; - WildCard searches:
; Considered not displaying a target .ZIP file's name at ALL
; if it has no "eligible" member wildcard contents (to shorten
; output to just those .ZIP files (and members) found in a wildcard
; search. However, decided a non-find is valuable information.
; At least the .ZIP file name will be output.
;
; - Removed earlier v1.n flags from code
;
; Considering processing file names (vname) to handle "long" names
; (e.g., those with paths).
; Paths would probably only be displayed in verbose mode
; (on a separate line, or maybe to screen right, letting screen
; display handle "wraparound").
;v1.4a Gisbert W.Selke (RECK@DBNUAMA1.BITNET for now): allow '/' as
; switch char, too (for them die-hard DOS people like myself).
; Output rrrrreal 4-byte CRCs. Add text for 'Imploded'.
;v1.4 Keith Petersen (W8SDZ@WSMR-SIMTEL20.ARMY.MIL) informs me ZDIR
; is failing on a lot of ZIP files from local BBS's. Seems the BBS's
; are adding huge comments, and ZDIR can't find the central directory.
; Increasing ENDOFS (the amount of ZIP file trailer we read in)
; to allow for this.
; Keith also suggested the verbose mode as default .. but I don't agree!
; If I wanted verbose, I would have used PKZIP -v in the first place!
; Also programs like Buerg's ZIPV do that just fine.
;v1.3c Bug: I'd written code to handle no cmdline parms at all,
; but failed to test it! Turns out system locks up!
; Thanks to FAAR RBBS for alerting me. Sloppy testing procedures.
;v1.3b Bug: Forgot the added "-v" verbose switch as the FIRST PSP
; cmdline argument would blow away using our PSP FCB's to get
; the parsed member filename.
; Using _Args (from KEGELUNX unix-like cmdline parsing)
; to handle the -v switch anywhere on cmdline.
; (Affects Parse_CmdLine only.)
;v1.3a 23 Mar 89
; - Adding file comment display to verbose mode.
; - Because of the occasional padding (by XMODEM) of garbage to the
; end of a file, we're back to reading a full 256 bytes at the
; ZIP file end to find that end structure (149 bytes for the largest
; likely ENDDIR structure, plus the padding to the next 128 boundary).
; If you don't HAVE any of these files, change ENDOFS back to 149.
;v1.3 22 Mar 89
; - Adding verbose display (a -v switch).
; Cribbing code from QBARCV3.ASM (my improvements on QBARV2.ASM,
; a utility for RBBS ARC/PAK displays).
; Usage: ZDIR -v foob (yep, -v must be first parm)
;v1.2 21 Mar 89
; - Missed the obvious .. the very last structure in a ZIP file
; (call it ENDDIR) contains a pointer to the central directory.
; Since the only variable length fields in that last structure
; are fairly short (zip comments, etc.), it should be MUCH smaller.
; We won't need that huge 5Kb SWAG for the initial directory read.
; Cutting buffer down to 128 bytes, reading in ENDDIR to get that
; central directory pointer, and then going right to central directory.
; Since the max possible ZIP file comment (in ENDDIR) is 7FH bytes,
; the max possible ENDDIR would be 7FH + 22 (normal structure size)
; or 149 bytes (ENDOFS).
; - Moved initial cmdline parsing down to code end
; (just to clean up things)
; - Caught a bug .. failed on a single '*' wildcard cmdline parm.
;v1.1 18 Mar 89
; - Added full pathing capabilities for target .ZIP files.
; - Added ambiguous member screening (e.g., search FOOB.ZIP
; for just *.ASM files).
; - Cleaned up the kludgey ADIR wildcard finds with all its
; multiple DTAs, etc.
; - Still no way to test for member filename paths.
;Original ADIR.EXE fixes:
; - Fixed bug that never closed any files!
; - Changed to .COM format, tightened, etc.
;ZIP functional changes: MANY! I consider this almost a total rewrite
;(except for the kludge with wildcard finds from the original ADIR.EXE).
; - Since the filename length is variable (and may include paths),
; the handling, formatting and display is significantly different
; than in ADIR.
; - Have to put back the ambiguous member parsing.
;David Kirschbaum
;Toad Hall
FALSE equ 0
TRUE equ NOT FALSE
PLAIN equ 0 ; v1.6a
VERBO equ 0FFh ; v1.6a
MONOSYL equ 1 ; v1.6a
CR equ 0DH
LF equ 0AH
STDOUT equ 1
STDERR equ 2
ENDOFS equ 4096 ;enough for largest possible
;ZIP end directory structure.
;(to include XMODEM padding)
;(and hormongous comments!)
;(was 256 in v1.3)
Print macro name ; display a field
mov dx,offset name
call PrintS
endm
;PKZIP central directory structure:
zdirEntry STRUC
zsig1 db 50H,4BH,01H,02H ;central file header signature 4 bytes
;(0x02014b50)
zVerMade dw ? ;version made by 2 bytes
zVerExt dw ? ;version needed to extract 2 bytes
zBitflag dw ? ;general purpose bit flag 2 bytes
zCmpMeth dw ? ;compression method 2 bytes
zModTime dw ? ;last mod file time 2 bytes
zModDate dw ? ;last mod file date 2 bytes
zCrc32 dw ?,? ;crc-32 4 bytes
zCmpSiz dw ?,? ;compressed size 4 bytes
zUncmpSiz dw ?,? ;uncompressed size 4 bytes
zNameLen dw ? ;filename length 2 bytes
zExtraLen dw ? ;extra field length 2 bytes
zFilCmtLen dw ? ;file comment length 2 bytes
zDskNrPtr dw ? ;disk number start 2 bytes
zIntAttr dw ? ;internal file attributes 2 bytes
zExtAttr dw ?,? ;external file attributes 4 bytes
zHdrOfs dw ?,? ;relative offset of local header 4 bytes
zFilename db ? ;filename (variable size)
;extra field (variable size)
;file comment (variable size)
zdirEntry ENDS
; End of central dir record:
zdirEnd STRUC
zEndSig db 50H,4BH,05H,06H ;end of central dir signature 4 bytes
;(0x06054b50)
zDskNr dw ? ;number of this disk 2 bytes
zDirDsk dw ? ;number of the disk with the
;start of the central directory 2 bytes
zDskNrEntry dw ? ;total number of entries in
;the central dir on this disk 2 bytes
zDirNrEntry dw ? ;total number of entries in
;the central dir 2 bytes
zDirSiz dw ?,? ;size of the central directory 4 bytes
zDirOfs dw ?,? ;offset of start of central
;directory with respect to
;the starting disk number 4 bytes
zCmtLen dw ? ;zipfile comment length 2 bytes
zCmt db ? ;zipfile comment (variable size)
zdirEnd ENDS
CSEG segment para public
assume CS:CSEG,DS:CSEG
org 5CH ;FCB #1
db ? ;drive val
fcb1 db 10H dup(?) ;5DH, FCB #1 first char
fcb2 db 10H dup(?) ;6DH, FCB #2 first char
org 80H
nchar db ?
params db ?
;program entry point
org 100H
Zdir proc near
jmp Start ;skip over runtime data
usage db CR,LF,9,9, 'ZDIR version 1.6b, 890928',CR,LF ; v1.6a
db 9,9,'David Kirschbaum, Toad Hall/mod GWS',CR,LF ; v1.6a
db 9,9, 'USAGE: ',9,'ZDIR zipname[.zip] [afn] [-v|-m]',CR,LF ;v1.6a
db 9,9,9,'zipname may be ambiguous (wildcarded)',CR,LF
db 9,9,9,'afn = ambiguous member file name', CR,LF
db 9,9,9,'-v = verbose display',CR,LF
db 9,9,9,'-m = monosyllabic display',CR,LF ; v1.6a
USAGELEN equ $ - usage
ziptyp db '.ZIP'
ZIPTYPLEN equ $ - ziptyp
msg1 db CR,LF, 9, 9, 9, ' ZIP file: '
MSG1LEN equ $ - msg1
msg2 db 'ZIP file not found',CR,LF
MSG2LEN equ $ - msg2
msg3 db 'Central directory not found',CR,LF
MSG3LEN equ $ - msg3
msg4 db 'ZIP is out of alignment or it''s not a ZIP'
crlf db CR,LF
MSG4LEN equ $ - msg4
CRLFLEN equ $ - crlf
;Keith Petersen suggested this oughtta go .. Sigh ...
;rivvvt db 'Rivvvvt',CR,LF
;RVTLEN equ $ - rivvvt
handle dw 0
flag1 db LOW(TRUE) ;Find First flag
verbose db PLAIN ;verbose display switch v1.6a
;PLAIN, VERBO, or MONOSYLLABIC v1.6a
znameptr dw 0 ;point past ZIP target file path
mnameptr dw 0 ;remember .zFilename start
dirNrEntry dw 0 ;central directory file count
dirctr dw 0 ;for counting down members
pathflag db ' ' ;is set to '+' if member
; filename includes a path
vhdrflag db FALSE ;set true when first member
;file is found
Comment ~ Looks like:
+filename.typ 000K / 000K +filename.typ 000K / 000K +filename.typ 000K / 000K
Comment ends ~
blankline db ' . K / K . K / K '
db ' . K / K',CR,LF
LINELEN equ $ - blankline
; Verbose display data
; display lines for verbose
;v1.5 Adding a "E" (just before Stowage style) if encrypted
vhdr db CR,LF
db ' Name Length E Stowage SF Size now Date Time CRC' ; v1.6a
db CR,LF
db ' ============ ======== = ======== ==== ======== ========= ====== ========' ; v1.6a
db CR,LF
VHDRLEN equ $ - vhdr
vline label byte ;db CR,LF
vname db 15 dup (' ') ; v1.6a
vlength db 9 dup (' ') ; length in archive v1.6a
vencflag db 3 dup (' ') ;"E" if encrypted, else blank
vstyle db 10 dup (' ') ; compression method (text)
vfactor db ' xx% ' ; compression factor (percentage)
vsize db 10 dup (' ') ; actual file bytes
vdate db 'dd ' ; creation date
vmonth db 'mmm '
vyear db 'yy '
vtime db 'hh:mm ' ; creation time
vcrc db 'xxxxxxxx' ; 32-bit crc in hex v1.4a
db CR,LF
VLINELEN equ $ - vline
; final totals line
vthdr db '*Total '
vtmbrs db 5 dup (' ') ; v1.6a
vtlen db 8 dup (' '),' '
db 12 dup (' ')
vtsf db ' % '
vtsize db 8 dup (' ')
db CR,LF ; for tom
VTHDRLEN equ $ - vthdr
;Totals for each ZIP file's members:
totcmp dw 0,0 ; total of file lengths
totuncmp dw 0,0 ; total of file sizes
totmbrs dw 0 ; total number of files
TOTLEN equ $ - totcmp
;Totals for ALL ZIP files displayed:
ttotcmp dw 0,0 ;total compressed file size
ttotuncmp dw 0,0 ;total uncompressed file size
ttotmbrs dw 0 ;total nr member files
; ZIP compression types:
zstyles label byte
db ' Stored' ;0 - The file is stored (no compression)
db ' Shrunk' ;1 - The file is Shrunk
db 'Reduced1' ;2 - Reduced with compression factor 1
db 'Reduced2' ;3 - Reduced with " " 2
db 'Reduced3' ;4 - Reduced with " " 3
db 'Reduced4' ;5 - Reduced with " " 4
db 'Imploded' ;6 - The file is imploded
db ' Unknown' ;illegal or unknown value
months db 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec '
Zdir endp
Start proc near
call Parse_CmdLine ;parse cmdline for
;target filenames
jb Jmp_Msg_Term ;failed
;Let's get to work
call Find_Zip
jnc ZipLup_73 ;found the first one
mov dx,offset msg2 ;'Zipfile not found'
mov cx,MSG2LEN
Jmp_Msg_Term:
jmp Msg_Term ;display, terminate
ZipLup_73:
call Read_CentralDir ;try to read in file trailer
jnb Got_Dir ;found it, DS:BX -> directory
mov dx,offset msg3 ;'No central directory'
mov cx,MSG3LEN
jmp short Next1 ;right to next ZIP file
Got_Dir:
;v1.5 We'll only show our verbose header line
; when and if we find our first member file.
; (Down in Show_FileData)
cmp verbose,MONOSYL ;mode switch v1.6a
ja MemberLup ;verbose, no init v1.6a
jb GD_InitLine ;plain, use standard v1.6a
mov di,offset blankline ;if monosyllabic, blank v1.6a
mov al,' ' ; that blankline for v1.6a
mov cx,LINELEN-2 ; good v1.6a
rep stosb ; v1.6a
GD_InitLine: ; v1.6a
call Refresh_LineBuff ;Init formatted display line
MemberLup:
cmp word ptr [bx],4B50H ;signature?
jnz Bad_Dir ;nope, bogus
cmp word ptr 2[bx],0201H ;normal entry?
jnz Bad_Dir ;nope, bogus
;Normal directory entry. Display it.
call Show_FileData ;display entry info
dec dirctr ;decr nr entries
jz Next_Zip ;last entry
jmp MemberLup ;next member
Bad_Dir:
mov dx,offset msg4 ;'ZIP is out of alignment'
mov cx,MSG4LEN ; or not a .ZIP file'
call Pr_StdOut ;display msg
Next_Zip:
mov dx,offset crlf ;need a new line
mov cx,CRLFLEN ;length
cmp verbose,VERBO ;verbose mode? v1.6a
jz Show_Verbose_Totals ;yep, maybe display totals
mov ax,offset LINEBUFF ;formatted line start
cmp di,ax ;got any file entries?
jz Next1 ;nope
dec di ;back up over the two spaces
dec di ; from the last file size
mov dx,ax ;DX'll need it for display
mov cx,ax ;CX will be nr chars
mov ax,0A0DH ;CR/LF
stosw ;stuff
stosw ;2 CR/LFs
xchg cx,di ;CX = end, DI = start
sub cx,di ;end - start = length
jmp short Next1
;Verbose mode
Show_Verbose_Totals:
cmp totmbrs,0 ;any totals?
jz Next1 ;nope
call Format_Totals ;yep, format
;CX,DX prepared for ...
;v1.5 If just 1 member, there won't BE a total display!
; BP returns from Format_Totals with 0 (1 file) or 1 (more than
; 1 file)
or bp,bp ;just 1 file?
jz Next2 ;yep, no totals
Next1:
call Pr_StdOut ;display line seg or CR/LF
Next2:
call Find_Zip ;next .ZIP file
jnc ZipLup_73 ;found it, loop
xor ax,ax ;handy 0/FALSE
cmp al,verbose ;nonverbose mode? v1.6a
;;;;;;; jnc NoMsg_Term ;*TEST**TEST* ;nonverbose, terminate v1.6a
jle NoMsg_Term ;*TEST**TEST* ;nonverbose, terminate v1.6a
;(errorlevel 0)
; We've been accumulating overall totals.
; Now display them. Gotta move the total totals
; into the totals (where Format_Totals expects them).
;v1.5 If we never displayed our verbose header line,
; we never found a qualifying member file!
; Ergo .. no totals ..
cmp vhdrflag,al ;FALSE ;no header line?
jz NoMsg_Term ;no hdr, so no totals
mov ax,ttotmbrs ;accumulated total mbrs
cmp ax,dirNrEntry ;just the one ZIP?
jz No_More ;yep, forget total totals
cmp ax,1 ;<= 1?
jbe No_More ;yep, forget the total totals
mov si,offset ttotcmp ;move total totals
mov di,offset totcmp ;into totals
mov cx,TOTLEN SHR 1 ;nr words
rep movsw
call Pr_CrLf ;down extra line
call Format_Totals ;format overall totals
call Pr_StdOut ;display them
No_More:
; Keith Petersen suggested this oughtta go .. Sigh ..
; mov dx,offset rivvvt
; mov cx,RVTLEN ;fall thru to...
xor ax,ax ;ERRORLEVEL 0
jmp short NoMsg_Term
Msg_Term:
push ax ;save errorlevel
call Pr_StdOut ;display error msg
pop ax
NoMsg_Term:
mov ah,4Ch ;terminate, AL = ERRORLEVEL
int 21h
Start endp
;SUBROUTINE
Find_Zip proc near
;We reset our DTA to the default PSP DTA each time through.
;The actual ZIP file open uses another one.
mov dx,80H ;use PSP DTA
mov ah,1AH ;set DTA
int 21h
cmp flag1,0FFh ;first time thru?
jne FindNext_162 ;nope
;First time through
not flag1 ;set to NOT first time thru
mov dx,offset ziptarget ;DS:DX -> zip target name
xor cx,cx ;read-only
mov ah,4Eh ;find first
int 21h
jmp short Find_Done
FindNext_162:
mov ah,4Fh ;find next
int 21h
Find_Done:
jb FindZ_X ;failed, return CF set
call Move_FileName ;move name in after path
;to create full name for open
;Found target file. Announce, set new DTA, open it.
mov dx,offset msg1 ;'Zip file: '
mov cx,MSG1LEN
call Pr_StdOut
Print ziptarget ;'filename.zip'
call Pr_CrLf ;new line
;We need a new DTA for the file open/read so we don't blow away
;the find first/find next stuff in the PSP DTA.
mov dx,offset dta1BA ;DS:DX -> new DTA
mov ah,1Ah ;set DTA
int 21h
mov dx,offset ziptarget ;DS:dx -> filename buffer
mov ax,3D00H ;open file, read only
int 21h
mov handle,ax ;save handle
FindZ_X:
ret
Find_Zip endp
Move_FileName proc near
mov si,9EH ;FCB #1 +1
mov di,znameptr ;pointer to after path
mov cx,13 ;include AsciiZ 0
rep movsb
ret
Move_FileName endp
;Reads in file tailer.
;Then scans for the unique central directory signature.
;On success:
; DS:BX -> central directory structure
; readlen = nr bytes actually read
; CF clear
;Else CF set for failure
Read_CentralDir proc near
xor cx,cx ;CX:DX = offset from end
xor dx,dx ;to very end
mov bx,handle ;file handle
mov ax,4202H ;move file pointer to end
int 21H ;gets file size in DX:AX
jb RCD_Close ;seek failed
; Increased ENDOFS to read in more of the ZIP file's tail.
; Original value wasn't enough to allow for huge comments.
sub ax,ENDOFS ;back up psn.lo
jnb Read1 ;ok, no problem
sub dx,1 ;got a borrow, decr psn.hi
jnb Read1 ;no problem
xor ax,ax ;sigh .. small file ..
xor dx,dx ; .. back to very start
Read1:
mov cx,dx ;psn.hi
mov dx,ax ;psn.lo
mov ax,4200H ;now move from start
int 21H ;CX:DX point to offset from end
;(big) or to start (small)
jb RCD_Close ;failed, close up
mov cx,ENDOFS ;try to read this much
mov dx,offset DIRBUFF ;into our directory buffer
mov di,dx ;DI'll need it in a second
mov ah,3FH ;read from file/device
int 21H ;AX=bytes read
jb RCD_Close ;failed, close up
;First scan the end structure to locate the central directory.
call Sig_Scan ;find the structure start
;(using bytes read in AX)
jb RCD_InvalidDir ;failed, close and exit
;ES:DI -> end structure start (the signature)
;While we have the end structure, let's pick up and display
;any ZIP file comment.
mov cx,[di].zCmtLen ;comment length
jcxz RCD_NoComment ;nope, forget it
lea dx,[di].zCmt ;DS:DX -> comment
call Pr_StdOut ;display it.
call Pr_CrLf ;and a new line
RCD_NoComment:
; Pick up the central directory file pointer (long integer):
; It's a pointer directly to the central directory start.
; This will only work for single-disk ZIP files.
mov si,[di].zDirSiz ;save central directory size
mov ax,[di].zDirNrEntry ;nr central dir entries
mov dirNrEntry,ax ;save it for later
mov dirctr,ax ;two places
mov dx,[di].zDirOfs ;central directory offset.lo
mov cx,[di].zDirOfs[2] ;offset.hi
mov ax,4200H ;move file ptrs from start
int 21H
jb RCD_Close ;seek failed, close and exit
mov cx,si ;read central dir size bytes
mov dx,offset dirbuff ;into our directory buffer
mov ah,3FH ;read from file/device
int 21H
jb RCD_Close ;failed, forget it
mov di,dx ;DI -> dir start
xor ax,ax ;return AX=0 for ok
cmp word ptr [di],4B50H ;is it a signature?
jnz RCD_InvalidDir ;nope
cmp word ptr 2[di],0201H ;is it a file entry sig?
jz RCD_Close ;yep, good to go, CF clear
RCD_InvalidDir:
mov al,11 ;Invalid format
stc ;insure CF set, AL=error
RCD_Close:
mov dx,ax ;save error value (if any)
pushf ;save flags
mov ah,3EH ;close file
int 21H ;(BX = file handle)
popf ;restore orig flags
mov ax,dx ;and any error value
mov bx,di ;if it went well,
;DS:BX -> central dir struc
ret
; Subroutine for Read_CentralDir
; Scans backwards through buffer for a directory end signature.
; Enter with:
; SI = second two signature bytes
; DX -> buffer start
; AX = bytes read
;We must scan at the byte level (since a member or directory entry
;can be ANY length).
Sig_Scan:
mov di,dx ;ES:DI -> DIRBUFF
;(now a ZIP end dir structure)
add di,ax ;+ bytes read = -> buff end
mov cx,ax ;bytes read for the scan
inc cx ;debug for MAX size
inc cx
mov ax,4B50H ;scan for first signature char
;(same for all structures)
mov si,0605H ;2d 2 chars of a directory
;end structure signature
std ;scan from end to start
SS_Lup:
repne scasb ;ES:DI -> read buffer
jnz No_Sig ;not found
jcxz No_Sig ;scanned it all
;ES:DI -> the byte BEFORE the 50H signature
cmp 2[di],ah ;2d signature byte?
jnz SS_Lup ;nope, keep searching
cmp 3[di],si ;last 2 signature bytes?
jnz SS_Lup ;nope, keep searching
inc di ;bump from that last scasb
;DS:DI -> dir structure
cld ;forwards again to be neat
clc ;CF clear for success
ret
No_Sig: cld ;forward again to be neat
stc ;return CF set for failure
ret
Read_CentralDir endp
;SUBROUTINE
;1 call
Show_FileData proc near
;Added tests for cmdline member name testing
call Member_Test ;see if file is eligible
jb SF_NextRec ;nope, forget it
push bx
xor ax,ax ;FALSE
cmp al,verbose ;not verbose? v1.6a
jle SFD_NonVerbose ;not verbose v1.6a
;Verbose mode
cmp vhdrflag,al ;FALSE ;verbose hdr displayed?
jnz SFD_HdrDone ;yep
not vhdrflag ;set to TRUE
mov dx,offset vhdr ;display verbose header
mov cx,VHDRLEN
call Pr_StdOut
SFD_HdrDone:
call Show_Verbose ;show verbose member display
jmp short SFD_BumpPtrs ;skip nonverbose stuff
;Nonverbose mode
SFD_NonVerbose:
call Stuff_FileName ;parse, pad at ES:DI
cmp verbose,MONOSYL ;monosyllabic output? v1.6a
je SFD_FlushLine ;shove it out v1.6a
lea si,[bx].zCmpSiz ;compressed size (long int)
call Stuff_FileSize ;to ES:DI
lea si,[bx].zUncmpSiz ;uncompressed size
inc di ;adjust line ptr
call Stuff_FileSize ;stuff uncompressed size
; to ES:DI
cmp di,offset LINEBUFF + LINELEN ;hit end?
jb SFD_BumpPtrs ;nope
SFD_FlushLine: ; v1.6a
mov dx,offset LINEBUFF ;display the line
mov cx,LINELEN ;length
call Pr_StdOut ;display it
call Refresh_LineBuff ;refresh dynamic variable
SFD_BumpPtrs:
pop bx
SF_NextRec:
;Now bump our BX pointer to the next entry
lea ax,[bx].zFilename ;from filename start
add ax,[bx].zNameLen ;add in name field length
add ax,[bx].zExtraLen ;and extra field length
add ax,[bx].zFilCmtLen ;and file comment field length
mov bx,ax ;should be next record
ret
Show_FileData endp
;Tests to see if THIS file is eligible (e.g., meets the ambiguous
;filename entered on cmdline at startup).
; If no such parm, accept it (return CF clear).
; If it matches, return CF clear.
; If no match, return CF set.
;While we're here, let's Asciify that stupid ZIP directory file name
;for later tests.
Member_Test proc near
mov cx,[bx].zNameLen ;name length
jcxz MT_StepStone ;zero .. flunk it! v1.6a
push di ;save formatted line pointer
lea si,[bx].zFilename ;dirEntry filename
mov di,si ;start
dec di ;back it up one
mov ax,di ;remember new start
mov dx,cx ;save length
rep movsb ;move it left 1 char
;(make room for AsciiZ 0)
mov byte ptr [di],0 ;AsciiZ the name
mov cx,dx ;restore count/length
mov si,ax ;new start (1 char to left)
mov dx,ax ;remember in DX
mov mnameptr,ax ;assume no paths v1.6a
cmp byte ptr 1[si],':' ;a drive separator?
jnz MT_NoDrive ;nope
mov ax,2
add si,ax ;bump past d:
sub cx,ax ;adjust length counter
jbe MT_Fail ;zeroed out, flunk it!
MT_NoDrive:
;Check for directory slashes
mov di,si ;starting point
;PKZIP uses the '/' character for paths!
;v1.6b Wonder why GWS is loading the entire AX?
; We're only doing a SCASB for the slash!
; Aha! Because of that je MT01 below .. tricky, tricky...
mov ax,' /' ;scan for PKZIP v1.6a
; directory slashes
MT_SlashScan:
repne scasb
jnz MT_NoSlash ;none
jcxz MT_Fail ;zeroed out, flunk it!
mov si,di ;new starting point
mov byte ptr [di-1], '\' ;old habits never die v1.6a
jmp MT_SlashScan ;and try again until all gone
MT_StepStone: ;a stepping stone v1.6a
jmp short MT_Fail1 ;Johnny hates kludges v1.6a
MT_NoSlash:
;SI now points at first filename char.
cmp verbose,MONOSYL ;are we monosyllabic? v1.6a
je MT_01 ;yes,remember full path v1.6a
mov mnameptr,si ;remember the new address
;v1.5 We'll set a global flag if there was a path.
; Simpler than testing if mnameptr = filename start
; in both verbose and nonverbose display modes.
; DX -> shifted .zFilename
; SI -> filename first char
mov ax,'+ ' ;assume yes (no paths)
;(AL=' ',AH='+')
cmp dx,si ;name^ = zFileName^?
jz MT_1 ;yep, no path
MT_01: ;if monosyl, use ' '! v1.6a
mov al,ah ;'+' ;set the path flag
MT_1:
mov pathflag,al ;post path flag
cmp byte ptr pname1,20H ;empty 2d cmdline parm?
jz MT_Done ;empty, forget the compares
mov di,offset fcb3 ;ES:DI -> dynamic working FCB
mov dx,di ;save a sec
xor ax,ax ;fill with 0's
mov cx,word ptr 6 ;clear old 12-byte filename
rep stosw
mov di,dx ;ES:DI -> dynamic working FCB
mov ax,2900H + 00001101b ;parse filename
;(strip leading spaces,
; don't set drive spec,
; change only if valid)
int 21H
cmp al,0FFH ;failed?
jz MT_Fail ;yep
;We can now compare two expanded file names .. the 2d cmdline parm
;and the ZIP file member.
inc di ;skip leading 0 in filename
mov si,offset pname1 ;possible 2d cmdline parm
MT_CmpLup:
lodsb ;arg1 char
or al,al ;hit end?
jz MT_Done ;yep, done with a match
mov ah,[di] ;snarf expanded name char
inc di ;bump expanded filename pointer
cmp al,'?' ;wildcard? (no test)
jz MT_CmpLup ;yep, no test
cmp al,ah ;match?
jz MT_CmpLup ;yep, continue
MT_Fail: ;no match
pop di ;restore formatted line ptr
MT_Fail1:
stc ;return CF set
MT_X: ret
MT_Done:
pop di
clc ;return CF clear for success
ret
Member_Test endp
;ES:DI -> next position on formatted line
;zFilename is now AsciiZed
;mnameptr contains ptr to original or real zFilename start
;(paths stripped)
Stuff_FileName proc near
push di ;save formatted line ptr
;v1.5 Now using pathflag if target filename has a path
mov al,pathflag ;' ' if no paths,
;'+' if paths
mov si,mnameptr ;ptr to filename start
stosb ;stuff space or '+'
mov dx,di ;name start
add dx,8 ;bump to the dot
SN_Lup: lodsb
or al,al ;hit AsciiZ 0 yet?
jz SN_Done ;yep
cmp verbose,MONOSYL ;monosyllabic? v1.6a
je SN_NoDot ;ay,don't mess with '.' v1.6a
cmp al,'.' ;.typ separator?
jnz SN_NoDot ;nope
cmp di,dx ;where the dot should go?
jz SN_NoDot ;yep, put it there
mov di,dx ; bump to the dot psn
SN_NoDot:
stosb ;stuff in formatted line
jmp SN_Lup
SN_Done:
pop di ;orig ptr
add di,14 ;bump to size psn
ret
Stuff_FileName endp
;SUBROUTINE
;2 calls
Stuff_FileSize proc near
push bx ;Preserve BX! (dta ptr)
add di,3 ;move to number string end
push di ;save it (ptr to last digit)
mov ax,[si]
mov dx,[si+2]
add ax,3FFh ;div 1024 to get Kb
adc dx,0
mov cl,0Ah
shr ax,cl
mov cl,6
shl dx,cl
add ax,dx
mov si,0Ah
SFS_Lup:
xor dx,dx
div si
add dl,30H ;asciify
dec di ;back up the digit pointer
mov [di],dl ;stuff digit in buffer
or ax,ax ;number done?
jnz SFS_Lup ;nope
pop di ;restore line buffer ptr
add di,3 ;bump past ' / ' or ' | '
pop bx
ret
Stuff_FileSize endp
;Verbose display functions
;Format, display single line for each member
;On success, return:
; CF clear
; AL = 0
;On error, return:
; CF set (because of output write fail)
; AL = error code
;Preserve BX (buffer ptr)
;v1.5 Right before the compression style:
; "E" if encrypted
; Else blank
;v1.5 Forgot to show our possible "extended" filename with path
; (via a "+" preceding the stripped filename).
; Worked fine for nonverbose mode, now adding for verbose mode.
sign db ' ' ;local variable
hundred dw 100 ; for computing percentages
Show_Verbose proc near
mov si,mnameptr ;move real member name
;(no paths)
mov di,offset vname ;into formatted line
mov cx,word ptr 14 ;14 spaces in field
;(added 1 for '+')
mov al,pathflag ;' ' if no paths,
;'+' if paths
;v1.6a cleaned this up .. nice!
stosb ;stuff '+'
dec cx ;adjust pad counter
;for the stuffed '+'
SV_Lup:
lodsb ;snarf char
or al,al ;AsciiZ ending?
jz SV_5 ;yep
stosb
loop SV_Lup
SV_5:
mov al,ah ;' '
rep stosb ;pad with spaces
; reduce the size/length to word values
mov si,[bx].zUncmpSiz ; get uncompressed file size
mov ax,[bx].zUncmpSiz[2]
mov cx,[bx].zCmpSiz ;compressed size
mov dx,[bx].zCmpSiz[2]
SVL_51: or ax,ax ; big number?
jz SV_52 ; nope, can use it
shr ax,1 ; yup, divide by two
rcr si,1
shr dx,1
rcr cx,1
jmp SVL_51 ;loop
SV_52:
mov ax,si ; low word of actual size
mov sign,' '
cmp ax,cx ; arc member is larger?
jb SV_520
sub ax,cx ; amount saved
jmp short SV_521
SV_520:
sub ax,cx
neg ax
mov sign,'-'
SV_521:
mul hundred ; to percentage
add ax,50
;v1.5 I'm thinking PK isn't doing this rounding ..
; our percentage figures are sometimes 1% off the PKZIP -v display.
; Close enough for govt work...
adc dx,0 ; round up percent
or si,si ; empty file?
jnz SV_53
mov ax,100
jmp short SV_54
SV_53: div si
SV_54: cmp ax,100 ; archive fouled?
jbe SV_55
sub ax,ax
SV_55:
mov di,offset vfactor-2 ;format stowage factor
call Asciify ;display AX
mov al,sign
mov vfactor,al
;v1.5 If encrypted, stuff an 'E', else a blank
mov di,offset vEncFlag ;space for "E", space
mov ax,' E' ;assume unencrypted
;(AL='E',AH=' ')
test byte ptr [bx].zBitFlag,1 ;If 0 bit is set,
jnz SV_56 ;it's encrypted
mov al,ah ;' ' ;blank (not encrypted)
SV_56:
stosw ;stuff 'E' or space,
;trailing space
;DI -> vstyle field
;v1.5 Adding test to insure compression method (0..6) is in legal range
; (e.g., doesn't overrun our compression style table)
mov si,offset zstyles ;style table start
xor ax,ax ;clear msb
or ax,[bx].zCmpMeth ;bring in method
; of compression
jz SV_58 ;0 -> use table base
cmp al,6 ;max legal
jbe SV_57 ;it's legal
mov al,7 ;' Unknown'
SV_57:
mov cl,3 ; eight bytes each entry
shl ax,cl
SV_58:
add si,ax ;table base + offset
;DI already points to vstyle
mov cx,word ptr 4 ;move as words (8 bytes)
rep movsw
mov dx,[bx].zCmpSiz[2] ;compressed size.hi
mov ax,[bx].zCmpSiz ;compressed size
add totuncmp,ax ;accumulate
adc totuncmp[2],dx
mov di,offset vsize ;format file size
call Asciify_Long
mov dx,[bx].zUncmpSiz[2] ;uncompressed size.hi
mov ax,[bx].zUncmpSiz ;uncompressed size.lo
add totcmp,ax ;accumulate
adc totcmp[2],dx
mov di,offset vlength ;format file length
call Asciify_Long
mov ax,[bx].zModDate ; format file date
call GetDate
mov ax,[bx].zModTime ; format file time
call GetTime
;v1.4a mov ax,[bx].zCrc32 ; format crc in hex
;v1.4a mov di,offset vcrc ;(just low word for now)
;v1.4a call Cvh
mov ax,[bx].zCrc32 ; format crc.lo in hex
mov di,offset vcrc + 4 ; v1.4a
call Cvh ; v1.4a
mov ax,[bx].zCrc32[2] ; format crc.hi in hex v1.4a
mov di,offset vcrc
call Cvh
inc totmbrs ;bump total file count
mov dx,offset vline ;display formatted info
mov cx,VLINELEN
call Pr_StdOut
ret
Show_Verbose endp
;Formats, displays verbose totals
Format_Totals proc near
mov ax,totmbrs ;total members
add ttotmbrs,ax ;accumulate
;v1.5 Don't display totals for this ZIP file's members
; unless there's more than 1.
xor bp,bp ;use BP for a flag
;(undisturbed by Asciify)
;(0 = only 1 file)
cmp ax,1 ;just one?
jbe FT_1 ;yep, no Asciify
inc bp ;flag more than 1 file
mov di,offset vtmbrs-2 ;format total members
call Asciify
FT_1: mov dx,totcmp[2] ;total compressed file size
mov ax,totcmp
add ttotcmp,ax ;accumulate total totals
adc ttotcmp[2],dx
or bp,bp ;just 1 file?
jz FT_2 ;yep, no Asciify
mov di,offset vtlen ;format total compressed file
;size
call Asciify_Long
FT_2:
mov dx,totuncmp[2] ; total uncompressed file size
mov ax,totuncmp
add ttotuncmp,ax ;accumulate total totals
adc ttotuncmp[2],dx
or bp,bp ;just 1 file?
jz FT_9 ;yep, no Asciify
;no fancy computations
mov di,offset vtsize ;format total uncompressed
;file size
call Asciify_Long
; reduce the total size/length to word values
mov si,totcmp ; get compressed file size
mov ax,totcmp[2]
mov cx,totuncmp ; uncompressed file size
mov dx,totuncmp[2]
FTDiv: or ax,ax ; big number?
jz FT_4 ; nope, can use it
shr ax,1 ; yup, divide by two
rcr si,1
shr dx,1
rcr cx,1
jmp short FTDiv
FT_4:
mov ax,si
mov sign,' ' ;whata kludge
cmp ax,cx ;compressed > uncompressed?
jb FT_5 ;yep
sub ax,cx ;amount saved
jmp short FT_6
FT_5: sub ax,cx
neg ax
mov sign,'-'
FT_6: mul hundred ; to percentage
add ax,50
adc dx,0 ; round up percent
or si,si ; empty file?
jnz FT_7
mov ax,100
jmp short FT_8
FT_7: div si
FT_8: mov di,offset vtsf-2 ;format stowage factor
call Asciify ;AX
mov al,sign
mov vtsf,al
FT_9:
mov di,offset totcmp ;starting at totcmp
mov cx,TOTLEN/2 ;length of totals to clear
; (words)
xor ax,ax ;handy 0
rep stosw
or bp,bp ;just 1 file?
jz FT_X ;yep, exit
mov dx,offset vthdr ;prepare to display totals
mov cx,VTHDRLEN ;msg length
FT_X:
ret ;to display
Format_Totals endp
; format the time (in AX)
time record hour:5,min:6,sec:5 ;packed time
GetTime proc near ;format the date
mov di,offset vtime
or ax,ax ;it is zero?
jz GotTime
push ax ;save date
and ax,mask hour ;get hour part
mov cl,hour ;bits to shift
shr ax,cl
call Cnvrt1
stosw
mov al,':'
stosb
GT3: pop ax ;get the time back
and ax,mask min ;get min part
mov cl,min ;bits to shift
call Cnvrt
stosw
GotTime:ret
GetTime endp
Cnvrt2 proc near ;convert to ascii
call Cnvrt
cmp al,'0' ;suppress leading zero
jne Cnvrtd
mov al,' '
ret
Cnvrt: shr ax,cl
Cnvrt1: aam ;make al into bcd
or ax,'00' ; and to ascii
xchg al,ah
Cnvrtd: ret
Cnvrt2 endp
page
; format the date (in AX)
date record yr:7,mo:4,dy:5 ;packed date
GetDate proc near ;format the date
or ax,ax ;is it zero?
jz GotDate
push bx ;preserve BX (buff ptr)
push ax ;save date
and ax,mask yr ;get year part
mov cl,yr ;bits to shift
call Cnvrt
mov di,offset vyear
or al,'8' ;adjust for base year
stosw
pop bx ;get the date back
push bx ;save it
and bx,mask mo ;get month part
mov cl,mo ;bits to shift
shr bx,cl
add bx,bx ; form month table index
add bx,bx
lea si,word ptr months-4[bx]
mov cx,word ptr 3
mov di,offset vmonth
rep movsb
pop ax ;get the date back
and ax,mask dy ;get day part
mov cl,dy ;bits to shift
call Cnvrt
mov di,offset vdate
stosw
pop bx ;restore buff ptr
GotDate:ret
GetDate endp
;A severely hacked single/double precision number conversion function.
;Originally from JMODEM, but severely hacked by Toad Hall.
;ES:DI -> string
;Destroys everything almost.
;Enter here if integer in AX
Asciify proc near
push bx ;save buff ptr
xor dx,dx ; clear fake long.hi
mov si,ax ;move integer into SI
xor ah,ah ;clear msb (flag)
jmp short Ascii_Ax ;jump into the code
;Enter here if long integer in DX:AX.
Asciify_Long:
push bx ;save buff ptr
mov si,ax ;move long.lo into SI
xor ah,ah ;clear msb (flag)
Comment ~
Taking out the extremely high numbers to reduce column width.
MOV CX,3B9AH ; Get billions
MOV BX,0CA00H
CALL Subtr ; Subtract them out
MOV CX,05F5H ; Get hundred-millions
MOV BX,0E100H
CALL Subtr ; Subtract them out
Comment ends ~
and dx,4FFH ;seems likely
MOV CX,word ptr 0098H ; Get ten-millions
MOV BX,9680H
CALL Subtr ; Subtract them out
MOV CX,word ptr 000FH ; Get millions
MOV BX,4240H
CALL Subtr ; Subtract them out
MOV CX,word ptr 1 ; Get hundred-thousands
MOV BX,86A0H
CALL Subtr ; Subtract them out
Ascii_Ax:
xor cx,cx ; Get ten-thousands
MOV BX,2710H
CALL Subtr ; Subtract them out
MOV BX,03E8H
CALL Subtr ; Subtract them out
MOV BX,word ptr 0064H
CALL Subtr ; Subtract them out
MOV BX,word ptr 10
CALL Subtr ; Subtract them out
mov ax,si ;residual in SI
add AL,'0' ; Add bias to residual
stosb ; Put in the string
pop bx ;restore buff ptr
RET
;Common subroutine for Asciify
Subtr: mov al,'0'-1
Subtr1: INC al ; Bump the digit character
SUB si,BX ; Dword subtraction
SBB DX,CX
JNB Subtr1 ; Continue until a carry
ADD si,BX ; One too many, add back
ADC DX,CX ; and the remainder
cmp al,'0'
jnz Subtr2 ;nope, turn off leading flag,
; stuff
or ah,ah ;no more leading spaces?
jnz Sub_Stuff ;right, stuff the '0'
mov al,' ' ;make it neat
; with leading spaces
Sub_Stuff:
stosb ;stuff the char
RET
Subtr2: inc ah ;turn off leading space flag
stosb
ret
Asciify ENDP
;Convert 16-bit binary word in AX
;to hex ASCII string at ES:DI
;(Protect BX, directory array pointer)
hexchar db '0123456789ABCDEF'
Cvh proc near
push bx ;save buff ptr
mov si,offset hexchar ;for faster access
mov dx,ax ; save 16-bits
mov bl,dh ; third nibble
xor bh,bh ;clear msb
mov cx,0F04H ;CL=4 for shifting,
;CH=0FH for masking
shr bl,cl
mov al,[si][bx] ;snarf hex char
stosb
mov bl,dh ; last nibble
and bl,ch ;0fh
mov al,[si][bx] ;snarf hex char
stosb
mov bl,dl ; first nibble
shr bl,cl ; isolate (CL still 4)
mov al,[si][bx] ;snarf hex char
stosb
mov bl,dl ; second nibble
and bl,ch ;0fh ; isolate
mov al,[si][bx] ;snarf hex char
stosb
pop bx ;restore buff ptr
ret
Cvh endp
;SUBROUTINE
Pr_CrLf proc near
mov dx,offset crlf
mov cx,CRLFLEN ;fall through to ...
Pr_CrLf endp
Pr_StdOut proc near
push bx ;preserve bx
mov bx,STDOUT
mov ah,40H ;write
int 21H
pop bx
ret
Pr_StdOut endp
;Print null-terminated (AsciiZ) string like int 21h function 9
;Enter with DS:DX -> AsciiZ string
;Destroys AX
;On success, return:
; CF clear
; AL = 0
;On failure (StdOut write fail), return:
; CF set
; AL = error
PrintS proc near
mov cx,0FFFFH ;max scan
xor al,al ;handy 0
mov di,dx ;string start
repne scasb ;find the terminator
inc cx ;adjust
not cx ;CX=length
call Pr_StdOut ;display to StdOut
ret
PrintS endp
;Reinit our nonverbose formatted display line
;Returne ES:DI -> formatted line start
Refresh_LineBuff proc near
mov si,offset blankline ;formatted display line
mov ax,offset LINEBUFF ;dynamic data area
mov di,ax ;move to ...
mov cx,LINELEN ;length
rep movsb
mov di,ax ;ES:DI -> formatted line start
ret
Refresh_LineBuff endp
;Parses cmdline for target files.
;If failure:
; Returns CF set,
; AL = ERRORLEVEL,
; DX -> error msg
;Adding command line switch ('-v') parsing.
;And '-m', too. Also accept '/' as switch char. v1.6a
;And fixing the bug.
Parse_CmdLine proc near
call _Args ;parse cmdline
;returns AX = argc
or ax,ax ;any argc?
jnz Got_Parm ;yep
Cmd_Err:
mov dx,offset usage ;intro, usage
mov cx,USAGELEN ;total length
mov al,1 ;errorlevel 1
stc ;CF set
ret ;for a jmp to Msg_Term
Got_Parm:
mov cx,ax ;argc
;See if any of the argv's are our '-v' verbose switch.
;Or '-m'. Or '/v'. Or '/m'. Or. Or. Or. v1.6a
;If so, turn switch on, clear that argv.
cmp cx,word ptr 1 ;just 1 arg? v1.6b
jz Chk_ZipName ;yep, can't be any switches
mov bx,offset argv[2] ;start with ^argv(1)
Chk_VSwitch:
mov si,[bx] ;argv^(1)
cmp byte ptr 2[si],0 ;arg longer than 2 chars?v1.6a
jnz Chk_VRelup ;yes, can't be switch v1.6a
mov ax,[si] ;snarf possible -V parm
cmp al,'-' ;is first(!) char '-'? v1.6a
je Found_Some_Switch ;yes, check which v1.6a
cmp al,'/' ;is first(!) char '/'? v1.6a
jne Chk_VRelup ;no, cant' be switch v1.6a
Found_Some_Switch: ; v1.6a
or ah,32 ;force to lowercase
mov al,VERBO ;assume it's 'v' v1.6a
cmp ah,'v' ;'v'? v1.6a
jz Found_Switch ;yep v1.6a
cmp ah,'m' ;'m'? v1.6a
jnz Chk_VRelup ;nope
mov al,MONOSYL ;hah-it's monosyllabic v1.6a
Found_Switch: ; v1.6a
cmp byte ptr 2[si],0 ;Just the '-v'?
jnz Chk_VRelup ;nope, must be a name
mov verbose,al ;got switch,set verbose v1.6a
mov byte ptr [si],0 ;clear this argv
cmp cx,argc ;first argc?
jnz Chk_V_NoShift ;nope
inc argc ; .. sigh .. readjust
call _Shift ;yep, move other args down one
;(will decr argc again)
Chk_V_NoShift:
dec argc ;final decr to eliminate it
jmp short Chk_ZipName ;done
Chk_VRelup:
add bx,2 ;next argv^
loop Chk_VSwitch ;check all args
;If argv(1) was the '-v' switch,
;or that other one, v1.6a
;that argv has been cleared via the _Shift call.
;The target zip file name HAS to be argv(1)!
Chk_ZipName:
mov cx,argc ;argc arg counter
jcxz Cmd_Err ;he only had a switch!
mov bx,offset argv[2] ;^argv(1)
mov si,[bx] ;argv^(argc)
call Parse_MoveZipName ;handle the zip name
call _Shift ;move argv's down one
;Check for member testing (2d or 3d argv).
;We make the call even if argc = 0 (to clear a buffer)
Chk_MbrName:
call Parse_MoveMbrName ;handle the member name
Parse_Done:
clc
ret
Parse_CmdLine endp
;Cmdline parsing subroutine
;Moves argv into our targetfile buffer,
;picks up a pointer to past the paths (if any)
;SI -> argv
Parse_MoveZipName proc near
push si ;save SI
mov bx,offset ziptarget ;full zip target filename
mov di,bx
mov cx,64 ;clear ziptarget
xor ax,ax
rep stosw
mov di,bx ;DI -> ziptarget
Move_ZipName:
lodsb ;snarf cmdline char
or al,al ;AsciiZ terminator?
jz Parse_Zip_Type ;yep
stosb ;stuff
jmp Move_ZipName
Parse_Zip_Type:
;DI -> last ziptarget's real char +1
mov dx,di ;end, last char +1
sub dx,bx ;end - start = length
mov di,bx ;back to start
mov cx,dx ;counter for a scan
mov al,'.'
repne scasb ;find a .TYP separator
;DI -> char after '.' or actual filename end
jnz No_Type
dec di ;back up to the '.'
No_Type:
mov si,offset ziptyp ;assume need full '.ZIP'
movsw ;copy .ZIP in
movsw ;(4 chars)
mov dx,di ;end
sub dx,bx ;end - start = length
dec di ;adjust from last movsb
std ;backwards
mov si,di ;SI -> ziptarget last char
;BX = ziptarget start
;SI -> ziptarget lastchar
;DX = ziptarget length
;DI -> ziptarget lastchar
;Now find any path separators in ziptarget
mov cx,dx ;scan length
mov al,'\' ;subdir dividers
repne scasb ;scan till we hit one
jz Zip_GotPath ;DI -> char before the '\'
mov di,si ;back to last char
mov cx,dx ;scan length
mov al,':' ;how about just drive?
repne scasb
jnz Zip_NoPath ;nope, DI -> ziptarget-1
;skip the extra inc
Zip_GotPath:
inc di ;bump to '\' or ':'
Zip_NoPath:
inc di ;and past it to filename's char
cld ;forward again
mov znameptr,di ;points to start of
;true filename (beyond path)
;for later appending Find Next
;filenames to path
pop si ;restore
ret
Parse_MoveZipName endp
;Cmdline parsing subroutine
;Has DOS parse the member name (could be wildcards).
;Moves that parsed name into runtime buffer.
;Enters with SI -> AsciiZ member name.
Parse_MoveMbrName proc near
mov di,offset pname1
mov cx,6 ;12 bytes long
mov ax,2020H
rep stosw ;clear with spaces
mov cx,argc ;argc counter
jcxz Parse_MbrDone ;all done, no member
mov bx,offset argv[2] ;it's now ^argv(2)
mov si,[bx] ;argv^(argc)
mov di,offset fcb3
mov ax,2900H + 00001100b ;parse filename
;(no leading spaces,
; don't set drive spec,
; change only if valid)
int 21H
cmp al,0FFH ;failed?
jz Parse_MbrDone ;yep
mov si,di ;0 preceding parsed name
inc si ;bump past 0
mov di,offset pname1 ;dynamic variable space
mov cx,6 ;11 filename chars + AsciiZ 0
rep movsw
Parse_MbrDone:
ret
Parse_MoveMbrName endp
;---- args.asm ----------------------------------------------------------
;from KEGELUNX.ARC Unix-like utils.
;
; Args parses the command line into a unix-style parameter array.
; CALL _ARGS to have it parse the cmdline.
; Argc and Argv are just as in C; argc = # of params on cmd line,
; argv is an array of pointers to strings (null terminated, of course).
; Because MS-DOS doesn't pass us the name used to invoke the program,
; argv[0] always points to a null string.
; _Shift updates argc.
;
;--------------------------------------------------------------------------
; Maximum number of parameters
MAXPARMS equ 4
_Args proc near
; initialize
cld ; clear decrement mode
mov bx, 2 ; argc = 0 (will sub 2 from bx at end)
mov argv[0], offset null ; prog name unknown; set to null.
mov si,offset PARAMS ; i = 0
mov cl,nchar ; cx = # of chars in command line
xor ch,ch
jcxz Args_Done ; no arg chars -> we're done.
mov di,offset argbuff ;big arg buffer
; Move arguments out of default DTA.
push cx
push di
rep movsb
pop si
pop cx
; Big loop- find arguments...
mov ah,20H ;handy space
ParmL:
; Little loop #1: strip leading blanks from argument.
; while (i<NCHAR && params[i] = ' ') do i++;
StripL: lodsb ; al = [si++]
cmp al,ah ; space?
loopz StripL ; if so, keep skipping.
jne Args_GotOne ; If we found a nonblank,
; skip zero test.
jcxz Args_Done ; found no unblank chars -> we're done.
Args_GotOne:
dec si ; bump SI back to start of nonblank.
inc cx
mov argv[bx],si ; save pointer to this string
; Little loop #2: skip nonblank chars.
; while (i<NCHAR && params[i] <> ' ') do i++;
SkipL: lodsb
cmp al,ah
loopnz SkipL
jz Oky
; Last char of line was not blank.
inc si ; make next statement put null
; AFTER arg.
Oky:
mov byte ptr [si][-1], 0 ; put null at end of arg
add bx,2 ; argc++
jcxz Args_Done ; if we ran off end of cmdline,
; no more args.
cmp bx, MAXPARMS*2
jb ParmL ; loop if argc < MAXPARMS.
Args_Done:
; All done finding parms; now share argc with caller.
mov ax,bx
sub ax, 2
shr ax, 1
mov argc,ax ;save argc
or ax,ax ;no args at all?
jz Args_X ;yep, leave now
;While we're here, let's uppercase all the args
mov cx,ax ;argc loop counter
mov dl,20H ;handy constant for uppercasing
mov ah,'a' ;and for testing
mov bx,offset argv[2] ;^argv(1)
Args_UpLoop:
mov si,[bx] ;argv^(argc)
dec si ;prep for upcoming...
Argv_UpLoop:
inc si
mov al,[si] ;snarf char
or al,al ;argv^(argc) end?
jz Args_Relup ;yep, bump argc
cmp al,ah ;'a' ;lowercase?
jb Argv_UpLoop ;nope
sub [si],dl ;uppercase
jmp Argv_UpLoop ;next char
Args_Relup:
add bx,2 ;next argv^
loop Args_UpLoop
mov ax,argc ;return argc
Args_X:
ret
_Args endp
;---- _Shift: --------------------------------------------
; Shifts %2 to %1, %3 to %2, etc. Leaves %0 alone.
; Works by shuffling argv[*].
_Shift proc near
cld
mov si, offset argv[4]
mov di, offset argv[2]
mov cx, MAXPARMS
rep movsw
dec argc
ret
_Shift endp
;---- parameter count, array ----------------
; Pointers to up to MAXPARMS parameter strings are held here.
null dw 0 ; the null string
argc dw ?
argv dw MAXPARMS dup (null)
;Dynamic variables start here.
;Not REAL big .. we could calculate them and release unneeded memory ..
;but who needs it? This ain't TSR, and the normal 64Kb required for
;a .COM program shouldn't stress anyone's system capacity.
dta1BA equ $ ;alternate DTA
ziptarget equ dta1BA + 42 ;up to 128 bytes
pname1 equ ziptarget + 128 ;11-byte FCB filename + 0
fcb3 equ pname1 + 14 ;full 44-byte FCB
Comment ~ Looks like:
+filename.typ 000K / 000K +filename.typ 000K / 000K +filename.typ 000K / 000K
Comment ends ~
argbuff equ fcb3 + 44 ;cmdline parsing buff
linebuff equ argbuff ;82 chars long
dirbuff equ linebuff + 82 ;ENDOFS bytes long
CSEG ends
end Zdir