Windows2003-3790/base/mvdm/dos/v86/cmd/command/dir.asm
2020-09-30 16:53:55 +02:00

3472 lines
85 KiB
NASM
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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