2 ; Directory routines for MSDOS
7 CODE SEGMENT BYTE PUBLIC 'CODE'
8 ASSUME
SS:DOSGROUP
,CS:DOSGROUP
17 TITLE DIR
- Directory
and path cracking
34 i_need SecClusPos
,BYTE
36 i_need NxtClusNum
,WORD
39 i_need Device_availability
,BYTE
44 SUBTTL BUILDDIR
,NEWDIR
-- ALLOCATE DIRECTORIES
46 procedure BUILDDIR
,NEAR
47 ASSUME
DS:DOSGROUP
,ES:NOTHING
51 ; [THISFCB] Set if using NEWDIR entry point
52 ; [LASTENT] current last valid entry number in directory if no free
55 ; Grow directory if no free entries and not root
57 ; CARRY SET IF FAILURE
59 ; AX entry number of new entry
60 ; If a new dir [DIRSTART],[CLUSFAC],[CLUSNUM],[DIRSEC] set
61 ; AX = first entry of new dir
62 ; GETENT should be called to set [LASTENT]
70 return
; Can't grow root
96 MOV CL,ES:[BP.dpb_cluster_mask
]
103 MOV CX,ES:[BP.dpb_sector_size
]
116 MOV ES:[DI.BUFDIRTY
],AL
130 ; set up a . and .. directory entry for a directory
132 procedure SETDOTENT
,NEAR
138 MOV SI,WORD PTR [THISFCB
]
139 MOV AL,attr_directory
142 MOV AX,[SI.fcb_FTIME
]
144 MOV AX,[SI.fcb_FDATE
]
154 SUBTTL GETFILE
, GETNAME
, FINDNAME
-- LOOK FOR A
FILE
156 procedure SEARCH
,near
159 ASSUME
DS:NOTHING
,ES:NOTHING
160 ; Same as GETNAME except ES:DI points to FCB on successful return
171 ASSUME
DS:NOTHING
,ES:NOTHING
176 ; Find file name in disk directory. First byte is
177 ; drive number (0=current disk). "?" matches any
180 ; Carry set if file not found
182 ; Zero set if attributes match (always except when creating)
183 ; AH = Device ID (bit 7 set if not disk)
184 ; [THISDPB] = Base of drive parameters
187 ; [CURBUF+2]:BX = Pointer into directory buffer
188 ; [CURBUF+2]:SI = Pointer to First Cluster field in directory entry
189 ; [CURBUF] has directory record with match
190 ; [NAME1] has file name
191 ; All other registers destroyed.
195 retc
; Bad file name?
207 ; NOTE THE FALL THROUGH
209 SUBTTL FINDENTRY
-- LOOK FOR AN
ENTRY
212 ASSUME
DS:DOSGROUP
,ES:NOTHING
217 ; [DIRSEC] = Starting directory sector number
218 ; [CLUSNUM] = Next cluster of directory
219 ; [CLUSFAC] = Sectors/Cluster
220 ; [NAME1] = Name to look for
222 ; Find file name in disk directory.
223 ; "?" matches any character.
225 ; Carry set if name not found
227 ; Zero set if attributes match (always except when creating)
228 ; AH = Device ID (bit 7 set if not disk)
229 ; [THISDPB] = Base of drive parameters
232 ; [CURBUF+2]:BX = Pointer into directory buffer
233 ; [CURBUF+2]:SI = Pointer to First Cluster field in directory entry
234 ; [CURBUF] has directory record with match
235 ; [NAME1] has file name
236 ; [LASTENT] is entry number of the entry
237 ; All other registers destroyed.
240 CMP BYTE PTR [ATTRIB
],attr_volume_id
241 ; Looking for vol ID only ?
243 CALL SETROOTSRCH
; Yes force search of root
248 MOV DS,WORD PTR [CURBUF
+2]
251 OR AH,AH ; End of directory?
253 CMP AH,BYTE PTR [DELALL
] ; Free entry?
255 TEST BYTE PTR [BX+11],attr_volume_id
264 MOV DI,OFFSET DOSGROUP
:NAME1
269 CMP BYTE PTR ES:[DI-1],"?"
288 CMP AH,BYTE PTR [DELALL
] ; At end of directory?
289 JZ NEXTENT
; No - continue search
296 ; We have a file with a matching name. We must now consider
300 ; Volume_ID Is Volume_ID in test?
301 ; Otherwise If no create then Is ATTRIB+extra superset of test?
302 ; If create then Is ATTRIB equal to test?
304 MOV CH,[SI] ; Attributes of file
307 MOV AH,BYTE PTR [ATTRIB
] ; Attributes of search
308 TEST CH,attr_volume_id
; Volume ID file?
309 JZ check_one_volume_id
; Nope check other attributes
310 TEST AH,attr_volume_id
; Can we find Volume ID?
311 JZ NEXTENT
; Nope, (not even $FCB_CREATE)
312 XOR AH,AH ; Set zero flag for $FCB_CREATE
313 JMP SHORT RETF ; Found Volume ID
315 CMP AH,attr_volume_id
; Looking only for Volume ID?
316 JZ NEXTENT
; Yes, continue search
320 TEST BYTE PTR [CREATING
],-1 ; Pass back mismatch if creating
321 JZ NEXTENT
; Otherwise continue searching
324 MOV AH,ES:[BP.dpb_drive
]
330 SUBTTL GETENTRY
, NEXTENTRY
, GETENT
-- STEP THROUGH DIRECTORY
333 ASSUME
DS:DOSGROUP
,ES:NOTHING
336 ; [LASTENT] has directory entry
337 ; ES:BP points to drive parameters
339 ; Locates directory entry in preparation for search
340 ; GETENT provides entry for passing desired entry in AX
341 ; A valid search environment MUST exist
342 ; ENDENT,ENTLAST,ENTFREE
344 ; [CURBUF+2]:BX = Pointer to next directory entry in CURBUF
345 ; [CURBUF+2]:DX = Pointer to first byte after end of CURBUF
346 ; [LASTENT] = New directory entry number
355 RCL DX,1 ; Account for overflow in last shift
356 MOV BX,ES:[BP.dpb_sector_size
]
357 AND BL,255-31 ; Must be multiple of 32
359 MOV BX,DX ; Position within sector
364 MOV DX,WORD PTR [CURBUF
]
367 ADD DX,ES:[BP.dpb_sector_size
] ; Always clears carry
371 ASSUME
DS:DOSGROUP
,ES:NOTHING
374 ; Same as outputs of GETENTRY, above
376 ; Update BX, and [LASTENT] for next directory entry.
377 ; Carry set if no more.
386 MOV BL,BYTE PTR [SECCLUSPOS
]
388 CMP BL,BYTE PTR [CLUSFAC
]
407 MOV BYTE PTR [SECCLUSPOS
],BL
421 SUBTTL GETCURRDIR
-- GET CURRENT DIRECTORY
423 procedure Dir_search
,NEAR
425 ASSUME
DS:NOTHING
,ES:NOTHING
428 ; ES:BP Points to DPB
429 ; FATREAD should be called before this routine
431 ; Find current directory for drive
432 ; If path is bad set current directory to the root
436 ; [DIRSTART] = Cluster # of first cluster of directory ( 0 if root)
437 ; [DIRSEC] Set to phys sec # of first sector first cluster of directory
438 ; [CLUSNUM] Set to next cluster
439 ; [CLUSFAC] Sectors/cluster
440 ; Destroys all registers
442 MOV BX,ES:[BP.dpb_current_dir
]
449 LEA SI,[BP.dpb_dir_text
]
453 MOV ES:[BP.dpb_current_dir
],0
456 ASSUME
DS:NOTHING
,ES:NOTHING
462 MOV BYTE PTR [SECCLUSPOS
],AL
465 MOV AX,ES:[BP.dpb_first_sector
]
466 MOV DX,ES:[BP.dpb_dir_sector
]
468 MOV BYTE PTR [CLUSFAC
],AL
475 MOV ES:[BP.dpb_current_dir
],AX
479 ASSUME
DS:NOTHING
,ES:NOTHING
482 ; BX cluster number of start of directory
483 ; ES:BP Points to DPB
485 ; Set up a directory search
489 ; [CLUSFAC],[CLUSNUM],[SECCLUSPOS],[DIRSEC] set
498 MOV AL,ES:[BP.dpb_cluster_mask
]
500 MOV BYTE PTR [CLUSFAC
],AL
505 MOV BYTE PTR [SECCLUSPOS
],BL
511 SUBTTL MAKENODE
-- CREATE A NEW NODE
513 procedure MakeNode
,NEAR
514 ASSUME
DS:NOTHING
,ES:NOTHING
517 ; AL - attribute to create
518 ; DS:SI Points to asciz path
519 ; [THISFCB] Points to an empty FCB
524 ; ES:BP Points to DPB
526 ; AX = 1 A node by this name exists and is a directory
527 ; AX = 2 A new node could not be created error
528 ; AX = 3 A node by this name exists and is a file error
529 ; AX = 4 Bad Path error
530 ; AX = 5 Attribute mismatch error
533 ; [DIRSTART],[DIRSEC],[CLUSFAC],[CLUSNUM] set to directory
534 ; containing new node.
535 ; [CURBUF+2]:BX Points to entry
536 ; [CURBUF+2]:SI Points to entry.fcb_firclus
537 ; [ThisFCB] is filled in
538 ; If this is a new entry zero is set and
539 ; Attribute byte in entry is directory
540 ; else a file existed by this name and:
542 ; entry is not changed in any way
543 ; Destroys all registers
547 MOV DL,CL ; Save CL info
549 MOV BYTE PTR [ATTRIB
],CL
551 JNC make_exists
; File existed
552 JNZ make_err_4
; Path bad
553 OR DL,DL ; Check "CL" return from GETPATH
554 JNZ make_type
; Name simply not found
556 MOV AL,4 ; case 1 bad path
562 XOR AL,AL ; nothing exists... assume 0
567 MOV AL,3 ; file exists type 3
568 TEST BYTE PTR [ATTRIB
],(attr_volume_id
+attr_directory
)
569 JNZ make_err_ret_5
; but we wanted a volid or dir
571 JS make_dev
; No furthur checks if device
573 MOV DS,WORD PTR [CURBUF
+2]
574 MOV CH,[BX+dir_attr
] ; Get file attributes
575 TEST CH,attr_read_only
576 JNZ make_err_ret_5P
; Cannot create on read only files
580 JZ make_dev
; Attributes ok
582 MOV AL,5 ; Attribute mismatch
583 JMP SHORT make_err_ret
586 XOR AL,AL ; Make sure zero set(atts match), carry clear(exists)
587 MOV AL,3 ; Restore correct value
590 MOV AL,1 ; directory exists
591 TEST BYTE PTR [ATTRIB
],attr_directory
592 JZ make_err_ret
; we didn't want a directory
598 ; set up for call to NewEntry - it is in the middle of FCB_CREATE
599 ; so we must also pre-push two registers. They will be popped off
605 PUSHF ;Save state of flags
606 CMP BYTE PTR [NAME1
],'.' ;Detect attempt to make '.' or '..'
607 JNZ NOTLDOT
; Needed because no '.' or '..' in root
609 MOV AL,1 ;Force type 2 error
627 MOV AL,2 ; create failed case 2
637 MOV DS,WORD PTR [CURBUF
+2]
657 SUBTTL GETPATH
-- PARSE AN asciz PATH
660 procedure GETPATH
,near
661 ASSUME
DS:NOTHING
,ES:NOTHING
664 ; DS:SI Points to asciz path
668 ; [DRIVESPEC] is non zero if a drive was specified
669 ; [ROOTSTART] is non zero if a / started the path
670 ; [ATTRIB] set to attr_directory+attr_hidden+attr_system
671 ; Same as FINDPATH except if path specifies a device in which case
672 ; bit 7 of AH will be set and SI and BX will point DOSGROUP relative
673 ; Destroys all registers
676 MOV WORD PTR [DRIVESPEC
],AX
677 MOV BYTE PTR [ATTRIB
],attr_directory
+attr_system
+attr_hidden
691 CMP BYTE PTR [device_availability
],0
694 JNC BUILDFCBJ
; If no carry then we have a device
726 INC BYTE PTR [ROOTSTART
]
731 PUSH ES ; Save pointer to DPB
743 invoke BUILDFCB
; Clears carry sets zero
750 OR AL,20H
; Convert to lower case
751 SUB AL,60H
; Make A=1
775 XOR AL,AL ; Set zero (directory) clear carry
782 MOV DI,OFFSET DOSGROUP
:DEVSTRING
789 invoke GETLET
; Try convert to upper case
801 MOV DI,OFFSET DOSGROUP
:NAME1
827 SUBTTL ROOTPATH
, FINDPATH
-- PARSE A PATH
829 procedure ROOTPATH
,near
831 ASSUME
DS:NOTHING
,ES:NOTHING
834 ; ES:BP Points to DPB
835 ; FATREAD should be called before this routine
836 ; DS:SI Points to asciz string of path which is assumed to start at
837 ; the root (no leading '/').
839 ; Search from root for path
842 ; Destroys all registers
851 ASSUME
DS:NOTHING
,ES:NOTHING
854 ; ES:BP Points to DPB
855 ; DS:SI Points to asciz string of path (no leading '/').
857 ; [DIRSEC] = Phys sec # of first sector of directory
858 ; [CLUSNUM] = Cluster # of next cluster
859 ; [CLUSFAC] = Sectors per cluster
860 ; Validate_path should be called before this routine is used,
861 ; unless it is KNOWN the path is good.
865 ; ES:BP Points to DPB
866 ; Carry set if bad path
867 ; DS:SI Points to path element causing failure
869 ; [DIRSTART],[DIRSEC],[CLUSNUM], and [CLUSFAC] are set up to
870 ; start a search on the last directory
871 ; CL is zero if there is a bad name in the path
872 ; CL is non-zero if the name was simply not found
873 ; [ENTFREE] may have free spot in directory
874 ; [NAME1] is the name.
875 ; CL = 81H if '*'s or '?' in name 1, 80H otherwise
877 ; File in middle of path or bad name in path
878 ; or path too long or malformed path
882 ; [CURBUF] contains directory record with match
883 ; [CURBUF+2]:BX Points into [CURBUF] to start of entry
884 ; [CURBUF+2]:SI Points to fcb_FIRCLUS field for entry
885 ; [NAME1] Has entry name
886 ; If last element is a directory zero is set and:
887 ; [DIRSTART],[SECCLUSPOS],[DIRSEC],[CLUSNUM], and [CLUSFAC]
888 ; are set up to start a search on it.
889 ; If last element is a file zero is reset
890 ; Destroys all registers
908 ; can we see all devices
911 CMP BYTE PTR [device_availability
],0
915 ; check name1 to see if we have a device...
919 invoke DevName
; blast BX
934 PUSH DI ; Start of this element
944 TEST BYTE PTR [BX+dir_attr
],attr_directory
948 ; if we are not setting the directory, then
949 ; check for end of string
951 CMP BYTE PTR [NoSetDir
],0
983 MOV DI,WORD PTR [CURBUF
]
1001 XOR CL,CL ; Set zero
1011 MOV SI,DI ; Path too long
1026 MOV SI,DI ; Start of bad element
1027 OR AL,AL ; zero if bad element is last, non-zero if path too long
1032 SUBTTL STARTSRCH
-- INITIATE DIRECTORY SEARCH
1034 procedure StartSrch
,NEAR
1035 ASSUME
DS:DOSGROUP
,ES:NOTHING
1040 ; Set up a search for GETENTRY and NEXTENTRY
1042 ; ES:BP = Drive parameters
1043 ; Sets up LASTENT, ENDENT, ENTFREE=ENTLAST=-1, VOLID=0
1044 ; Destroys all registers (via FATREAD)
1049 MOV BYTE PTR [VOLID
],AL ; No volume ID found
1056 BREAK <MatchAttributes
- the final check for attribute matching
>
1059 ; Input: [Attrib] = attribute to search for
1060 ; CH = found attribute
1061 ; Output: JZ <match>
1064 procedure MatchAttributes
,near
1065 ASSUME
DS:NOTHING
,ES:NOTHING
1067 MOV AL,[Attrib
] ; AL <- SearchSet
1068 NOT AL ; AL <- SearchSet'
1069 AND AL,CH ; AL <- SearchSet' and FoundSet
1070 AND AL,attr_all
; AL <- SearchSet' and FoundSet and Important
1072 ; the result is non-zero if an attribute is not in the search set
1073 ; and in the found set and in the important set. This means that we do not
1074 ; have a match. Do a JNZ <nomatch> or JZ <match>
1078 MatchAttributes ENDP