3472 lines
85 KiB
NASM
3472 lines
85 KiB
NASM
page ,132
|
||
title DIR Internal Command
|
||
;/*
|
||
; * Microsoft Confidential
|
||
; * Copyright (C) Microsoft Corporation 1991
|
||
; * All Rights Reserved.
|
||
; */
|
||
|
||
;*** DIR.ASM - DIR internal command
|
||
|
||
comment % =================================================================
|
||
|
||
This module replaces TCMD1A.ASM. The old module was titled
|
||
"PART4 COMMAND Transient routines".
|
||
|
||
From residual documentation, I surmise that TCMD.ASM originally
|
||
contained the internal commands DIR, PAUSE, ERASE, TYPE, VOL, and
|
||
VER. The file seems to have been successively split:
|
||
|
||
TCMD -> TCMD1,TCMD2 -> TCMD1A,TCMD1B,TCMD2A,TCMD2B
|
||
|
||
TCMD1A.ASM contained only the DIR command.
|
||
|
||
Usage:
|
||
------
|
||
|
||
DIR <filespec> /w /p /b /s /l /o<sortorder> /a<attriblist>
|
||
|
||
DIR /?
|
||
|
||
|
||
<filespec> may include any or none of: drive; directory path;
|
||
wildcarded filename. If drive or directory path are
|
||
omitted, the current defaults are used. If the
|
||
file name or extension is omitted, wildcards are
|
||
assumed.
|
||
|
||
/w Wide listing format. Files are displayed in compressed
|
||
'name.ext' format. Subdirectory files are enclosed in
|
||
brackets, '[dirname]'.
|
||
|
||
/p Paged, or prompted listing. A screenful is displayed
|
||
at a time. The name of the directory being listed appears
|
||
at the top of each page.
|
||
|
||
Bugbug: pages nead to be uniform length..?
|
||
|
||
/b Bare listing format. Turns off /w or /p. Files are
|
||
listed in compressed 'name.ext' format, one per line,
|
||
without additional information. Good for making batch
|
||
files or for piping. When used with /s, complete
|
||
pathnames are listed.
|
||
|
||
/s Descend subdirectory tree. Performs command on current
|
||
or specified directory, then for each subdirectory below
|
||
that directory. Directory header and footer is displayed
|
||
for each directory where matching files are found, unless
|
||
used with /b. /b suppresses headers and footers.
|
||
|
||
Tree is explored depth first, alphabetically within the
|
||
same level.
|
||
|
||
Bugbug: hidden directories aren't searched.
|
||
|
||
/l Display file names, extensions and paths in lowercase. ;M010
|
||
|
||
/o Sort order. /o alone sorts by default order (dirs-first, name,
|
||
extension). A sort order may be specified after /o. Any of
|
||
the following characters may be used: nedsg (name, extension,
|
||
date/time, size, group-dirs-first). Placing a '-' before any
|
||
letter causes a downward sort on that field. E.g., /oe-d
|
||
means sort first by extension in alphabetical order, then
|
||
within each extension sort by date and time in reverse chronological
|
||
order.
|
||
|
||
/a Attribute selection. Without /a, hidden and system files
|
||
are suppressed from the listing. With /a alone, all files
|
||
are listed. An attribute list may follow /a, consisting of
|
||
any of the following characters: hsdar (hidden, system,
|
||
directory, archive, read-only). A '-' before any letter
|
||
means 'not' that attribute. E.g., /ar-d means files that
|
||
are marked read-only and are not directory files. Note
|
||
that hidden or system files may be included in the listing.
|
||
They are suppressed without /a but are treated like any other
|
||
attribute with /a.
|
||
|
||
/? Help listing. Display DIR useage information. ;M008;Handled externally
|
||
|
||
/h has been removed. ;M008
|
||
|
||
DIRCMD An environment variable named DIRCMD is parsed before the
|
||
DIR command line. Any command line options may be specified
|
||
in DIRCMD, and become defaults. /? will be ignored in DIRCMD.
|
||
A filespec may be specified in DIRCMD and will be used unless
|
||
a filespec is specified on the command line. Any switch
|
||
specified in DIRCMD may be overridden on the command line.
|
||
If the original DIR default action is desired for a particular
|
||
switch, the switch letter may be preceded by a '-' on the
|
||
command line. E.g.,
|
||
|
||
/-w use long listing format
|
||
/-p don't page the listing
|
||
/-b don't use bare format
|
||
/-s don't descend subdirectory tree
|
||
/-o display files in disk order
|
||
/-a suppress hidden and system files
|
||
|
||
|
||
Notes:
|
||
------
|
||
|
||
For sorted listings, file entries are loaded into the TPA buffer, which
|
||
is usually about 64K in size. This allows sorts of up to 3000 files at
|
||
a time. Each entry takes up 21 bytes in the buffer (see EntryStruc below).
|
||
The byte after the last entry is 0FFh. The first byte of each entry is
|
||
a flag byte which is made zero when the entry is loaded, and made one
|
||
when the entry is used.
|
||
|
||
|
||
Revision History
|
||
================
|
||
M01 md 7/13/90 Use ROM BIOS data area to obtain screen height
|
||
in the absence of ANSI.SYS
|
||
|
||
M007 sa 8/1/90 Allow /p/b combination
|
||
|
||
M008 sa 8/1/90 Remove /h parameter. Eliminate code used
|
||
to internally handle /? message.
|
||
|
||
M010 sa 8/5/90 Add support for /l (lowercase) option.
|
||
|
||
M011 sa 8/5/90 Patch up bug where MS-DOS does not load the
|
||
first FCB with the drive number when the drive
|
||
letter in the command line is preceded by a
|
||
switch. Now dir manually loads the drive
|
||
number after parsing.
|
||
|
||
M018 md 8/12/90 Increment the screen height by 1 when obtained
|
||
from the ROM BIOS.
|
||
|
||
M023 sa 8/31/90 Prevent DIR from failing if it encounters
|
||
a subdirectory having len(pathname)>MAXPATH.
|
||
Just skip over that subdirectory.
|
||
|
||
M028 dbo 9/24/90 When country=US, sort by strict character
|
||
byte value, rather than collating table.
|
||
This to match MS-DOS Shell's sort order.
|
||
|
||
========================================================================= %
|
||
|
||
|
||
|
||
|
||
;*** SYMBOLS & MACROS
|
||
|
||
.xlist
|
||
.xcref
|
||
|
||
include comsw.asm ; get COMMAND version switches
|
||
include dossym.inc ; get DOS basic symbol set
|
||
include syscall.inc ; get DOS call names
|
||
include doscntry.inc ; get extended country info symbols
|
||
include bpb.inc
|
||
include filemode.inc
|
||
include find.inc
|
||
include comseg.asm ; define segment order
|
||
include comequ.asm ; get equates for COMMAND routines
|
||
include ioctl.inc ; get symbols for ioctl's
|
||
include rombios.inc ; get ROM BIOS data definition
|
||
|
||
.list
|
||
.cref
|
||
|
||
;M008;NUM_DIR_SWS reduced by 2 for /h,/? not used
|
||
;M010;NUM_DIR_SWS increased by 2 for /l,/-l
|
||
NUM_DIR_SWS equ 14 ; # of dir switch synonyms in Dir_Sw_Ptrs list
|
||
|
||
;M010;'lcase' replaces removed 'help' in OptionRec
|
||
OptionRec record inmem:1,lcase:1,bare:1,subd:1,pagd:1,wide:1
|
||
; on/off bit record for /l, /b, /s, /p, /w options
|
||
; (order is hard-coded; see OnOffSw)
|
||
; Inmem is set when entries are loaded in memory.
|
||
|
||
NUM_ATTR_LTRS equ 6 ; length of attribute letter list
|
||
NUM_ORDER_LTRS equ 5 ; length of sort order letter list
|
||
|
||
ResultBuffer struc ; structure of parse result buffer
|
||
ValueType db ?
|
||
ValueTag db ?
|
||
SynPtr dw ?
|
||
ValuePtr dd ?
|
||
ResultBuffer ends
|
||
|
||
ErrorRec record baddir:1,dev:1
|
||
; Error bits are:
|
||
; Invalid directory format
|
||
; File is device
|
||
|
||
EntryStruc struc ; our private directory entry structure
|
||
used db ? ; =0 until entry used, then =1
|
||
filename db 8 dup (?) ; filename
|
||
fileext db 3 dup (?) ; extension
|
||
fileattr db ? ; file attributes
|
||
filetime dw ? ; file time
|
||
filedate dw ? ; file date
|
||
filesize dd ? ; file size
|
||
EntryStruc ends
|
||
|
||
shove macro val ; hose-bag 8086 doesn't push immediate
|
||
mov ax,val ; invisible, dangerous use of AX!
|
||
push ax
|
||
endm
|
||
|
||
|
||
|
||
|
||
;*** DATA
|
||
|
||
DATARES segment public byte
|
||
|
||
extrn Append_Flag:byte ; true when APPEND needs to be reset
|
||
extrn Append_State:word ; state to reset APPEND to
|
||
|
||
DATARES ends
|
||
|
||
|
||
TRANDATA segment public byte
|
||
|
||
extrn AttrLtrs:byte ; list of attribute letters
|
||
extrn BadCd_Ptr:word ; "invalid directory" msg block
|
||
extrn Bytes_Ptr:word ; "%1 bytes" msg block
|
||
extrn BytMes_Ptr:word ; "%1 bytes free" msg block
|
||
extrn DirCont_Ptr:word ; "(continuing %1)" msg block
|
||
extrn DirDat_Yr:word ; year field of date msg block
|
||
extrn DirDat_Mo_Day:word ; month/day field of date msg block
|
||
extrn DirDatTim_Ptr:word ; date/time msg block
|
||
extrn DirEnvVar:byte ; DIR environment variable name
|
||
extrn DirHead_Ptr:word ; directory header message block
|
||
extrn DirMes_Ptr:word ; "%1 File(s)" msg block
|
||
extrn DirTim_Hr_Min:word ; time field of msg block
|
||
extrn Dir_Sw_Ptrs:word ; list of DIR switch synonym ptrs
|
||
extrn Disp_File_Size_Ptr:word ; file size message block
|
||
extrn DMes_Ptr:word ; <DIR> message block
|
||
extrn ErrParsEnv_Ptr:word ; "(Error occurred in env.." msg blk
|
||
extrn Extend_Buf_Ptr:word ; extended error message block
|
||
extrn Extend_Buf_Sub:byte ; # substitions in message block
|
||
extrn Msg_Disp_Class:byte ; message display class
|
||
extrn OrderLtrs:byte ; list of sort order letters
|
||
extrn Parse_Dir:byte ; DIR parse block
|
||
extrn String_Buf_Ptr:word ; message block ptr to string
|
||
extrn Tab_Ptr:word ; tab output block
|
||
extrn Total_Ptr:word ; "Total:" msg block
|
||
|
||
TRANDATA ends
|
||
|
||
|
||
TRANSPACE segment public byte
|
||
|
||
extrn AttrSelect:byte ; attribute select states -
|
||
extrn AttrSpecified:byte ; attribute mask -
|
||
|
||
; Attribute conditions are recorded in two steps.
|
||
; AttrSpecified indicates which attributes are to be checked.
|
||
; AttrSelect indicates which state the specified attributes
|
||
; must be in for a file to be included in the listing.
|
||
; Attributes not indicated in AttrSpecified are ignored when
|
||
; deciding which files to include.
|
||
|
||
extrn Bits:word ; some option flags (see OptionRec)
|
||
extrn BwdBuf:byte ; 'build working dir string' buf
|
||
extrn BytCnt:word ; # bytes in TPA
|
||
extrn Bytes_Free:word ; #bytes free for BytMes_Ptr msg block
|
||
extrn CharBuf:byte ; character string buffer
|
||
extrn ComSw:word ; error bits (see ErrorRec)
|
||
extrn CountryPtrInfo:byte ; buffer for collating table ptr
|
||
extrn CountryPtrId:byte ; info ID for collating table ptr
|
||
extrn CountryPtr:dword ; collating table ptr
|
||
extrn CurDrv:byte ; current drive # (0-based)
|
||
extrn DestBuf:byte ; null-terminated sort codes -
|
||
|
||
; Sort order is specified as a series of 0 to 5 sort code
|
||
; bytes, followed by a zero byte.
|
||
; Codes are 1=name, 2=extension, 3=date&time, 4=size, and
|
||
; 5=filetype (subdir or not).
|
||
; Bit 7 of code is set for a downwards sort.
|
||
|
||
extrn DestIsDir:byte ; indicator of delim char's in path
|
||
extrn DestTail:word ; ptr to filename in pathname
|
||
extrn Dir_Num:word ; #files for DirMes_Ptr msg block
|
||
extrn DirBuf:byte ; DTA buffer for DOS calls
|
||
extrn DirFlag:byte ; signal to PathCrunch routine
|
||
extrn Display_Ioctl:word ; display info block for IOCTL call
|
||
extrn EndDestBuf:byte ; end of DestBuf (sort order codes)
|
||
extrn File_Size_High:word ; field for file size message block
|
||
extrn File_Size_Low:word ; field for file size message block
|
||
extrn FileCnt:word ; file count in a single directory
|
||
extrn FileCntTotal:dword ; file count in all directories
|
||
extrn FileSiz:dword ; file sizes in a single directory
|
||
extrn FileSizTotal:dword ; file sizes in all directories
|
||
extrn InternatVars:byte ; buffer for international info
|
||
extrn LeftOnLine:byte ; entries left on current display line
|
||
extrn LeftOnPage:word ; lines left on page
|
||
extrn LinPerPag:word ; lines/page entry in Display_Ioctl
|
||
extrn Msg_Numb:word ; extended error code
|
||
extrn OldCtrlCHandler:dword ; old int 23 vector
|
||
extrn Parse1_Syn:word ; ptr to matched synonym
|
||
extrn PathCnt:word ; length of pathname (see PathPos)
|
||
extrn PathPos:word ; ptr to pathname buffer (see SrcBuf)
|
||
extrn PerLine:byte ; # entries per line
|
||
extrn ResSeg:word ; RESGROUP seg addr
|
||
extrn ScanBuf:byte ; buffer for environment value and
|
||
; subdirectory names
|
||
extrn SrcBuf:byte ; pathname buffer
|
||
extrn String_Ptr_2:word ; message substitution string ptr
|
||
extrn Tpa:word ; TPA buffer seg addr
|
||
|
||
TRANSPACE ends
|
||
|
||
|
||
|
||
|
||
;*** PRINCIPAL ROUTINES
|
||
|
||
TRANCODE segment public byte
|
||
|
||
extrn CError:near ; COMMAND error recycle point
|
||
|
||
public Catalog ; our entry point
|
||
|
||
break <DIR (Catalog) principal routines>
|
||
|
||
assume cs:TRANGROUP,ds:TRANGROUP,es:nothing,ss:TRANGROUP
|
||
|
||
; Bugbug: Each routine should start with it's own ASSUME.
|
||
|
||
|
||
|
||
|
||
;*** Catalog - DIR command main routine
|
||
;
|
||
; ENTRY FCB #1 in PSP has drive# from cmd-line or default
|
||
; Cmd-line tail text is at 81h, terminated by 0Dh
|
||
; CS, DS, ES, SS = TRANGROUP seg addr
|
||
; Tpa = TPA buffer seg addr
|
||
; BytCnt = # bytes in TPA buffer
|
||
;
|
||
; EXIT nothing
|
||
;
|
||
; USED AX,BX,CX,DX,SI,DI,BP
|
||
;
|
||
; ERROR EXITS
|
||
;
|
||
; Errors are handled by setting up error message pointers
|
||
; for Std_EPrintf and jumping to CError. Syntax errors in
|
||
; the environment variable, however, are handled by printing
|
||
; an error message and continuing.
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; Directory listing is displayed (on standard output).
|
||
; APPEND is disabled. HeadFix routine is expected to
|
||
; restore APPEND state.
|
||
; Working directory may be changed. The user's default
|
||
; directory is saved and flagged for restoration by RestUDir
|
||
; during COMMAND cycle.
|
||
; Lots of variables may be changed in TRANSPACE segment.
|
||
;
|
||
; NOTES
|
||
;
|
||
; ES = TRANGROUP seg addr except when used to address the
|
||
; the TPA buffer, where directory entries are loaded from disk.
|
||
|
||
Catalog proc
|
||
|
||
call SetDefaults
|
||
call ParseEnvironment
|
||
call ParseCmdLine
|
||
jnc @F ; no parse error
|
||
jmp catErr ; error msg is set up
|
||
@@: call SetOptions
|
||
call SetCollatingTable
|
||
|
||
; Drive # to operate on has already been placed in FCB by
|
||
; COMMAND preprocessing. OkVolArg & PathCrunch depend on that.
|
||
|
||
test Bits,mask bare
|
||
jnz @F ; don't display volume info for /b
|
||
invoke OkVolArg ; find & display volume info
|
||
sub LeftOnPage,2 ; record display lines used by volume info
|
||
jmp short catCrunch
|
||
|
||
; OkVolArg side effects:
|
||
; APPEND is disabled;
|
||
; DTA established at DirBuf;
|
||
; Filename fields in FCB are wildcarded.
|
||
|
||
@@:
|
||
; OkVolArg wasn't executed, so we have to do these ourselves.
|
||
|
||
invoke DisAppend ; disable APPEND
|
||
|
||
mov dx,offset TRANGROUP:DirBuf
|
||
mov ah,Set_DMA
|
||
int 21h ; set DTA
|
||
|
||
mov di,FCB ; ES:DI = ptr to FCB
|
||
inc di ; ES:DI = ptr to filename field of FCB
|
||
mov al,'?' ; AL = wildcard character
|
||
mov cx,11
|
||
rep stosb ; wildcard filename field
|
||
|
||
catCrunch:
|
||
call CrunchPath ; crunch pathname to get directory and filename
|
||
jc catRecErr ; handle recorded or extended error
|
||
|
||
; User's directory has been saved, we've changed to specified directory.
|
||
; ComSw = error bits for later use
|
||
; FCB contains parsed filename
|
||
|
||
cmp ComSw,0
|
||
jne catRecErr ; handle recorded error
|
||
|
||
call InstallCtrlC ; install control-C handler
|
||
|
||
call ZeroTotals ; zero grand totals
|
||
call ListDir ; list main directory
|
||
jc catExtErr
|
||
|
||
test Bits,mask subd
|
||
jz @F ; subdirectories option not set
|
||
call ListSubds ; list subdirectories
|
||
jc catExtErr
|
||
@@:
|
||
; Check if any files were found.
|
||
|
||
test Bits,mask bare
|
||
jnz catRet ; don't bother for bare format
|
||
|
||
mov ax,word ptr FileCntTotal
|
||
or ax,ax
|
||
jz catNoFiles ; no files found
|
||
|
||
call DisplayTotals ; display trailing grand totals
|
||
jmp short catRet ; all done
|
||
|
||
catRecErr:
|
||
|
||
; ComSw may have error bit set. If not, do extended error.
|
||
|
||
test ComSw,mask dev
|
||
jnz catNoFiles ; filename is device, respond 'file not found'
|
||
|
||
test ComSw,mask baddir
|
||
jz catExtErr ; no ComSw error bits, must be extended error
|
||
mov dx,offset TRANGROUP:BadCd_Ptr ; invalid directory
|
||
jmp short catErr
|
||
|
||
catNoFiles:
|
||
|
||
; Display header and force 'file not found' message.
|
||
|
||
call DisplayHeader
|
||
mov ax,ERROR_FILE_NOT_FOUND
|
||
mov Msg_Disp_Class,EXT_MSG_CLASS
|
||
mov dx,offset TRANGROUP:Extend_Buf_ptr
|
||
mov Extend_Buf_ptr,ax
|
||
jmp short catErr
|
||
|
||
catExtErr:
|
||
|
||
; DOS has returned an error status. Get the extended error#, and
|
||
; set up an error message, changing 'No more files' error
|
||
; to 'File not found' error.
|
||
|
||
invoke Set_Ext_Error_Msg
|
||
cmp Extend_Buf_Ptr,ERROR_NO_MORE_FILES
|
||
jne @F
|
||
mov Extend_Buf_Ptr,ERROR_FILE_NOT_FOUND
|
||
@@:
|
||
|
||
; Error exit. Error message information has been set up
|
||
; for Std_EPrintf.
|
||
|
||
catErr: jmp CError ; go to COMMAND error recycle point
|
||
|
||
catRet: ret
|
||
|
||
Catalog endp
|
||
|
||
|
||
|
||
|
||
;*** SetDefaults - set default pathname, options
|
||
;
|
||
; ENTRY DS = TRANGROUP seg addr
|
||
;
|
||
; EXIT nothing
|
||
;
|
||
; USED AX,DI
|
||
;
|
||
; EFFECTS
|
||
; SrcBuf = '*',EOL - default pathname
|
||
; PathPos = ptr to pathname
|
||
; PathCnt = length of pathname
|
||
|
||
|
||
|
||
SetDefaults proc
|
||
|
||
mov di,offset TRANGROUP:SrcBuf ; DI = ptr to pathname buffer
|
||
mov PathPos,di ; PathPos = ptr to pathname
|
||
mov al,STAR
|
||
stosb
|
||
mov al,END_OF_LINE_IN
|
||
stosb ; SrcBuf = '*',0Dh
|
||
mov PathCnt,1 ; PathCnt = pathname length
|
||
|
||
xor ax,ax ; AX = 0
|
||
mov ComSw,ax ; = no error
|
||
mov Bits,ax ; = options off
|
||
mov DestBuf,al ; = no sort
|
||
mov AttrSpecified,ATTR_HIDDEN+ATTR_SYSTEM
|
||
mov AttrSelect,al ; exclude hidden, system files
|
||
|
||
ret
|
||
|
||
SetDefaults endp
|
||
|
||
|
||
|
||
|
||
;*** ParseEnvironment - find and parse our environment variable
|
||
;
|
||
; Find our environment variable and parse it. If a parse
|
||
; error occurs, issue an error message. The parse results
|
||
; up to the error will still have effect. Always leave
|
||
; the option variables in a useable state.
|
||
;
|
||
; ENTRY DS = TRANGROUP seg addr
|
||
;
|
||
; EXIT nothing
|
||
;
|
||
; USED AX,BX,CX,DX,SI,DI
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; Bits may contain new option settings.
|
||
; DestBuf may contain new series of sort codes.
|
||
; AttrSpecified, AttrSelect may contain new attribute conditions.
|
||
; SrcBuf may contain a new default pathname/filespec.
|
||
; PathPos, PathCnt updated for new pathname.
|
||
;
|
||
; If a parse error occurred, an error message will be issued.
|
||
|
||
ParseEnvironment proc
|
||
|
||
call GetEnvValue ; get environment variable value
|
||
jc peRet ; name not found in environment
|
||
|
||
; SI = ptr to value of environment variable, in TRANGROUP seg
|
||
|
||
call ParseLine ; parse environment value
|
||
cmp ax,END_OF_LINE
|
||
je peRet ; successful completion
|
||
|
||
; Some kind of parse error occurred.
|
||
; We're set up for a Std_EPrintf call.
|
||
|
||
invoke Std_EPrintf ; display the parse error
|
||
mov Msg_Disp_Class,UTIL_MSG_CLASS ; restore default msg class
|
||
|
||
mov dx,offset TRANGROUP:ErrParsEnv_Ptr
|
||
invoke Printf_Crlf ; "(Error occurred in environment.."
|
||
|
||
;M008;Internal handling of /? removed
|
||
;peOk: and Bits,not mask help ; disallow /h in environment variable
|
||
|
||
peRet: ret
|
||
|
||
ParseEnvironment endp
|
||
|
||
|
||
|
||
|
||
;*** ParseCmdLine - parse and record command line parameters
|
||
;
|
||
; ENTRY PSP offset 81h is beginning of cmd line buffer
|
||
; DS, ES, CS = TRANGROUP seg addr
|
||
;
|
||
; EXIT CY = set if parse error occurred
|
||
;
|
||
; If parse error occurred, we're set up for Std_EPrintf call:
|
||
; AX = system parser error code
|
||
; DX = ptr to message block
|
||
;
|
||
; USED AX,BX,CX,DX,SI,DI
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; Bits may contain new option settings.
|
||
; DestBuf may contain new series of sort codes.
|
||
; AttrSpecified, AttrSelect may contain new attribute conditions.
|
||
; SrcBuf may contain a new default pathname/filespec.
|
||
; PathPos, PathCnt updated for new pathname.
|
||
;
|
||
; If parse error occurred, we're set up for Std_EPrintf call:
|
||
; Msg_Disp_Class = parse error class
|
||
; Byte after last parameter in text is zeroed to make ASCIIZ string
|
||
; Message block (see DX) is set up for parse error message
|
||
|
||
ParseCmdLine proc
|
||
|
||
mov si,81h ; SI = ptr to cmd-line tail text
|
||
call ParseLine ; parse cmd line tail
|
||
cmp AX,END_OF_LINE
|
||
je pcOk ; parse completed successfully
|
||
|
||
; A parse error occurred. We're all set up for message output.
|
||
|
||
stc ; return failure
|
||
jmp short pcRet
|
||
|
||
pcOk: clc ; return success
|
||
|
||
pcRet: ret
|
||
|
||
ParseCmdLine endp
|
||
|
||
|
||
|
||
|
||
;*** SetCollatingTable - set up character collating table for sorting
|
||
;
|
||
; If country is other than USA, try to get a collating table
|
||
; for character sorting. For USA, use straight byte values.
|
||
; This is so DIR behaves like the MS-DOS Shell, which sorts
|
||
; by straight byte values in the USA for better performance.
|
||
;
|
||
; ENTRY ES = TRANGROUP seg addr
|
||
;
|
||
; EXIT nothing
|
||
;
|
||
; USED AX,BX,CX,DX,DI
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; If collating table is set -
|
||
; CountryPtrId = 6.
|
||
; CountryPtr points to collating table.
|
||
;
|
||
; Otherwise -
|
||
; CountryPtrId = 0.
|
||
|
||
SetCollatingTable proc
|
||
|
||
; Begin modification M028
|
||
|
||
mov dx,offset TRANGROUP:InternatVars
|
||
; DS:DX = ptr to international info buffer
|
||
mov ax,INTERNATIONAL shl 8
|
||
; AX = 'Get current country info'
|
||
int 21h ; call DOS
|
||
jc scNoTable ; error - so don't collate
|
||
|
||
; BX = country code
|
||
|
||
cmp bx,1
|
||
je scNoTable ; we're in USA, don't collate
|
||
|
||
; End modification M028
|
||
|
||
;* Country code is other than USA. Try to get a collating table.
|
||
|
||
mov ax,(GETEXTCNTRY shl 8) + SETCOLLATE
|
||
; AH = 'Get Extended Country Info'
|
||
; AL = 'Get Pointer to Collating Table'
|
||
mov bx,-1 ; BX = code page of interest = CON
|
||
mov cx,5 ; CX = length of info buffer
|
||
mov dx,bx ; DX = country ID = default
|
||
mov di,offset TRANGROUP:CountryPtrInfo
|
||
; ES:DI = ptr to info buffer
|
||
int 21h ; call DOS
|
||
jnc scRet ; success
|
||
|
||
;* Set CountryPtrId = 0 to signal no collating table.
|
||
|
||
scNoTable: ;M028
|
||
mov CountryPtrId,0
|
||
|
||
scRet: ret
|
||
|
||
SetCollatingTable endp
|
||
|
||
|
||
|
||
|
||
;*** SetOptions - check and set options
|
||
;
|
||
; ENTRY nothing
|
||
;
|
||
; EXIT nothing
|
||
;
|
||
; USED AX,BX,CX,DX
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; Bits may contain modified option settings.
|
||
; Display_Ioctl table, including LinPerPag variable, is filled in.
|
||
; LeftOnPage is initialized to # lines till end of page is handled.
|
||
; PerLine is set according to /w presence.
|
||
|
||
SetOptions proc
|
||
|
||
; If bare listing requested, cancel wide listings.
|
||
|
||
test Bits,mask bare
|
||
jz @F
|
||
and Bits,not mask wide ;M007;Allow /p with /b
|
||
|
||
@@:
|
||
|
||
; Set # lines per display page.
|
||
|
||
;M01 Obtain screen height from ROM BIOS data area
|
||
;
|
||
;M01 mov LinPerPag,LINESPERPAGE ; default value
|
||
|
||
ifndef JAPAN
|
||
push ds
|
||
MOV AX,ROMBIOS_DATA ; Get ROM Data segment
|
||
MOV DS,AX ;
|
||
Assume DS:ROMBIOS_DATA
|
||
|
||
MOV al,CRT_Rows ; Get max rows
|
||
pop ds ;
|
||
Assume DS:Trangroup
|
||
|
||
or al,al ; If zero specified
|
||
jnz @F ;
|
||
endif
|
||
MOV al,LINESPERPAGE ; assume 24 rows
|
||
|
||
@@:
|
||
xor ah,ah
|
||
ifndef JAPAN
|
||
inc al ; height + 1 ;M018
|
||
endif
|
||
mov LinPerPag,ax ; set the rows now
|
||
|
||
; Now the console driver can change the rows if it knows better (M01 end)
|
||
|
||
mov ax,(IOCTL shl 8)+GENERIC_IOCTL_HANDLE ; IOCTL for handles
|
||
mov bx,STDOUT ; handle #
|
||
mov ch,IOC_SC ; screen
|
||
mov cl,GET_GENERIC ; get display info
|
||
mov dx,offset TRANGROUP:Display_Ioctl ; info block
|
||
int 21h ; call DOS
|
||
|
||
mov ax,LinPerPag ; AX = # lines per page
|
||
mov LeftOnPage,ax ; initialize # lines left on page
|
||
|
||
; Set # entries per line.
|
||
|
||
mov PerLine,NORMPERLIN ; # entries per line without /w
|
||
test Bits,mask wide
|
||
jz @F
|
||
mov PerLine,WIDEPERLIN ; # entries per line with /w
|
||
@@:
|
||
;M011;start;The following code checks if a drive
|
||
;letter has been parsed into SrcBuf, and if
|
||
;so, the correct drive number is loaded into
|
||
;the first FCB, at offset 5C.
|
||
|
||
cmp TRANGROUP:[SrcBuf+1],COLON_CHAR ; is this a drive letter?
|
||
jne soRet
|
||
mov al,TRANGROUP:[SrcBuf] ; load drive letter into al
|
||
and al,not 20h ; capitalize ASCII drive letter (LowerCase-32)-->UpperCase
|
||
sub al,'@' ; convert to 1-based number (1=A)
|
||
mov ds:FCB,al ; store in first FCB
|
||
;M011;end
|
||
soRet: ret
|
||
|
||
SetOptions endp
|
||
|
||
|
||
|
||
|
||
;*** CrunchPath - analyze supplied or default pathname
|
||
;
|
||
; ENTRY PathPos = ptr to pathname buffer
|
||
; PathCnt = length of pathname, not incl trailing delimiter
|
||
; Pathname in buffer must end in delimiter (like CR) and
|
||
; must have space for another char after the delimiter.
|
||
;
|
||
; EXIT CY = clear if no error
|
||
; We are changed to directory found in pathname
|
||
; Previous directory ready to be restored via RestUDir
|
||
; FCB filename fields contain filename (possibly w/ wildcards)
|
||
;
|
||
; If error occurred,
|
||
; CY = set
|
||
; ComSw = error bits (see ErrorRec)
|
||
; If ComSw not set,
|
||
; Ready for DOS Get Extended Error call
|
||
|
||
|
||
CrunchPath proc
|
||
|
||
call FileIsDevice
|
||
jne @F ; not a device, skip ahead
|
||
or ComSw,mask dev ; signal file is device
|
||
jmp short cpErr ; return error
|
||
@@:
|
||
push PathPos ; save ptr to pathname
|
||
mov DirFlag,-1 ; tell PathCrunch not to parse file into FCB
|
||
invoke PathCrunch ; change to directory in pathname
|
||
mov DirFlag,0 ; reset our little flag
|
||
pop si ; SI = ptr to pathname
|
||
jc cpNoDir ; didn't find directory path
|
||
jz cpRet ; found directory path w/ no filename
|
||
; - leave wildcard default in FCB and return
|
||
|
||
;* We found a directory, and there was a filename attached.
|
||
; DestTail = ptr to ASCIIZ filename
|
||
|
||
mov si,DestTail ; SI = ptr to filename
|
||
jmp short cpFile ; go parse the file into FCB
|
||
|
||
;* PathCrunch failed to find a directory in the pathname.
|
||
;
|
||
; Msg_Numb = error code
|
||
; DestIsDir = nonzero if path delimiter char's occur in pathname
|
||
; SI = ptr to pathname (now an ASCIIZ string)
|
||
|
||
cpNoDir:
|
||
mov ax,Msg_Numb ; AX = error code from PathCrunch
|
||
or ax,ax
|
||
jnz cpErr ; error occurred - return it
|
||
cmp DestIsDir,0
|
||
je cpMaybe ; no path delimiters seen, maybe it's a file
|
||
or ComSw,mask baddir ; signal invalid directory name
|
||
jmp short cpErr ; return error
|
||
|
||
cpMaybe:
|
||
|
||
; SI = ptr to pathname
|
||
|
||
cmp byte ptr [si+1],COLON_CHAR
|
||
jnz @F ; no drive specifier, skip ahead
|
||
lodsw ; SI = ptr past drive specifier "d:"
|
||
@@: cmp [si],".."
|
||
jne cpFile ; if not "..", treat as a file
|
||
cmp byte ptr [si+2],0
|
||
jne cpFile ; or if there's more after "..", treat as file
|
||
or ComSw,mask baddir ; signal invalid directory
|
||
jmp short cpErr ; return error
|
||
|
||
; The preceding code was taken from the old DIR routine.
|
||
; It's garbage, I'm afraid. It's meant to check for ".."
|
||
; occurring when we're at the root directory. Too bad it
|
||
; doesn't handle problems with "..\..", etc.
|
||
|
||
|
||
; We're ready to parse a filename into the FCB.
|
||
; SI = ptr to ASCIIZ filename
|
||
|
||
cpFile: mov di,FCB ; DI = ptr to FCB
|
||
mov ax,(PARSE_FILE_DESCRIPTOR shl 8) or 0Eh
|
||
; wildcards already in FCB used as defaults
|
||
int 21h
|
||
clc ; return success
|
||
jmp short cpRet
|
||
|
||
cpErr: stc ; return error
|
||
|
||
cpRet: ret
|
||
|
||
CrunchPath endp
|
||
|
||
|
||
|
||
|
||
;*** InstallCtrlC - install our private control-C handler
|
||
;
|
||
; Put our control-c handler in front of command.com's default
|
||
; handler, to make sure the user's default directory gets restored.
|
||
; This shouldn't be necessary, but, for now, there are situations
|
||
; where the TDATA segment is left in a modified state when a
|
||
; control-c occurs. This means that the transient will be
|
||
; reloaded, and the user's directory cannot be restored.
|
||
;
|
||
; Bugbug: fix the wider problem? Involves message services. Ugly.
|
||
;
|
||
; ENTRY nothing
|
||
;
|
||
; EXIT nothing
|
||
;
|
||
; USED AX,BX,DX
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; CtrlCHandler address placed in int 23 vector.
|
||
;
|
||
; NOTE
|
||
;
|
||
; Command.com's basic control-c handler will be restored
|
||
; to the int 23 vector by the HeadFix routine, after DIR finishes.
|
||
|
||
InstallCtrlC proc
|
||
|
||
push es ; preserve ES
|
||
mov ax,(GET_INTERRUPT_VECTOR shl 8) + 23h
|
||
int 21h
|
||
mov word ptr OldCtrlCHandler,bx ; save old int 23 vector
|
||
mov word ptr OldCtrlCHandler+2,es
|
||
pop es ; restore ES
|
||
|
||
mov dx,offset TRANGROUP:CtrlCHandler ; DS:DX = ptr to CtrlCHandler
|
||
mov ax,(SET_INTERRUPT_VECTOR shl 8) + 23h
|
||
int 21h
|
||
ret
|
||
|
||
InstallCtrlC endp
|
||
|
||
|
||
|
||
|
||
;*** ListSubds - search and list files in subdirectories
|
||
;
|
||
; ENTRY Current directory (on selected drive) is top of subdir tree
|
||
; FCB is still set up for file searches
|
||
; Bits, AttrSpecified, AttrSelect, DestBuf all still set up
|
||
;
|
||
; EXIT CY = clear if no error
|
||
; FileCnt = # files found & displayed
|
||
; FileSiz = total size of files found
|
||
;
|
||
; If error,
|
||
; CY = set
|
||
; Ready for DOS Get Extended Error call
|
||
;
|
||
; USED AX,BX,CX,DX,SI,DI,BP
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; FileCntTotal, FileSizTotal are updated.
|
||
; Subdirectories may be listed on standard output device.
|
||
;
|
||
; NOTES
|
||
;
|
||
; ListSubds seeds the recursive entry point lsNode with a ptr
|
||
; to a buffer where we'll stack up subdirectory filenames.
|
||
; Each name is stored ASCIIZ.
|
||
|
||
ListSubds proc
|
||
|
||
invoke SetRest1 ; make sure user's dir gets restored
|
||
|
||
mov bx,offset TRANGROUP:ScanBuf ; BX = ptr to child name buffer
|
||
|
||
lsNode:
|
||
mov byte ptr ds:[bx],0 ; start with null child name
|
||
lsLoop:
|
||
call FindNextChild ; search for next subdirectory
|
||
jc lsErr ; search failed - examine error
|
||
|
||
mov dx,bx ; DX = ptr to child's name
|
||
call ChangeDir ; enter child directory
|
||
|
||
; M023;start
|
||
jnc @F ; check for error
|
||
cmp ax,ERROR_PATH_NOT_FOUND ; error due to len(pathname)>MAXPATH?
|
||
je lsLoop ; yes, skip over this subdirectory
|
||
jmp SHORT lsRet ; no, other error: DIR must fail
|
||
; M023;end
|
||
|
||
@@: push bx
|
||
call ListDir ; list the directory
|
||
pop bx
|
||
|
||
; Note we're ignoring errors returned here.
|
||
|
||
mov di,bx ; DI = ptr to child's name
|
||
mov cx,13 ; CX = max name length w/ null
|
||
xor al,al ; AL = zero byte to look for
|
||
repne scasb ; DI = ptr to next name pos'n in buf
|
||
push bx ; save ptr to child's name
|
||
mov bx,di ; BX = ptr to next name pos'n in buf
|
||
call lsNode ; recurse from new node
|
||
pop bx ; BX = ptr to child's name
|
||
pushf ; save error condition
|
||
|
||
shove 0
|
||
shove ".."
|
||
mov dx,sp ; DX = ptr to "..",0 on stack
|
||
call ChangeDir ; return to parent directory
|
||
pop ax ; restore stack
|
||
pop ax
|
||
|
||
popf ; restore error condition from child
|
||
jc lsRet ; return error
|
||
|
||
jmp lsLoop ; look for more children
|
||
lsErr:
|
||
invoke Get_Ext_Error_Number ; AX = extended error code
|
||
cmp ax,ERROR_FILE_NOT_FOUND
|
||
je lsRet ; file not found, we're ok
|
||
cmp ax,ERROR_NO_MORE_FILES
|
||
je lsRet ; no more files, we're ok
|
||
stc ; return other errors
|
||
|
||
lsRet: ret
|
||
|
||
ListSubds endp
|
||
|
||
|
||
|
||
|
||
break <DIR support routines>
|
||
|
||
;*** SUPPORT ROUTINES
|
||
|
||
|
||
|
||
|
||
;*** CheckChild - check potential subdirectory name for FindNextChild
|
||
;
|
||
; ENTRY DirBuf contains DOS Find-buffer with potential child
|
||
; BX = ptr to last child's name
|
||
; BP = ptr to temp child's name
|
||
;
|
||
; EXIT nothing
|
||
;
|
||
; USED AX,CX,SI,DI
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; Filename pointed to by BP may be changed.
|
||
;
|
||
; NOTES
|
||
;
|
||
; Potential filename replaces temp filename if:
|
||
; it's a subdirectory file;
|
||
; it doesn't start with a '.';
|
||
; it's alphanumerically greater than last child's name;
|
||
; and it's alphanumerically less than temp name.
|
||
|
||
CheckChild proc
|
||
|
||
test DirBuf.find_buf_attr,ATTR_DIRECTORY
|
||
jz ccRet ; not a subdirectory file- return
|
||
|
||
cmp DirBuf.find_buf_pname,'.'
|
||
je ccRet ; starts with a dot- return
|
||
|
||
mov si,offset TRANGROUP:DirBuf+find_buf_pname
|
||
mov di,bx
|
||
call CmpAscz ; compare candidate to last child's name
|
||
jna ccRet ; it's not above it- return
|
||
|
||
mov si,offset TRANGROUP:DirBuf+find_buf_pname
|
||
mov di,bp
|
||
call CmpAscz ; compare candidate to temp name
|
||
jnb ccRet ; it's not below it- return
|
||
|
||
; New kid is alright. Copy to temp.
|
||
|
||
mov si,offset TRANGROUP:DirBuf+find_buf_pname
|
||
mov di,bp
|
||
mov cx,13
|
||
rep movsb
|
||
|
||
ccRet: ret
|
||
|
||
CheckChild endp
|
||
|
||
|
||
|
||
|
||
;*** CmpEntry - compare one directory entry to another in sort order
|
||
;
|
||
; Compare one directory entry against another according to
|
||
; the sort codes in DestBuf. One or more comparisons
|
||
; may be made of file name, extension, time/date, and
|
||
; size. Comparisons may be made for upward or downward
|
||
; sort order.
|
||
;
|
||
; ENTRY ES:BX = ptr to entry to compare
|
||
; ES:BP = ptr to entry to be compared against
|
||
; DestBuf contains sort codes (see DestBuf)
|
||
; DS = TRANGROUP seg addr
|
||
;
|
||
; EXIT BX = unchanged
|
||
; BP = unchanged
|
||
; Condition flags set for same, above, or below
|
||
; comparing BX entry against BP entry.
|
||
; 'Same, above, below' translate to 'same, after, before'.
|
||
;
|
||
; USED: AX,CX,DX,SI,DI
|
||
|
||
CmpEntry proc
|
||
|
||
mov si,offset TRANGROUP:DestBuf ; (DS:SI) = ptr to sort codes
|
||
ceLoop:
|
||
xor ax,ax ; AX = 0
|
||
mov al,[si] ; AL = sort code
|
||
or al,al
|
||
jz ceDone ; sort code is zero, we're done
|
||
inc si ; DS:SI = ptr to next sort code
|
||
push si ; save ptr to next sort code
|
||
dec al
|
||
sal al,1 ; AX = index into cmp call table
|
||
; CY set for downward sort order
|
||
mov si,ax ; SI = index into cmp call table
|
||
mov ax,cs:FieldCmps[si] ; AX = addr of compare routine
|
||
jc ceDn ; downwards sort - go swap entries
|
||
call ax ; do upwards sort
|
||
jmp short @F
|
||
ceDn:
|
||
xchg bx,bp ; swap entry ptrs for downward sort order
|
||
call ax ; do sort
|
||
xchg bx,bp ; swap ptrs back
|
||
@@:
|
||
pop si ; SI = ptr to next sort code
|
||
je ceLoop ; compare showed no difference, keep trying
|
||
|
||
ceDone:
|
||
|
||
; Get here either from unequal compare or sort code = 0.
|
||
; In the latter case, condition codes indicate equality,
|
||
; which is correct.
|
||
|
||
ret
|
||
|
||
FieldCmps label word ; call table of entry comparisons
|
||
dw CmpName
|
||
dw CmpExt
|
||
dw CmpTime
|
||
dw CmpSize
|
||
dw CmpType
|
||
|
||
CmpEntry endp
|
||
|
||
|
||
|
||
|
||
;*** CmpName - compare file name of two entries
|
||
;*** CmpExt - compare extension of two entries
|
||
;
|
||
; ENTRY ES:BX = ptr to one entry
|
||
; ES:BP = ptr to another entry
|
||
;
|
||
; EXIT BX = unchanged
|
||
; BP = unchanged
|
||
; Condition flags set for same, above, or below
|
||
; comparing BX entry to BP entry.
|
||
;
|
||
; USED: AX,CX,DX,SI,DI
|
||
|
||
CmpName proc
|
||
|
||
mov si,bx ; ES:SI = ptr to BX entry
|
||
mov di,bp ; ES:DI = ptr to BP entry
|
||
add si,filename ; ES:SI = ptr to BX name
|
||
add di,filename ; ES:DI = ptr to BP name
|
||
mov cx,size filename; CX = length of name
|
||
jmp short CmpStr
|
||
|
||
CmpExt: mov si,bx ; ES:SI = ptr to BX entry
|
||
mov di,bp ; ES:DI = ptr to BP entry
|
||
add si,fileext ; ES:SI = ptr to BX extension
|
||
add di,fileext ; ES:DI = ptr to BP extension
|
||
mov cx,size fileext ; CX = length of extension field
|
||
|
||
; Bugbug: use symbol for subfunction code.
|
||
|
||
CmpStr: cmp CountryPtrId,6
|
||
jne cnNoCollTable ; no collating table available
|
||
|
||
;* Compare strings using collating table.
|
||
;
|
||
; ES:SI = ptr to 1st string
|
||
; ES:DI = ptr to 2nd string
|
||
; CX = length
|
||
|
||
push bp ; preserve BP
|
||
push bx ; preserve BX
|
||
push ds ; preserve DS
|
||
lds bx,CountryPtr ; DS:BX = ptr to collating table
|
||
assume ds:NOTHING
|
||
mov bp,ds:[bx] ; BP = size of collating table
|
||
inc bx
|
||
inc bx ; DS:BX = ptr to collating values
|
||
; DS:[BX]-2 = size of table
|
||
xor ax,ax ; AX = 0 for starters
|
||
|
||
; Bugbug: Investigate removing collating table length checks.
|
||
|
||
cnNextChar:
|
||
mov al,es:[di] ; AL = AX = char from 2nd string
|
||
inc di ; ES:DI = ptr to next char 2nd string
|
||
cmp ax,bp ; compare to collating table length
|
||
jae @F ; char not in table
|
||
xlat
|
||
@@: ; AL = AX = collating value
|
||
mov dx,ax ; DX = collating value from 2nd string
|
||
lods byte ptr es:[si] ; AL = AX = char from 1st string
|
||
; ES:SI = ptr to next char 1st string
|
||
cmp ax,bp ; compare to collating table length
|
||
jae @F ; char not in table
|
||
xlat
|
||
@@: ; AL = AX = collating value
|
||
cmp ax,dx ; compare collating values
|
||
|
||
ifdef DBCS ; DBCS tail byte must not use
|
||
; collating table
|
||
jnz cnNot_Same
|
||
mov al,es:[di-1] ; get previous 2nd string character
|
||
invoke testkanj
|
||
jz cnDo_Next ; if it was not DBCS lead byte
|
||
mov al,es:[di] ; get tail byte from 2nd string
|
||
cmp es:[si],al ; compare with 1st strings tail byte
|
||
jnz cnNot_Same
|
||
inc si ; pass tail byte
|
||
inc di
|
||
dec cx
|
||
cnDo_Next:
|
||
loop cnNextChar
|
||
cnNot_Same:
|
||
|
||
else ; Not DBCS
|
||
|
||
loope cnNextChar ; until unequal or no more left
|
||
endif
|
||
|
||
pop ds ; restore DS
|
||
assume ds:TRANGROUP
|
||
pop bx ; restore BX
|
||
pop bp ; restore BP
|
||
ret
|
||
|
||
;* If no collating table is available, simply compare raw ASCII values.
|
||
; Don't we wish we could just do this all the time? Sigh.
|
||
|
||
cnNoCollTable:
|
||
rep cmps byte ptr es:[si],[di]
|
||
ret
|
||
|
||
CmpName endp
|
||
|
||
|
||
|
||
|
||
;*** CmpTime - compare entries by date/time
|
||
;
|
||
; ENTRY ES:BX = ptr to one entry
|
||
; ES:BP = ptr to another entry
|
||
;
|
||
; EXIT BX = unchanged
|
||
; BP = unchanged
|
||
; Condition flags set for same, above, or below
|
||
; comparing BX entry to BP entry.
|
||
;
|
||
; USED: CX,SI,DI
|
||
;
|
||
; NOTE Filetime and filedate fields in our private entry
|
||
; structure must be adjacent and in that order.
|
||
|
||
CmpTime proc
|
||
|
||
mov si,bx
|
||
mov di,bp
|
||
add si,filedate + size filedate - 1
|
||
add di,filedate + size filedate - 1
|
||
mov cx,size filetime + size filedate
|
||
std
|
||
repe cmps byte ptr es:[si],[di]
|
||
cld
|
||
ret
|
||
|
||
CmpTime endp
|
||
|
||
|
||
|
||
|
||
;*** CmpSize - compare entries by size
|
||
;
|
||
; ENTRY ES:BX = ptr to one entry
|
||
; ES:BP = ptr to another entry
|
||
;
|
||
; EXIT BX = unchanged
|
||
; BP = unchanged
|
||
; Condition flags set for same, above, or below
|
||
; comparing BX entry to BP entry.
|
||
;
|
||
; USED: CX,SI,DI
|
||
|
||
CmpSize proc
|
||
|
||
mov si,bx
|
||
mov di,bp
|
||
add si,filesize + size filesize - 1
|
||
add di,filesize + size filesize - 1
|
||
mov cx,size filesize
|
||
std
|
||
repe cmps byte ptr es:[si],[di]
|
||
cld
|
||
ret
|
||
|
||
CmpSize endp
|
||
|
||
|
||
|
||
|
||
;*** CmpType - compare entries by file type (subdirectory or not)
|
||
;
|
||
; ENTRY ES:BX = ptr to one entry
|
||
; ES:BP = ptr to another entry
|
||
;
|
||
; EXIT BX = unchanged
|
||
; BP = unchanged
|
||
; Condition flags set for same, above, or below
|
||
; comparing BX entry to BP entry.
|
||
;
|
||
; USED: AX
|
||
|
||
CmpType proc
|
||
|
||
mov al,es:[bx].fileattr
|
||
mov ah,es:[bp].fileattr
|
||
and ax,(ATTR_DIRECTORY shl 8) + ATTR_DIRECTORY
|
||
cmp ah,al
|
||
ret
|
||
|
||
CmpType endp
|
||
|
||
|
||
|
||
|
||
|
||
;*** DefaultAttr - set default attribute conditions
|
||
;
|
||
; ENTRY nothing
|
||
;
|
||
; EXIT CY clear
|
||
;
|
||
; USED
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; AttrSpecified, AttrSelect are updated with new attribute conditions.
|
||
|
||
DefaultAttr proc
|
||
|
||
mov AttrSpecified,ATTR_HIDDEN+ATTR_SYSTEM ; specify H and S
|
||
mov AttrSelect,0 ; H and S must be off
|
||
clc ; return success
|
||
ret
|
||
|
||
DefaultAttr endp
|
||
|
||
|
||
|
||
|
||
;*** DisplayTotals - display grand total stats
|
||
;
|
||
; If we searched subdirectories, display the total # files found
|
||
; and total size of files found.
|
||
; Display disk space remaining.
|
||
;
|
||
; ENTRY FileCntTotal, FileSizTotal contain correct values
|
||
; Bits contains setting of /s
|
||
; FCB contains drive #
|
||
;
|
||
; EXIT nothing
|
||
;
|
||
; USES AX,DX
|
||
; FileSiz
|
||
|
||
DisplayTotals proc
|
||
|
||
test Bits,mask subd
|
||
jz dtFree ; no subdirectories- do bytes free
|
||
|
||
invoke Crlf2 ; start on new line
|
||
call UseLine
|
||
|
||
mov dx,offset TRANGROUP:Total_Ptr
|
||
invoke Std_Printf ; "Total:",cr,lf
|
||
call UseLine
|
||
|
||
mov ax,word ptr FileCntTotal ; AX = # files found mod 64K
|
||
mov si,offset TRANGROUP:FileSizTotal
|
||
mov di,offset TRANGROUP:FileSiz
|
||
movsw
|
||
movsw ; move total size to size variable
|
||
call DisplayCntSiz ; display file count & size
|
||
dtFree:
|
||
mov ah,GET_DRIVE_FREESPACE ; AH = DOS Get Free Space function
|
||
mov dl,byte ptr ds:FCB ; DL = drive#
|
||
int 21h ; call DOS
|
||
cmp ax,-1 ; check 'invalid drive' return code
|
||
jz dtRet ; can't get drive space - return
|
||
mul cx
|
||
mul bx
|
||
mov Bytes_Free,ax
|
||
mov Bytes_Free+2,dx
|
||
mov dx,offset TRANGROUP:BytMes_Ptr
|
||
invoke Std_Printf ; "nnn bytes free",cr,lf
|
||
call UseLine
|
||
|
||
dtRet: ret
|
||
|
||
DisplayTotals endp
|
||
|
||
|
||
|
||
|
||
;*** FileIsDevice - see if file looks like a device
|
||
;
|
||
; ENTRY PathPos = ptr to pathname
|
||
; PathCnt = length of pathname w/o terminating char
|
||
; DirBuf is DOS DTA
|
||
;
|
||
; EXIT ZR = set if file looks like a device
|
||
;
|
||
; USED AX,BX,CX,DX,DI
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; DTA buffer holds results of Find First function
|
||
;
|
||
; NOTES
|
||
;
|
||
; We try to flag devices in two ways. First, we try
|
||
; the DOS Find First function. It returns attribute bit 6
|
||
; set on a successful find if it identifies a device name.
|
||
; Unfortunately, it returns 'path not found' for a device
|
||
; name terminated with colon, such as "CON:". So, we look
|
||
; for any colon in the pathname after the 2nd character,
|
||
; and flag the pathname as a device if we find one.
|
||
|
||
FileIsDevice proc
|
||
|
||
mov dx,PathPos ; DX = ptr to pathname
|
||
|
||
mov di,dx
|
||
add di,PathCnt ; DI = ptr to byte after pathname
|
||
xor bl,bl ; BL = NUL to terminate pathname with
|
||
xchg bl,byte ptr [di] ; BL = saved pathname terminating char
|
||
|
||
xor cx,cx ; CX = attribute mask (normal search)
|
||
mov ah,FIND_FIRST ; AH = DOS Find First function code
|
||
int 21h ; call DOS
|
||
xchg bl,byte ptr [di] ; restore pathname terminating char
|
||
jc piCol ; didn't find a dir entry, check for colon
|
||
|
||
; Found a dir entry, see if Find First thinks it's a device.
|
||
|
||
test byte ptr DirBuf.Find_Buf_Attr,ATTR_DEVICE
|
||
jz piCol ; device attribute not set, look for colon
|
||
xor cx,cx ; it's a device, return ZR flag
|
||
jmp short piRet
|
||
|
||
; Device attribute not returned by Find First function. But
|
||
; let's check for a colon anywhere in the pathname after the
|
||
; second byte.
|
||
;
|
||
; DI = ptr to byte after pathname
|
||
|
||
piCol: dec di ; DI = ptr to last char in pathname
|
||
mov al,COLON_CHAR ; AL = colon char to search for
|
||
mov cx,PathCnt ; CX = # chars to scan
|
||
dec cx
|
||
dec cx ; ignore 1st two chars of pathname
|
||
or cx,cx
|
||
js piRet ; if < 2 chars in pathname, just return
|
||
or di,di ; clear ZR in case CX = 0
|
||
std ; scan downward
|
||
repne scasb
|
||
cld ; restore default upward direction
|
||
|
||
; After scanning, the ZR flag is set to indicate presence of a colon.
|
||
|
||
piRet: ret
|
||
|
||
FileIsDevice endp
|
||
|
||
|
||
|
||
|
||
;*** FindFirst - find first directory entry to display
|
||
;*** FindNext - find next directory entry to display
|
||
;
|
||
; ENTRY Bits<inmem> = set if entries are loaded in TPA
|
||
; AttrSpecified, AttrSelect are set
|
||
;
|
||
; EXIT CY = clear if successful
|
||
; BX = offset in TPA buffer of directory entry found
|
||
;
|
||
; If unsuccessful,
|
||
; CY = set
|
||
; AX = DOS error code
|
||
; DOS Get Extended Error call will get error code
|
||
;
|
||
; NOTE: if entries were loaded into TPA, AX contains
|
||
; ERROR_NO_MORE_FILES when no more entries are available,
|
||
; but DOS Get Extended Error call WON'T return the correct
|
||
; error. That's ok, because we'll see the value in AX
|
||
; and recognize it as a non-error condition.
|
||
;
|
||
; USED AX,CX,DX,SI,DI
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; Entries in memory may be marked as output.
|
||
; If not sorted, entry is loaded at TPA.
|
||
;
|
||
; NOTES
|
||
;
|
||
; If we don't find a qualifying file, we return after the final
|
||
; DOS Find File call. A DOS Get Extended Error call will then
|
||
; indicate an appropriate condition.
|
||
|
||
FindFirst proc
|
||
|
||
mov ax,offset TRANGROUP:GetFirst
|
||
jmp short ffFindEntry
|
||
|
||
FindNext:
|
||
mov ax,offset TRANGROUP:GetNext
|
||
|
||
; AX = address of correct disk get routine to use.
|
||
|
||
ffFindEntry:
|
||
push es ; save TRANGROUP seg addr
|
||
test Bits,mask inmem
|
||
jz ffDisk ; entries not in memory, search disk
|
||
|
||
; Entries are loaded in memory to sort out. Find the first one.
|
||
; There will always be one, or LoadEntries would've failed.
|
||
|
||
call FindInMem ; find first entry in TPA
|
||
jmp short ffRet ; return what TPA search returns
|
||
|
||
; Get entry from disk.
|
||
|
||
ffDisk:
|
||
call ax ; get entry from disk
|
||
jc ffGetErr ; get & return error
|
||
mov es,Tpa ; ES = seg addr of TPA
|
||
xor di,di ; ES:DI = ptr to TPA
|
||
mov bx,di ; BX = offset of entry in TPA
|
||
call LoadEntry ; load entry to TPA
|
||
clc ; return success
|
||
jmp short ffRet
|
||
|
||
ffGetErr:
|
||
invoke Get_Ext_Error_Number ; AX = DOS error code
|
||
stc
|
||
|
||
ffRet: pop es ; ES = TRANGROUP seg addr again
|
||
ret
|
||
|
||
FindFirst endp
|
||
|
||
|
||
|
||
|
||
;*** FindInMem - find next directory entry in TPA buffer
|
||
;
|
||
; ENTRY TPA is loaded (see LoadEntries)
|
||
;
|
||
; EXIT BX = offset in TPA of entry found
|
||
;
|
||
; If no more files,
|
||
; CY = set
|
||
; AX = DOS 'no more files' error code
|
||
;
|
||
; USED AX,BX,CX,DX,SI,DI,BP,ES
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; Entry found is flagged as 'used' (see EntryStruc).
|
||
|
||
FindInMem proc
|
||
|
||
mov es,Tpa ; ES = TPA seg addr
|
||
xor bx,bx ; ES:BX = ptr to 1st entry in TPA
|
||
cld ; make sure default string direction is up
|
||
|
||
call FindOneInMem ; locate an entry
|
||
jc fiNoMore ; none left, set up 'no more files' error
|
||
|
||
; BX = ptr to entry in TPA
|
||
|
||
fiBest:
|
||
mov bp,bx ; BP = ptr to best entry so far
|
||
fiNext:
|
||
call FindNextInMem ; locate next entry
|
||
jc fiFound ; no more, best entry so far wins
|
||
|
||
; BX = ptr to next entry
|
||
|
||
call CmpEntry ; compare it to best found so far (BP)
|
||
jnb fiNext ; it's not better, go look at next one
|
||
jmp fiBest ; it's better, go mark it as best so far
|
||
|
||
fiNoMore:
|
||
|
||
; No more entries available in TPA. Set up 'no more files' error.
|
||
|
||
mov ax,ERROR_NO_MORE_FILES ; AX = 'no more files' error code
|
||
stc ; return error
|
||
jmp short fiRet
|
||
|
||
fiFound:
|
||
mov bx,bp ; BX = ptr to best entry found
|
||
mov byte ptr es:[bx],1 ; mark entry 'used'
|
||
clc ; return success
|
||
fiRet: ret
|
||
|
||
FindInMem endp
|
||
|
||
|
||
|
||
|
||
;*** FindNextChild - find next subdirectory in current directory
|
||
;
|
||
; ENTRY BX = ptr to last child found, ASCIIZ filename
|
||
; DirBuf is established DTA
|
||
;
|
||
; EXIT BX = ptr (same addr) to next child found, ASCIIZ filename
|
||
;
|
||
; If failure,
|
||
; CY = set
|
||
; DOS Get Extended Error call will get error
|
||
;
|
||
; USED AX,CX,DX,SI,DI,BP
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; DirBuf is used for find first/next calls.
|
||
;
|
||
; NOTES
|
||
;
|
||
; We keep on checking files until DOS returns an error. If
|
||
; the error is 'no more files' and the temp filename is not
|
||
; the initial high tag, copy the temp to the child's name spot
|
||
; and return success. Otherwise, send the error back to caller.
|
||
;
|
||
; This routine depends on DS,ES,CS, & SS all being equal.
|
||
|
||
FindNextChild proc
|
||
|
||
sub sp,12 ; make temp filename buf on stack
|
||
shove 00FFh ; temp filename = high tag
|
||
mov bp,sp ; BP = ptr to temp filename buf
|
||
shove "*"
|
||
shove ".*"
|
||
call GetDriveLtr ; AX = "d:"
|
||
push ax
|
||
mov dx,sp ; DX = ptr to "d:*.*",0 on stack
|
||
|
||
; See that the stack is restored properly at the end of this proc.
|
||
|
||
mov cx,ATTR_DIRECTORY ; CX = attributes for file search
|
||
mov ah,FIND_FIRST
|
||
int 21h ; DOS- Find First matching file
|
||
jc fcRet ; return error
|
||
|
||
call CheckChild ; check child against last, temp
|
||
|
||
fcNext: mov cx,ATTR_DIRECTORY ; CX = attributes for file search
|
||
mov ah,FIND_NEXT
|
||
int 21h ; DOS- Find Next matching file
|
||
jc fcErr ; examine error
|
||
|
||
call CheckChild ; check child against last, temp
|
||
jmp fcNext ; go find another child
|
||
|
||
fcErr:
|
||
invoke Get_Ext_Error_Number ; AX = extended error code
|
||
cmp ax,ERROR_NO_MORE_FILES ; no more files?
|
||
jne short fcNope ; some other error- return it
|
||
|
||
; We ran out of files. See if we qualified at least one.
|
||
|
||
cmp byte ptr [bp],0FFh
|
||
je fcNope ; temp filename is unused- no child
|
||
|
||
; Move temp filename to child name position.
|
||
|
||
mov si,bp ; SI = ptr to temp filename
|
||
mov di,bx ; DI = ptr to child name pos'n
|
||
fcMove: lodsb ; AL = next byte of filename
|
||
stosb ; store byte
|
||
or al,al
|
||
jz fcRet ; byte was zero, return success (CY clear)
|
||
jmp fcMove ; go move another byte
|
||
|
||
fcNope: stc ; return error
|
||
fcRet: lahf
|
||
add sp,20 ; restore stack
|
||
sahf
|
||
ret
|
||
|
||
FindNextChild endp
|
||
|
||
|
||
|
||
|
||
|
||
;*** FindOneInMem - find the first available entry in TPA
|
||
;*** FindNextInMem - find the next available entry in TPA
|
||
;
|
||
; ENTRY ES = TPA seg addr
|
||
; BX = ptr to entry in TPA
|
||
;
|
||
; EXIT BX = ptr to entry found
|
||
; CY = set if no more entries available in TPA
|
||
;
|
||
; USED AL
|
||
|
||
FindOneInMem proc
|
||
|
||
mov al,es:[bx] ; examine 'used' byte of starting entry
|
||
cmp al,1
|
||
je FindNextInMem ; entry has already been used
|
||
cmp al,0FFh
|
||
je foNoMore ; 0FFh, we're at the end of the list
|
||
|
||
; BX = ptr to entry that hasn't been output yet.
|
||
|
||
clc ; return success
|
||
ret
|
||
|
||
FindNextInMem:
|
||
add bx,size EntryStruc ; BX = ptr to next entry
|
||
jmp FindOneInMem ; go look at it
|
||
|
||
foNoMore:
|
||
stc ; ran out of entries, return failure
|
||
ret
|
||
|
||
FindOneInMem endp
|
||
|
||
|
||
|
||
|
||
;*** GetEnvValue - get value of our environment variable
|
||
;
|
||
; ENTRY DS, ES = TRANGROUP seg addr
|
||
;
|
||
; EXIT CY = set if environment variable not in environment
|
||
;
|
||
; Otherwise:
|
||
; SI = ptr to environment variable asciiz value in TRANGROUP
|
||
;
|
||
; USED AX,BX,CX,DX,DI
|
||
; (We assume the (almost) worst, since we don't know about
|
||
; Find_Name_In_Environment.)
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; ScanBuf is loaded with value text
|
||
|
||
GetEnvValue proc
|
||
|
||
push es ; save ES
|
||
mov si,offset TRANGROUP:DirEnvVar ; DS:SI = ptr to variable name
|
||
invoke Find_Name_In_Environment
|
||
jc geRet ; name not found in environment
|
||
|
||
; ES:DI = ptr to value of environment variable
|
||
; We're assuming DS, CS, and SS are unchanged.
|
||
|
||
push ds
|
||
push es
|
||
pop ds
|
||
pop es
|
||
|
||
assume ds:nothing
|
||
|
||
; DS = seg addr of environment variable value (in environment segment)
|
||
; ES = TRANGROUP seg addr
|
||
|
||
mov si,di ; DS:SI = ptr to value string
|
||
mov di,offset TRANGROUP:ScanBuf ; ES:DI = ptr to dest buffer
|
||
@@: lodsb
|
||
or al,al
|
||
stosb
|
||
loopnz @B ; move the string, including trailing null
|
||
|
||
push es
|
||
pop ds ; DS = TRANGROUP seg addr again
|
||
assume ds:TRANGROUP
|
||
|
||
mov si,offset TRANGROUP:ScanBuf ; SI = ptr to var value
|
||
geRet: pop es ; restore ES
|
||
ret
|
||
|
||
GetEnvValue endp
|
||
|
||
|
||
|
||
|
||
;*** GetFirst - get first directory entry from disk
|
||
;
|
||
; ENTRY DOS DTA established at DirBuf
|
||
; FCB contains drive # and filename
|
||
; Current directory (on selected drive) is the one to search
|
||
; AttrSpecified & AttrSelect masks set
|
||
;
|
||
; EXIT CY = clear if success
|
||
; DirBuf contains extended FCB for file found
|
||
;
|
||
; If unsuccessful,
|
||
; CY = set
|
||
; Ready for DOS Get Extended Error call
|
||
;
|
||
; USED AX,DX
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; FCB-7 = 0FFh to mark extended FCB
|
||
; FCB-1 = attribute mask to find all files
|
||
; These fields should remain unmodified for GetNext calls.
|
||
;
|
||
;
|
||
;*** GetNext - get next directory entry from disk
|
||
;
|
||
; ENTRY As for GetFirst, plus
|
||
; FCB-7 set up as extended FCB w/ find-all attribute byte
|
||
;
|
||
; EXIT As for GetFirst
|
||
;
|
||
; USED AX,DX
|
||
|
||
GetFirst proc
|
||
|
||
mov byte ptr ds:FCB-7,0FFh ; signal extended FCB
|
||
mov byte ptr ds:FCB-1,ATTR_ALL
|
||
; find any file
|
||
mov dx,FCB-7 ; DX = ptr to extended FCB
|
||
mov ah,DIR_SEARCH_FIRST ; AH = DOS Find First function code
|
||
int 21h ; call DOS
|
||
shl al,1 ; CY = set if error
|
||
jc gfRet ; return error
|
||
jmp short gfFound ; go look at attr's
|
||
|
||
GetNext:
|
||
mov dx,FCB-7 ; DX = ptr to extended FCB
|
||
mov ah,DIR_SEARCH_NEXT ; AH = DOS Find Next function code
|
||
int 21h ; call DOS
|
||
shl al,1 ; CY = set if error
|
||
jc gfRet ; return error
|
||
|
||
;* Found an entry. Check attributes.
|
||
|
||
gfFound:
|
||
mov al,[DirBuf+8].dir_attr ; AL = file attributes
|
||
mov ah,AttrSpecified ; AH = mask of pertinent attr's
|
||
and al,ah ; AL = pertinent attr's of file
|
||
and ah,AttrSelect ; AH = attr settings to match
|
||
cmp al,ah
|
||
jne GetNext ; attr's don't match, look for another
|
||
|
||
gfRet: ret
|
||
|
||
GetFirst endp
|
||
|
||
|
||
|
||
|
||
;*** ListDir - search for and list files in the current directory
|
||
;
|
||
; List header, files, and trailer for current directory on selected
|
||
; drive. Header & trailer are listed if at least one file is found.
|
||
; If no qualifying files are found, no display output occurs.
|
||
;
|
||
; ENTRY Current directory (on selected drive) is the one to be listed
|
||
; FCB contains selected drive # and filename spec
|
||
; Option bits, attribute masks, and sort codes set up
|
||
;
|
||
; EXIT CY = clear if no error
|
||
; FileCnt = # files found & displayed
|
||
;
|
||
; If error,
|
||
; CY = set
|
||
; Ready for DOS Get Extended Error call
|
||
;
|
||
; USED AX,BX,CX,DX,SI,DI,BP
|
||
; FileSiz
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; FileCntTotal, FileSizTotal are updated.
|
||
; Files found are listed. A directory header and trailer are
|
||
; displayed only if files are found.
|
||
|
||
ListDir proc
|
||
|
||
xor ax,ax
|
||
mov FileCnt,ax ; zero file count
|
||
mov word ptr FileSiz,ax ; zero file size accumulator
|
||
mov word ptr FileSiz+2,ax
|
||
|
||
cmp DestBuf,0 ; check for sort code
|
||
je @F ; no sort
|
||
call LoadEntries ; load entries for sorted listing
|
||
jnc @F ; no error - continue
|
||
invoke Get_Ext_Error_Number ; AX = DOS error code
|
||
stc
|
||
jmp short ldErr ; return error
|
||
@@:
|
||
call FindFirst ; find first file
|
||
jc ldErr ; not found, return error
|
||
|
||
; BX = offset in TPA buffer of entry found
|
||
|
||
call DisplayHeader ; if at least one file, display header
|
||
call DisplayFile ; display the file entry
|
||
ldNext:
|
||
call FindNext ; find another file
|
||
jc ldErr ; not found
|
||
call DisplayFile ; display entry
|
||
jmp ldNext ; go find another one
|
||
|
||
ldErr:
|
||
cmp ax,ERROR_FILE_NOT_FOUND
|
||
je ldDone ; file not found, we're done
|
||
cmp ax,ERROR_NO_MORE_FILES
|
||
je ldDone ; no more files, we're done
|
||
stc
|
||
jmp short ldRet
|
||
|
||
ldDone:
|
||
cmp FileCnt,0
|
||
je @F ; no files found, just return
|
||
call DisplayTrailer ; display trailing info
|
||
@@: clc ; return success
|
||
|
||
ldRet: ret
|
||
|
||
ListDir endp
|
||
|
||
|
||
|
||
|
||
;*** LoadEntries - attempt to load entries from current directory
|
||
;
|
||
; Load all qualifying directory entries from the current directory
|
||
; into the TPA. If an error is returned by FindFirst/FindNext calls
|
||
; other than 'no more files', return to caller with carry flag set.
|
||
; If we run out of buffer space, display a message that we haven't
|
||
; enough memory to sort this directory, but return without error.
|
||
; Other routines know whether or not entries have been loaded by
|
||
; the 'inmem' flag bit, which we set here.
|
||
;
|
||
; The TPA is usually 64K - 512 bytes long. At 20 bytes per entry,
|
||
; this allows sorting over 3000 entries in a directory.
|
||
;
|
||
; ENTRY Tpa = buffer seg addr
|
||
; BytCnt = buffer length, in bytes
|
||
; Current directory (on selected drive) is the one to load
|
||
; FCB contains drive # and filespec
|
||
; Bits, AttrSpecified, AttrSelect, & DestBuf (sort codes) are set
|
||
;
|
||
; EXIT CY = set if error
|
||
; If error, DOS Get Extended Error will get error info
|
||
;
|
||
; USED AX,CX,DX,SI,DI
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; Inmem bit of Bits = set if load succeeded.
|
||
; Tpa buffer contains directory entries.
|
||
; Byte after last entry = 0FFh.
|
||
|
||
LoadEntries proc
|
||
|
||
push es ; save TRANGROUP seg addr
|
||
mov es,Tpa ; ES = TPA seg addr
|
||
xor di,di ; ES:DI = destination ptr
|
||
and Bits,not mask inmem ; signal entries not loaded
|
||
|
||
call GetFirst ; look for first file
|
||
jc leRet ; return any error
|
||
call LoadEntry ; load entry into TPA
|
||
|
||
leNext: call GetNext ; get another file
|
||
jc leLoaded ; assume any error is no more files
|
||
mov ax,BytCnt ; AX = size of TPA
|
||
sub ax,di ; AX = bytes left in TPA
|
||
cmp ax,size EntryStruc+2 ; insist on entry size + 2 bytes
|
||
jb leOk ; not enough memory left, give up
|
||
call LoadEntry ; load entry into TPA
|
||
jmp leNext ; go get another file
|
||
|
||
leLoaded:
|
||
mov byte ptr es:[di],0FFh ; mark end of entry list
|
||
or Bits,mask inmem ; signal entries loaded in memory
|
||
leOk: clc ; return no error
|
||
|
||
leRet: pop es ; ES = TRANGROUP seg addr again
|
||
ret
|
||
|
||
LoadEntries endp
|
||
|
||
|
||
|
||
|
||
;*** LoadEntry - load directory entry from DirBuf ext'd FCB
|
||
;
|
||
; ENTRY ES:DI = ptr to load point in TPA
|
||
; DirBuf contains extended FCB of entry to load
|
||
;
|
||
; EXIT ES:DI = ptr to next byte available in TPA
|
||
;
|
||
; USED AX,CX,SI
|
||
;
|
||
; NOTES
|
||
;
|
||
; I could've used symbolic offsets and sizes of fields from
|
||
; the dir_entry struc to do this, but this is time-critical,
|
||
; so I hard-wired the structure of the DOS 4.x returned FCB,
|
||
; as well as our private directory entry structure.
|
||
;
|
||
; We force a zero size for subdirectory files. A zero size is
|
||
; ordinarily returned for subdirectories, but with Novell
|
||
; Netware 286 or 386 loaded, we can't depend on it. Bug #1594.
|
||
|
||
LoadEntry proc
|
||
|
||
mov si,offset TRANGROUP:Dirbuf+8 ; DS:SI = ptr to filename
|
||
xor al,al ; AL = 0
|
||
stosb ; 'used' byte = false
|
||
mov cx,11
|
||
rep movsb ; transfer filename & extension
|
||
lodsb ; AL = attrib byte
|
||
stosb ; store attrib byte
|
||
add si,dir_time-dir_attr-1 ; skip to time field
|
||
movsw ; transfer time
|
||
movsw ; transfer date
|
||
inc si ; skip alloc unit
|
||
inc si
|
||
|
||
and al,ATTR_DIRECTORY
|
||
jnz leSetDirSize ; force zero size for subdir
|
||
|
||
movsw
|
||
movsw ; transfer size
|
||
ret
|
||
|
||
leSetDirSize:
|
||
xor ax,ax
|
||
stosw
|
||
stosw ; store zero size
|
||
ret
|
||
|
||
LoadEntry endp
|
||
|
||
|
||
|
||
|
||
;*** NoOrder - turn sorting off
|
||
;
|
||
; ENTRY nothing
|
||
;
|
||
; EXIT CY clear
|
||
;
|
||
; USED AX
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; DestBuf is updated with sort code bytes. See DestBuf description.
|
||
|
||
NoOrder proc
|
||
|
||
mov DestBuf,0 ; no sort
|
||
clc ; no error
|
||
ret
|
||
|
||
NoOrder endp
|
||
|
||
|
||
|
||
|
||
;*** OnOffSw - record occurence of on/off option switch
|
||
;
|
||
; ENTRY DI = index into word list of switches
|
||
;
|
||
; EXIT CY clear
|
||
;
|
||
; USED AX,CX
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; Bits modified to indicate option state.
|
||
|
||
OnOffSw proc
|
||
|
||
mov cx,di ; CX = index into word list of options
|
||
shr cx,1
|
||
shr cx,1 ; CX = bit position of option
|
||
mov ax,1
|
||
shl ax,cl ; AX = bit mask of option
|
||
test di,2 ; check if it is a negated option
|
||
jz @F ; it's negated
|
||
or Bits,ax ; turn option on
|
||
jmp short ooRet
|
||
|
||
@@: not ax ; AX = complemented bit mask of option
|
||
and Bits,ax ; turn option off
|
||
|
||
ooRet: clc ; always return success
|
||
ret
|
||
|
||
OnOffSw endp
|
||
|
||
|
||
|
||
|
||
;*** ParseAttr - parse and record /A option
|
||
;
|
||
; ENTRY BX = ptr to system parser result buffer for /A occurence
|
||
;
|
||
; EXIT CY = set if error occurs parsing attribute conditions
|
||
;
|
||
; For parse error, we set up for Std_EPrintf call:
|
||
; AX = parse error code, like system parser
|
||
; DX = ptr to message block
|
||
;
|
||
; USED AX,CX,DX,DI
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; AttrSpecified, AttrSelect are updated with new attribute conditions.
|
||
; If parse error occurs, attribute conditions parsed so far hold.
|
||
;
|
||
; For parse error, we set up for Std_EPrintf call:
|
||
; Msg_Disp_Class = parse error message class
|
||
; Message block (see DX) is set up for parse error message
|
||
|
||
ParseAttr proc
|
||
|
||
push si ; save SI
|
||
mov AttrSpecified,0 ; cancel all attribute conditions
|
||
|
||
; Each /A invocation starts by assuming all files are to be listed.
|
||
|
||
mov si,word ptr [bx].ValuePtr ; SI = ptr to string after /A
|
||
|
||
paLoop: mov dx,1 ; DX = 1 (for un-negated attribute)
|
||
|
||
lodsb ; AL = next char in string
|
||
or al,al
|
||
jz paOk ; it's terminating null, we're done
|
||
cmp al,'-'
|
||
jne @F ; not '-', go look for letter
|
||
dec dx ; DX = 0 (for negated attribute)
|
||
lodsb ; AL = next char
|
||
@@:
|
||
mov di,offset TRANGROUP:AttrLtrs ; DI = ptr to attrib letter list
|
||
mov cx,NUM_ATTR_LTRS ; CX = length of attrib letter list
|
||
repne scasb ; look for our letter in the list
|
||
jne paErr ; not found, return error
|
||
|
||
not cx
|
||
add cx,NUM_ATTR_LTRS ; CX = attrib bit #, 0-5
|
||
|
||
; Note that we rely on AttrLtrs to be in the attribute bit order,
|
||
; starting from bit 0.
|
||
|
||
; Record this attribute bit in AttrSpecified.
|
||
|
||
mov al,1
|
||
shl al,cl ; AL = mask for our bit
|
||
or AttrSpecified,al ; set it in the 'specified' mask
|
||
|
||
; Record the selected state for this attribute in AttrSelect.
|
||
; DX = 0 or 1, the selected state for this attribute.
|
||
|
||
not al ; AL = mask for all other bits
|
||
and AttrSelect,al ; clear our bit
|
||
shl dl,cl ; DL = our bit state in position
|
||
or AttrSelect,dl ; set selected attr state
|
||
jmp paLoop ; go look at next char
|
||
|
||
; The attribute letter string is invalid.
|
||
|
||
paErr:
|
||
call SetupParamError ; set message up for Std_EPrintf
|
||
stc ; return error
|
||
jmp short paRet
|
||
|
||
paOk: clc ; return success
|
||
|
||
paRet: pop si ; restore SI
|
||
ret
|
||
|
||
ParseAttr endp
|
||
|
||
|
||
|
||
|
||
;*** ParseLine - parse a line of text
|
||
;
|
||
; Parse text until an EOL (CR or NUL) is found, or until a parse
|
||
; error occurs.
|
||
;
|
||
; ENTRY DS:SI = ptr to text
|
||
; CS, DS, ES = TRANGROUP seg addr
|
||
;
|
||
; EXIT AX = last return code from system parser
|
||
; CX = # positional parameters (pathnames) found - 0 or 1
|
||
;
|
||
; If parse error occurred, we're set up for Std_EPrintf call:
|
||
; DX = ptr to message block
|
||
;
|
||
; USED BX,CX,DX,SI,DI
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; Bits may contain new option settings.
|
||
; DestBuf may contain new series of sort codes.
|
||
; AttrSpecified, AttrSelect may contain new attribute conditions.
|
||
; SrcBuf may contain a new default pathname/filespec.
|
||
; PathPos, PathCnt updated for new pathname.
|
||
;
|
||
; If parse error occurred, we're set up for Std_EPrintf call:
|
||
; Msg_Disp_Class = parse error class
|
||
; Byte after last parameter in text is zeroed to make ASCIIZ string
|
||
; Message block (see DX) is set up for parse error message
|
||
|
||
ParseLine proc
|
||
|
||
mov di,offset TRANGROUP:Parse_Dir ; ES:DI = ptr to parse block
|
||
xor cx,cx ; CX = # positionals found
|
||
plPars:
|
||
invoke Parse_With_Msg ; call parser
|
||
cmp ax,END_OF_LINE
|
||
je plRet ; EOL encountered, return
|
||
cmp ax,RESULT_NO_ERROR
|
||
jne plRet ; parse error occurred, return
|
||
|
||
; Parse call succeeded. We have a filespec or a switch.
|
||
; DX = ptr to result buffer
|
||
|
||
mov bx,dx ; BX = ptr to parse result buffer
|
||
cmp byte ptr [bx],RESULT_FILESPEC
|
||
je plFil ; we have a filespec
|
||
|
||
call ParseSwitch ; else we have a switch
|
||
jc plRet ; error parsing switch, return
|
||
jmp plPars ; parse more
|
||
|
||
plFil: call CopyPathname ; copy pathname into our buffer
|
||
jmp plPars ; parse more
|
||
|
||
plRet: ret
|
||
|
||
ParseLine endp
|
||
|
||
|
||
|
||
|
||
;*** ParseOrder - parse and record /O option
|
||
;
|
||
; ENTRY BX = ptr to system parser result buffer for /O occurence
|
||
;
|
||
; EXIT CY = set if error occurs parsing order
|
||
;
|
||
; For parse error, we set up for Std_EPrintf call:
|
||
; AX = parse error code, like system parser
|
||
; DX = ptr to message block
|
||
;
|
||
; USED AX,CX,DX,DI
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; DestBuf is updated with sort code bytes. See DestBuf description.
|
||
;
|
||
; For parse error, we set up for Std_EPrintf call:
|
||
; Msg_Disp_Class = parse error message class
|
||
; Message block (see DX) is set up for parse error message
|
||
|
||
ParseOrder proc
|
||
|
||
push si ; save SI
|
||
push bx ; save ptr to result buffer
|
||
|
||
mov si,word ptr [bx].ValuePtr ; SI = ptr to order letters
|
||
mov bx,offset TRANGROUP:DestBuf ; BX = ptr to sort code buffer
|
||
mov al,[si] ; AL = 1st char of order string
|
||
or al,al
|
||
jnz poLtr ; not NUL, go parse letters
|
||
|
||
; We have /O alone. Set standard sort order.
|
||
; Note hardwired dependency on character order in OrderLtrs.
|
||
|
||
mov byte ptr [bx],5 ; sort 1st by group (subdirs 1st)
|
||
inc bx
|
||
mov byte ptr [bx],1 ; then by name
|
||
inc bx
|
||
mov byte ptr [bx],2 ; then by extension
|
||
inc bx
|
||
jmp short poOk ; return success
|
||
|
||
; We have /O<something>. Parse sort order letters.
|
||
|
||
poLtr: xor dl,dl ; DL = 0 (upward sort)
|
||
lodsb ; AL = next sort order letter
|
||
or al,al
|
||
jz poOk ; NUL found, return success
|
||
|
||
cmp al,'-'
|
||
jne @F ; not '-', go look for letter
|
||
mov dl,80h ; DL = downward sort mask
|
||
lodsb ; AL = next char
|
||
@@:
|
||
mov di,offset TRANGROUP:OrderLtrs ; DI = ptr to list of letters
|
||
mov cx,NUM_ORDER_LTRS ; CX = length of list
|
||
repne scasb ; look for our letter in the list
|
||
jne poErr ; not found, return error
|
||
|
||
neg cx
|
||
add cx,NUM_ORDER_LTRS ; CL = sort order code, 1-5
|
||
or cl,dl ; CL = sort code with up/dn bit
|
||
mov [bx],cl ; store sort order code in buffer
|
||
inc bx ; BX = ptr to next spot in buffer
|
||
cmp bx,offset TRANGROUP:EndDestBuf
|
||
jae poErr ; too many letters
|
||
|
||
jmp poLtr ; go look at next char
|
||
|
||
; The sort order string is invalid.
|
||
|
||
poErr: pop bx ; BX = ptr to result buffer
|
||
call SetupParamError ; set message up for Std_EPrintf
|
||
stc ; return failure
|
||
jmp short poRet
|
||
|
||
poOk: mov byte ptr [bx],0 ; mark end of sort code list
|
||
pop bx ; BX = ptr to result buffer
|
||
clc ; return success
|
||
|
||
poRet: pop si ; restore SI
|
||
ret
|
||
|
||
ParseOrder endp
|
||
|
||
|
||
|
||
|
||
;*** ParseSwitch - parse a switch
|
||
;
|
||
; ENTRY BX = ptr to parse result buffer after system parser processed
|
||
; a switch
|
||
;
|
||
; EXIT CY = set if parse error occurred
|
||
;
|
||
; If parse error occurred, we're set up for Std_EPrintf call:
|
||
; AX = parse error code, like system parser
|
||
; DX = ptr to message block
|
||
;
|
||
; USED AX,BX,DX
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; Bits may contain new option settings.
|
||
; DestBuf may contain new series of sort codes.
|
||
; AttrSpecified, AttrSelect may contain new attribute conditions.
|
||
;
|
||
; If parse error occurred, we're set up for Std_EPrintf call:
|
||
; Msg_Disp_Class = parse error class
|
||
; Byte after last parameter in text is zeroed to make ASCIIZ string
|
||
; Message block (see DX) is set up for parse error message
|
||
|
||
ParseSwitch proc
|
||
|
||
push cx ; save CX
|
||
push di ; save DI
|
||
|
||
mov ax,[bx].SynPtr ; AX = synonym ptr
|
||
mov di,offset TRANGROUP:Dir_Sw_Ptrs
|
||
; ES:DI = ptr to list of synonym ptrs
|
||
mov cx,NUM_DIR_SWS ; CX = # of dir switches in list
|
||
cld ; scan direction = upward
|
||
repne scasw ; locate synonym ptr in list
|
||
sub di,offset TRANGROUP:Dir_Sw_Ptrs + 2
|
||
|
||
; DI = index into word list of synonym ptrs
|
||
|
||
call cs:SwHandlers[di] ; use same index into call table
|
||
|
||
pop di ; restore DI
|
||
pop cx ; restore CX
|
||
|
||
ret
|
||
|
||
; Order in this table must correspond to order in Dir_Sw_Ptrs list.
|
||
; Simple on/off switches must occur first in both lists, and must be
|
||
; in order of option bits in Bits, starting with bit 0.
|
||
|
||
SwHandlers label word
|
||
dw OnOffSw ; /-W
|
||
dw OnOffSw ; /W
|
||
dw OnOffSw ; /-P
|
||
dw OnOffSw ; /P
|
||
dw OnOffSw ; /-S
|
||
dw OnOffSw ; /S
|
||
dw OnOffSw ; /-B
|
||
dw OnOffSw ; /B
|
||
dw OnOffSw ; /-L ;M010
|
||
dw OnOffSw ; /L ;M010
|
||
dw NoOrder ; /-O
|
||
dw ParseOrder ; /O
|
||
dw DefaultAttr ; /-A
|
||
dw ParseAttr ; /A
|
||
|
||
ParseSwitch endp
|
||
|
||
|
||
|
||
break <DIR utility routines>
|
||
|
||
;*** UTILITY ROUTINES
|
||
|
||
|
||
|
||
|
||
;*** ChangeDir - change directory on target drive
|
||
;
|
||
; ENTRY FCB contains drive #
|
||
; DS:DX = ptr to ASCIIZ string w/o drive specifier
|
||
;
|
||
; EXIT Changed current directory on drive
|
||
;
|
||
; If error,
|
||
; CY = set
|
||
; DOS Get Extended Error call will get error
|
||
;
|
||
; USED AX,DX,SI,DI
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; DirBuf is used to build "d:string".
|
||
|
||
ChangeDir proc
|
||
|
||
mov di,offset TRANGROUP:DirBuf
|
||
call GetDriveLtr ; AX = "d:"
|
||
stosw ; put drive specifier in buffer
|
||
mov si,dx ; SI = ptr to argument string
|
||
cdLoop:
|
||
lodsb
|
||
stosb ; move byte to buffer
|
||
or al,al
|
||
jne cdLoop ; continue until null transferred
|
||
|
||
mov dx,offset TRANGROUP:DirBuf ; DX = ptr to "d:string"
|
||
mov ah,CHDIR
|
||
int 21h ; change directory
|
||
ret ; return what CHDIR returns
|
||
|
||
ChangeDir endp
|
||
|
||
|
||
|
||
|
||
;*** CmpAscz - compare two ASCIIZ strings alphanumerically
|
||
;
|
||
; ENTRY DS:SI = ptr to one ASCIIZ string
|
||
; ES:DI = ptr to another ASCIIZ string
|
||
;
|
||
; EXIT flags set after REPE CMPSB
|
||
;
|
||
; USED AL,CX,SI,DI
|
||
;
|
||
; NOTES
|
||
;
|
||
; Maximum run of comparison is length of DS:SI string.
|
||
; This ensures that two identical strings followed by
|
||
; random characters will compare correctly.
|
||
|
||
CmpAscz proc
|
||
|
||
push di
|
||
|
||
mov di,si
|
||
xor al,al
|
||
mov cx,0FFFFh
|
||
repne scasb
|
||
not cx
|
||
|
||
pop di
|
||
repe cmpsb
|
||
ret
|
||
|
||
CmpAscz endp
|
||
|
||
|
||
|
||
|
||
;*** CopyPathname - copy pathname to our buffer
|
||
;
|
||
; ENTRY BX = ptr to parse result buffer after system parser processed
|
||
; a filespec
|
||
;
|
||
; EXIT nothing
|
||
;
|
||
; USED AX
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; SrcBuf may contain a new pathname/filespec.
|
||
; PathPos, PathCnt updated for new pathname.
|
||
|
||
CopyPathname proc
|
||
|
||
push si
|
||
lds si,dword ptr [bx].ValuePtr ; load far ptr from result buffer
|
||
invoke Move_To_SrcBuf ; copy pathname to SrcBuf
|
||
pop si
|
||
ret
|
||
|
||
CopyPathname endp
|
||
|
||
|
||
|
||
;*** CountFile - update counters with current file
|
||
;
|
||
; ENTRY BX = offset of entry in TPA buffer
|
||
;
|
||
; EXIT nothing
|
||
;
|
||
; USED AX,DX
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; FileCnt, FileCntTotal, FileSiz, FileSizTotal are updated.
|
||
|
||
CountFile proc
|
||
|
||
push es ; save TRANGROUP seg addr
|
||
mov es,Tpa ; ES = TPA seg addr
|
||
|
||
inc FileCnt ; # files this directory
|
||
inc word ptr FileCntTotal ; # files total
|
||
jnz @F
|
||
inc word ptr FileCntTotal+2
|
||
@@:
|
||
mov ax,word ptr es:[bx].filesize ; AX = low word of file size
|
||
mov dx,word ptr es:[bx].filesize+2 ; DX = high word of file size
|
||
add word ptr FileSiz,ax
|
||
adc word ptr FileSiz+2,dx ; size of this directory
|
||
add word ptr FileSizTotal,ax
|
||
adc word ptr FileSizTotal+2,dx ; total size of files listed
|
||
|
||
pop es ; ES = TRANGROUP seg addr again
|
||
ret
|
||
|
||
CountFile endp
|
||
|
||
|
||
|
||
|
||
;*** DisplayBare - display filename in bare format
|
||
;
|
||
; ENTRY BX = offset of entry in TPA buffer
|
||
;
|
||
; EXIT DX = # char's displayed, including dot
|
||
;
|
||
; USED AX,CX,SI,DI
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; Filename is displayed in name.ext format, followed by cr/lf.
|
||
; If /s is on, complete pathname is displayed.
|
||
;
|
||
; NOTE
|
||
;
|
||
; Directory pseudofiles . and .. and suppressed in bare listing.
|
||
|
||
DisplayBare proc
|
||
|
||
; Suppress . and .. files from bare listing.
|
||
|
||
mov cx,ds ; CX = saved TRANGROUP seg addr
|
||
mov ds,Tpa ; DS:BX = ptr to file entry
|
||
assume ds:NOTHING
|
||
cmp ds:[bx].filename,'.' ; check 1st char of filename
|
||
mov ds,cx ; DS = TRANGROUP seg addr again
|
||
assume ds:TRANGROUP
|
||
je dbRet ; it's . or .. - don't display
|
||
|
||
test Bits,mask subd
|
||
jz dbNameExt ; not /s - display filename only
|
||
|
||
invoke Build_Dir_String
|
||
mov di,offset TRANGROUP:BwdBuf ; ES:DI = ptr to dir string
|
||
|
||
test Bits,mask lcase ;M010;check for lowercase option
|
||
jz @F ;M010;lowercase not needed
|
||
mov si,di ;M010;DS:SI --> ASCIIZ string in BwdBuf
|
||
call LowercaseString ;M010;path string is in BwdBuf
|
||
|
||
@@: xor al,al ; AL = 0
|
||
mov cx,0FFFFh
|
||
cld
|
||
repne scasb ; ES:DI = ptr to byte after null
|
||
dec di ; ES:DI = ptr to null byte
|
||
|
||
ifdef DBCS
|
||
push si
|
||
push di
|
||
mov si,offset TRANGROUP:BwdBuf
|
||
dec di
|
||
call CheckDBCSTailByte
|
||
pop di
|
||
pop si
|
||
jz dbTailByte ; if last char is double byte
|
||
endif
|
||
|
||
cmp byte ptr es:[di-1],'\'
|
||
je @F ; already terminated w/ '\'
|
||
|
||
ifdef DBCS
|
||
dbTailByte:
|
||
endif
|
||
|
||
mov ax,'\' ; AX = '\',0
|
||
stosw ; add to dir string
|
||
@@:
|
||
mov String_Ptr_2,offset TRANGROUP:BwdBuf
|
||
mov dx,offset TRANGROUP:String_Buf_Ptr
|
||
invoke Std_Printf ; display device & directory path
|
||
|
||
dbNameExt:
|
||
call DisplayDotForm ; display name.ext
|
||
invoke CrLf2 ; display cr/lf
|
||
call UseLine ;M007;Allow /p with /b
|
||
dbRet: ret
|
||
|
||
DisplayBare endp
|
||
|
||
|
||
|
||
|
||
;*** DisplayDotForm - display filename in compressed dot format
|
||
;
|
||
; Display name.ext, with no cr/lf's. Dot is displayed only
|
||
; if the filename has a nonblank extension.
|
||
;
|
||
; ENTRY BX = offset of entry in TPA buffer
|
||
;
|
||
; EXIT DX = # char's displayed, including dot
|
||
;
|
||
; USED AX,CX,SI,DI
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; Filename is displayed in name.ext format.
|
||
;
|
||
; NOTE
|
||
;
|
||
; We allow for bogus filenames that have blanks embedded
|
||
; in the name or extension.
|
||
|
||
; Bugbug: might be a good performance gain if we buffered
|
||
; up the output and used DOS function 9.
|
||
|
||
DisplayDotForm proc
|
||
|
||
push ds ; save TRANGROUP seg addr
|
||
push es ; save ES
|
||
mov ax,cs:Tpa ; AX = TPA seg addr
|
||
mov ds,ax ; DS:BX = ptr to entry
|
||
assume ds:nothing
|
||
mov es,ax ; ES:BX = ptr to entry
|
||
|
||
mov di,bx ; ES:DI = ptr to entry
|
||
add di,filename + size filename - 1
|
||
; ES:DI = ptr to last char in name field
|
||
mov cx,size filename ; CX = length of name field
|
||
mov al,' '
|
||
std ; scan down
|
||
repe scasb ; scan for nonblank
|
||
|
||
; Assume file name has at least one character.
|
||
|
||
inc cx ; CX = # chars in name
|
||
mov dx,cx ; DX = # chars to be displayed
|
||
|
||
mov si,bx ; DS:SI = ptr to entry
|
||
add si,filename ; DS:SI = ptr to name
|
||
|
||
NextNameChar:
|
||
cld
|
||
lodsb ; AL = next char
|
||
|
||
ifdef DBCS
|
||
invoke testkanj
|
||
jz @f ; if this is not lead byte
|
||
invoke Print_Char ; display lead byte
|
||
dec cx
|
||
jz ExtChar ; if this is end
|
||
lodsb ; get tail byte
|
||
jmp short NameChar10 ; display tail byte
|
||
@@:
|
||
endif
|
||
|
||
test Bits,mask lcase ;M010;check for lowercase option
|
||
jz @F ;M010;lowercase not required
|
||
call LowerCase ;M010;filename char is in AL
|
||
|
||
ifdef DBCS
|
||
NameChar10:
|
||
endif
|
||
|
||
@@: invoke Print_Char ; display it
|
||
loop NextNameChar
|
||
|
||
ifdef DBCS
|
||
ExtChar:
|
||
endif
|
||
|
||
; Now do extension.
|
||
|
||
mov di,bx ; ES:DI = ptr to entry
|
||
add di,fileext + size fileext - 1
|
||
; ES:DI = ptr to last char in ext field
|
||
mov cx,size fileext ; CX = length of ext field
|
||
mov al,' '
|
||
std ; scan down
|
||
repe scasb ; scan for nonblank
|
||
je ddDone ; no nonblank chars in ext
|
||
|
||
inc cx ; CX = # chars in ext
|
||
add dx,cx ; DX = total # chars to be displayed
|
||
inc dx ; including dot
|
||
|
||
mov al,'.'
|
||
invoke Print_Char
|
||
mov si,bx ; DS:SI = ptr to entry
|
||
add si,fileext ; DS:SI = ptr to ext
|
||
|
||
NextExtChar:
|
||
cld
|
||
lodsb ; AL = next char
|
||
|
||
ifdef DBCS
|
||
invoke testkanj
|
||
jz @f ; if this is not lead byte
|
||
invoke Print_Char ; display lead byte
|
||
dec cx
|
||
jz ddDone ; if this is end
|
||
lodsb ; get tail byte
|
||
jmp short ExtChar10 ; display tail byte
|
||
@@:
|
||
endif
|
||
|
||
test CS:Bits,mask lcase ;M010;check for lowercase option
|
||
jz @F ;M010;lowercase not required
|
||
call LowerCase ;M010;fileext char is in AL
|
||
|
||
ifdef DBCS
|
||
ExtChar10:
|
||
endif
|
||
|
||
@@: invoke Print_Char ; display it
|
||
loop NextExtChar
|
||
|
||
ddDone: pop es ; restore ES
|
||
pop ds ; DS = TRANGROUP seg addr again
|
||
assume ds:TRANGROUP
|
||
cld ; leave direction flag = up
|
||
ret
|
||
|
||
DisplayDotForm endp
|
||
|
||
|
||
|
||
|
||
;*** DisplayFile - display file entry, update counters
|
||
;
|
||
; ENTRY BX = offset of entry in TPA buffer
|
||
; Bits contains /w, /p settings
|
||
;
|
||
; EXIT nothing
|
||
;
|
||
; USED AX,CX,DX,SI,DI,BP
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; Entry is displayed.
|
||
; If not /b,
|
||
; Cursor is left at end of entry on screen.
|
||
; FileCnt, FileCntTotal, FileSiz, FileSizTotal are updated.
|
||
; If /b,
|
||
; Cursor is left at beginning of next line.
|
||
; Cnt's and Siz's aren't updated.
|
||
|
||
DisplayFile proc
|
||
|
||
test Bits,mask bare
|
||
jz dfNorm ; not /b - do normal display
|
||
|
||
call DisplayBare ; display file in bare format
|
||
jmp short dfRet
|
||
|
||
dfNorm: call DisplayNext ; pos'n cursor for next entry
|
||
test Bits,mask wide
|
||
jz dfFull ; full format
|
||
call DisplayWide ; wide format
|
||
jmp short dfCnt
|
||
|
||
dfFull: call DisplayName ; display filename & extension
|
||
call DisplayTheRest ; display size, date, time
|
||
|
||
dfCnt: call CountFile ; update file counters
|
||
dfRet: ret
|
||
|
||
DisplayFile endp
|
||
|
||
|
||
|
||
|
||
;*** DisplayHeader - display directory header of working directory
|
||
;
|
||
; ENTRY Current directory (on selected drive) is the one to display
|
||
; LeftOnPage = # lines left on display page
|
||
;
|
||
; EXIT nothing
|
||
;
|
||
; ERROR EXIT
|
||
;
|
||
; Build_Dir_String will exit through CError with "Invalid drive
|
||
; specification" if there's a problem obtaining the current
|
||
; directory pathname.
|
||
;
|
||
; USED AX,DX,SI,DI
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; BwdBuf (which is really the same buffer as DirBuf, which
|
||
; we are using for the DTA) contains the directory string.
|
||
; LeftOnPage is adjusted.
|
||
|
||
DisplayHeader proc
|
||
|
||
test Bits,mask bare
|
||
jnz dhRet ; /b - don't display header
|
||
|
||
test Bits,mask subd
|
||
jz dhNorm ; not /s
|
||
|
||
; For subdirectory listings, put a blank line before the header.
|
||
|
||
invoke Crlf2 ; start with a blank line
|
||
call UseLine
|
||
jmp short dhCom
|
||
dhNorm:
|
||
mov al,BLANK ; if not /s, precede by a blank
|
||
invoke Print_Char ; print a leading blank
|
||
dhCom:
|
||
invoke Build_Dir_String
|
||
mov dx,offset TRANGROUP:DirHead_ptr
|
||
invoke Std_Printf ; print header & cr/lf
|
||
call UseLine
|
||
invoke Crlf2 ; another cr/lf
|
||
call UseLine
|
||
dhRet: ret
|
||
|
||
DisplayHeader endp
|
||
|
||
|
||
|
||
|
||
;*** DisplayName - display file name & extension
|
||
;
|
||
; ENTRY BX = offset of entry in TPA buffer
|
||
;
|
||
; EXIT nothing
|
||
;
|
||
; USED AX,CX,DX,SI,DI
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; Filename & extension are displayed in spread format.
|
||
; Cursor is left at end of extension.
|
||
|
||
DisplayName proc
|
||
|
||
push ds ; save TRANGROUP seg addr
|
||
mov ds,Tpa ; DS:BX = ptr to entry
|
||
assume ds:nothing
|
||
mov si,bx ; DS:SI = ptr to entry
|
||
add si,filename ; DS:SI = ptr to filename
|
||
mov di,offset TRANGROUP:CharBuf ; ES:DI = ptr to CharBuf
|
||
|
||
mov cx,8
|
||
cld
|
||
rep movsb ; move filename to CharBuf
|
||
mov al,' '
|
||
stosb ; add a blank
|
||
mov cx,3
|
||
rep movsb ; add extension
|
||
xor al,al
|
||
stosb ; add a NULL
|
||
|
||
pop ds ; DS = TRANGROUP seg addr again
|
||
assume ds:TRANGROUP
|
||
|
||
test Bits,mask lcase ;M010;check for lowercase option
|
||
jz @F ;M010;lowercase not required
|
||
mov si,offset TRANGROUP:CharBuf ;M010;DS:SI --> ASCIIZ string
|
||
call LowercaseString ;M010;filename.ext string is in CharBuf
|
||
|
||
@@: mov String_Ptr_2,offset TRANGROUP:CharBuf
|
||
mov dx,offset TRANGROUP:String_Buf_Ptr
|
||
invoke Std_Printf ; print filename & extension
|
||
ret
|
||
|
||
DisplayName endp
|
||
|
||
|
||
|
||
|
||
;*** DisplayNext - move display cursor to next entry position
|
||
;
|
||
; ENTRY LeftOnLine = # entries can still be printed on this line
|
||
; LeftOnPage = # lines can still be printed for this page
|
||
; FileCnt = # files in this dir displayed before this one
|
||
; Bits contains /w setting
|
||
;
|
||
; EXIT nothing
|
||
;
|
||
; USED AX,DX
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; LeftOnLine will be updated to reflect the entry about to be
|
||
; displayed.
|
||
; LeftOnPage may be updated.
|
||
|
||
DisplayNext proc
|
||
|
||
cmp FileCnt,0
|
||
je dn1st ; 1st file in directory
|
||
cmp LeftOnLine,0
|
||
jng dnEol ; no more room on this line
|
||
|
||
; We are in wide mode (LeftOnLine is always 0 otherwise) and
|
||
; we still have room for more on this line.
|
||
; Tab to next position.
|
||
|
||
mov dx,offset TRANGROUP:Tab_Ptr
|
||
invoke Std_Printf
|
||
jmp short dnDone
|
||
|
||
dnEol:
|
||
|
||
; Start this entry on a new line.
|
||
|
||
invoke Crlf2 ; start on new line
|
||
call UseLine
|
||
dn1st: mov al,PerLine
|
||
mov LeftOnLine,al ; reset # entries left on line
|
||
|
||
dnDone: dec LeftOnLine ; reflect the entry about to be displayed
|
||
ret
|
||
|
||
DisplayNext endp
|
||
|
||
|
||
|
||
|
||
;*** DisplayTheRest - display file size/dir, date, time
|
||
;
|
||
; ENTRY BX = offset of entry in TPA buffer
|
||
; Display cursor is at end of file extension
|
||
;
|
||
; EXIT nothing
|
||
;
|
||
; USED AX,CX,DX,SI,DI,BP
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; File size, date, & time are displayed.
|
||
|
||
DisplayTheRest proc
|
||
|
||
push es ; save TRANGROUP seg addr
|
||
mov es,Tpa ; ES = TPA seg addr
|
||
mov bp,bx ; BP = offset of entry in TPA
|
||
test es:[bp].fileattr,ATTR_DIRECTORY
|
||
jz drNonDir ; not a directory file
|
||
|
||
; For a directory file, display <DIR> instead of size.
|
||
|
||
mov dx,offset TRANGROUP:DMes_Ptr
|
||
invoke Std_Printf
|
||
jmp short drCom ; skip to common fields
|
||
|
||
drNonDir:
|
||
|
||
; For a non-directory file, display file size.
|
||
|
||
mov dx,word ptr es:[bp].filesize
|
||
mov File_Size_Low,dx
|
||
mov dx,word ptr es:[bp].filesize+2
|
||
mov File_Size_High,dx
|
||
mov dx,offset TRANGROUP:Disp_File_Size_Ptr
|
||
invoke Std_Printf
|
||
|
||
drCom:
|
||
|
||
; For all files, display date & time.
|
||
|
||
mov ax,es:[bp].filedate ; AX = date word
|
||
or ax,ax ; test for null date (DOS 1.x)
|
||
jz drDone ; no date, skip date/time display
|
||
mov bx,ax ; BX = date word
|
||
and ax,1Fh ; AX = day of month
|
||
mov dl,al ; DL = day of month
|
||
mov ax,bx ; AX = date word
|
||
mov cl,5
|
||
shr ax,cl ; shift day out
|
||
and al,0Fh ; AL = month
|
||
mov dh,al ; DH = month
|
||
mov cl,bh
|
||
shr cl,1 ; CL = year - 1980
|
||
xor ch,ch ; CX = year - 1980
|
||
add cx,80 ; CX = 2-digit year
|
||
cmp cl,100
|
||
jb @F ; not year 2000 yet, skip ahead
|
||
sub cl,100 ; adjust for 21st century
|
||
@@: xchg dh,dl ; DX = month/day
|
||
mov DirDat_Yr,cx ; move year to msg block
|
||
mov DirDat_Mo_Day,dx ; move month/day to msg block
|
||
mov cx,es:[bp].filetime ; CX = file time
|
||
jcxz drPrint ; no time field - go print
|
||
shr cx,1
|
||
shr cx,1
|
||
shr cx,1 ; CH = hours
|
||
shr cl,1
|
||
shr cl,1 ; CL = minutes
|
||
xchg ch,cl ; CX = hr/min
|
||
mov DirTim_Hr_Min,cx ; move time to msg block
|
||
drPrint:mov dx,offset TRANGROUP:DirDatTim_Ptr
|
||
invoke Std_Printf ; print date & time
|
||
|
||
drDone: pop es ; ES = TRANGROUP seg addr again
|
||
mov bx,bp ; BX = offset of entry in TPA again
|
||
ret
|
||
|
||
DisplayTheRest endp
|
||
|
||
|
||
|
||
|
||
;*** DisplayTrailer - display trailing lines for directory listing
|
||
;
|
||
; ENTRY LeftOnPage = # lines left on display page
|
||
; FileCnt = # files listed
|
||
; FileSiz = total size of files listed
|
||
;
|
||
; EXIT nothing
|
||
;
|
||
; USED
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; Trailing info lines are displayed
|
||
|
||
DisplayTrailer proc
|
||
|
||
test Bits,mask bare
|
||
jnz dtrRet ; /b - don't display trailer
|
||
|
||
invoke Crlf2 ; start on new line
|
||
call UseLine
|
||
mov ax,FileCnt ; AX = # files found
|
||
|
||
DisplayCntSiz:
|
||
|
||
; DisplayTotals uses this entry point.
|
||
;
|
||
; AX = # files
|
||
; FileSiz = dword total size of files
|
||
|
||
mov Dir_Num,ax ; load # files
|
||
mov dx,offset TRANGROUP:DirMes_Ptr ; DX = ptr to message block
|
||
invoke Std_Printf ; "nnn File(s)"
|
||
|
||
mov dx,offset TRANGROUP:Bytes_Ptr
|
||
invoke Std_Printf ; "nnn bytes",cr,lf
|
||
call UseLine
|
||
|
||
dtrRet: ret
|
||
|
||
DisplayTrailer endp
|
||
|
||
|
||
|
||
|
||
;*** DisplayWide - display filename in wide format
|
||
;
|
||
; ENTRY BX = offset of entry in TPA buffer
|
||
;
|
||
; EXIT nothing
|
||
;
|
||
; USED AX,CX,DX,SI,DI
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; Name.ext is displayed. Cursor left at end of field (padded
|
||
; with blanks). Subdirectory files are displayed as [name.ext].
|
||
|
||
DisplayWide proc
|
||
|
||
push ds ; save TRANGROUP seg addr
|
||
mov ds,Tpa ; DS:BX = ptr to entry
|
||
assume ds:nothing
|
||
|
||
test ds:[bx].fileattr,ATTR_DIRECTORY
|
||
jz @F ; not a subdirectory file
|
||
mov al,'['
|
||
invoke Print_Char ; prefix subdirectory
|
||
|
||
@@: call DisplayDotForm ; display name.ext
|
||
|
||
; DX = # chars displayed in name.ext
|
||
|
||
test ds:[bx].fileattr,ATTR_DIRECTORY
|
||
jz @F ; not a subdirectory file
|
||
mov al,']'
|
||
invoke Print_Char ; postfix subdirectory
|
||
@@:
|
||
; Pad field with blanks.
|
||
|
||
mov cx,size filename + size fileext + 1
|
||
; CX = field size
|
||
sub cx,dx ; CX = # pad char's
|
||
jcxz dwDone
|
||
mov al,' '
|
||
|
||
@@: invoke Print_Char
|
||
loop @B
|
||
|
||
dwDone: pop ds ; DS = TRANGROUP seg addr again
|
||
assume ds:TRANGROUP
|
||
ret
|
||
|
||
DisplayWide endp
|
||
|
||
|
||
|
||
|
||
;*** EndPage - end the current display page
|
||
;
|
||
; ENTRY LeftOnPage = # lines left on display page
|
||
; Current directory (on selected drive) is the one being listed
|
||
; Bits contains /p setting
|
||
;
|
||
; EXIT LeftOnPage = # lines left for next page
|
||
;
|
||
; USED AX,DX
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; Pause is invoked to display a message and wait for a keystroke.
|
||
; BwdBuf (same as DirBuf) used to build directory string.
|
||
|
||
EndPage proc
|
||
|
||
test Bits,mask pagd
|
||
jz epNew ; paged display isn't enabled
|
||
|
||
push bx ; save BX
|
||
push cx ; save CX
|
||
|
||
invoke Pause ; "Press any key to continue..."
|
||
|
||
invoke Build_Dir_String
|
||
mov dx,offset TRANGROUP:DirCont_Ptr
|
||
invoke Printf_Crlf ; "(continuing <dir>)", cr/lf
|
||
|
||
pop cx ; restore CX
|
||
pop bx ; restore BX
|
||
|
||
epNew: mov ax,LinPerPag ; AX = # lines per page
|
||
dec ax ; AX = # lines till next EndPage
|
||
mov LeftOnPage,ax ; LeftOnPage = countdown variable
|
||
|
||
ret
|
||
|
||
EndPage endp
|
||
|
||
|
||
|
||
|
||
;*** GetDriveLtr - get target drive letter
|
||
;
|
||
; ENTRY FCB contains drive #
|
||
;
|
||
; EXIT AX = "d:"
|
||
;
|
||
; USED nothing
|
||
|
||
GetDriveLtr proc
|
||
|
||
mov al,ds:Fcb ; AL = target drive #
|
||
or al,al
|
||
jnz @F ; not current drive default, skip ahead
|
||
mov al,ds:CurDrv ; AL = current drive #
|
||
inc al ; AL = 1-based drive #
|
||
@@: add al,'A'-1 ; AL = target drive letter
|
||
mov ah,':' ; AX = "d:"
|
||
ret
|
||
|
||
GetDriveLtr endp
|
||
|
||
|
||
|
||
|
||
|
||
;*** SetupParamError - set up for Std_EPrintf parameter parse error message
|
||
;
|
||
; Do for our /O and /A string parsers what Parse_With_Msg does
|
||
; for system parser calls. Set up a message substitution block,
|
||
; etc. for invalid value strings. I copied the procedure from
|
||
; Setup_Parse_Error_Msg.
|
||
;
|
||
; ENTRY BX = ptr to system parser result buffer (contains ptr to str)
|
||
;
|
||
;
|
||
; EXIT AX = system parser error return code for bad param format
|
||
; DX = ptr to message description block for Std_EPrintf
|
||
;
|
||
; USED SI
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; Msg_Disp_Class = parse error message class
|
||
; Message block (see DX) is set up for parse error message
|
||
|
||
SetupParamError proc
|
||
|
||
mov ax,9 ; parse error #
|
||
mov Msg_Disp_Class,PARSE_MSG_CLASS
|
||
mov Extend_Buf_Ptr,ax
|
||
mov si,word ptr [bx].ValuePtr
|
||
mov String_Ptr_2,si
|
||
mov Extend_Buf_Sub,ONE_SUBST
|
||
mov dx,offset TRANGROUP:Extend_Buf_Ptr
|
||
ret
|
||
|
||
SetupParamError endp
|
||
|
||
|
||
|
||
|
||
;*** UseLine - use a display line, start a new page if none left
|
||
;
|
||
; ENTRY nothing
|
||
;
|
||
; EXIT nothing
|
||
;
|
||
; USED flags
|
||
|
||
UseLine proc
|
||
|
||
dec LeftOnPage
|
||
ifndef NEC_98
|
||
cmp LeftOnPage,2
|
||
else ;NEC_98
|
||
cmp LeftOnPage,1 ;NEC04 Canged Page Line (23 to 24)
|
||
endif ;NEC_98
|
||
ja ulRet
|
||
call EndPage
|
||
ulRet: ret
|
||
|
||
UseLine endp
|
||
|
||
|
||
|
||
|
||
;*** ZeroTotals - zero grand total file count, size
|
||
;
|
||
; ENTRY nothing
|
||
;
|
||
; EXIT nothing
|
||
;
|
||
; USED AX
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; FileCntTotal & FileSizTotal are zeroed.
|
||
;
|
||
; NOTES
|
||
;
|
||
; FileCntTotal & FileSizTotal must be juxtaposed, in that order.
|
||
|
||
ZeroTotals proc
|
||
|
||
mov di,offset TRANGROUP:FileCntTotal
|
||
mov cx,size FileCntTotal+size FileSizTotal
|
||
xor al,al
|
||
rep stosb
|
||
ret
|
||
|
||
ZeroTotals endp
|
||
|
||
|
||
|
||
|
||
;*** CtrlCHandler - our own control-c handler
|
||
;
|
||
; Make sure user's default directory gets restored. See notes
|
||
; at InstallCtrlCHandler.
|
||
;
|
||
; ENTRY control-c
|
||
;
|
||
; EXIT to OldCtrlCHandler
|
||
;
|
||
; USED DS,flags
|
||
;
|
||
; EFFECTS
|
||
;
|
||
; Restore user's default directory.
|
||
;
|
||
; NOTES
|
||
;
|
||
; This handler is only installed after calling PathCrunch,
|
||
; which sets UserDir1, so the restoration will work.
|
||
;
|
||
; The original control-c vector will be restored, whether
|
||
; or not this one is invoked, in the HeadFix routine.
|
||
|
||
CtrlCHandler proc far
|
||
|
||
;SR;
|
||
; Save all registers used: ds, dx, ax. I know ax is being used by the
|
||
;CtrlC handler, am not sure about ds & dx. Save them to be safe
|
||
;
|
||
push ds
|
||
push cs
|
||
pop ds ; DS = TRANGROUP seg addr
|
||
push ax
|
||
push dx
|
||
invoke RestUDir ; restore user's default directory
|
||
pop dx
|
||
pop ax
|
||
pop ds
|
||
jmp cs:OldCtrlCHandler ; go to previous int 23 handler
|
||
|
||
CtrlCHandler endp
|
||
|
||
|
||
;M010;start
|
||
;*** LowerCase - convert ASCII character in AL to lowercase
|
||
;
|
||
; ENTRY AL = character to be displayed
|
||
;
|
||
; EXIT AL is lowercase
|
||
;
|
||
; USED nothing
|
||
|
||
LowerCase proc
|
||
|
||
assume ds:NOTHING,es:NOTHING
|
||
|
||
cmp al,'A' ; ensure AL is in range 'A'-'Z'
|
||
jb lcRet
|
||
cmp al,'Z'
|
||
ja lcRet
|
||
|
||
or al,20h ; convert to ASCII lowercase (UpperCase+32)-->LowerCase
|
||
|
||
lcRet: ret
|
||
|
||
LowerCase endp
|
||
|
||
|
||
|
||
|
||
;*** LowercaseString - convert ASCIIZ string at DS:SI to lowercase
|
||
;
|
||
; ENTRY DS:SI points to start of ASCIIZ string
|
||
; ES = DS
|
||
;
|
||
; EXIT nothing
|
||
;
|
||
; USED AL,SI
|
||
|
||
LowercaseString proc
|
||
|
||
assume ds:NOTHING,es:NOTHING
|
||
|
||
push di ; save di
|
||
mov di,si ; ES:DI --> ASCIIZ string
|
||
cld
|
||
|
||
NextChar:
|
||
lodsb ; get character from string into al
|
||
or al,al ; are we at end of string?
|
||
jz EndOfString
|
||
|
||
ifdef DBCS
|
||
invoke testkanj
|
||
jz @f ; if this is not lead byte
|
||
stosb ; store lead byte
|
||
lodsb ; get tail byte
|
||
or al,al
|
||
jz EndOfString ; if end
|
||
stosb ; store tail byte
|
||
jmp short NextChar
|
||
@@:
|
||
endif
|
||
|
||
call LowerCase ; convert character to lowercase
|
||
stosb ; store character back into buffer
|
||
jmp SHORT NextChar ; repeat until end of string
|
||
|
||
EndOfString:
|
||
pop di ; restore di
|
||
ret
|
||
|
||
LowercaseString endp
|
||
;M010;end
|
||
|
||
ifdef DBCS
|
||
;
|
||
; Check if the character position is at Tail Byte of DBCS
|
||
;
|
||
; input: ds:si = start address of the string
|
||
; ds:di = character position to check
|
||
; output: ZF = 1 if at Tail Byte
|
||
;
|
||
CheckDBCSTailByte proc near
|
||
push ax
|
||
push cx
|
||
push di
|
||
mov cx,di ; save character position
|
||
cdtb_check:
|
||
cmp di,si
|
||
jz cdtb_next ; if at the top
|
||
dec di ; go back
|
||
mov al,[di] ; get character
|
||
invoke testkanj
|
||
jnz cdtb_check ; if DBCS lead byte do next
|
||
inc di ; adjust
|
||
cdtb_next:
|
||
sub cx,di ; if the length is odd then
|
||
xor cl,1 ; the character position is
|
||
test cl,1 ; at the tail byte
|
||
pop di
|
||
pop cx
|
||
pop ax
|
||
ret
|
||
CheckDBCSTailByte endp
|
||
endif
|
||
|
||
|
||
TRANCODE ends
|
||
|
||
end
|
||
|