Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

710 lines
18 KiB

  1. page ,132
  2. ; SCCSID = @(#)tenv2.asm 1.1 85/05/14
  3. ; SCCSID = @(#)tenv2.asm 1.1 85/05/14
  4. TITLE Part6 COMMAND Transient routines.
  5. ;/*
  6. ; * Microsoft Confidential
  7. ; * Copyright (C) Microsoft Corporation 1991
  8. ; * All Rights Reserved.
  9. ; */
  10. ; Environment utilities and misc. routines
  11. .xlist
  12. .xcref
  13. include comsw.asm
  14. include dossym.inc
  15. include syscall.inc
  16. include find.inc
  17. include comseg.asm
  18. include comequ.asm
  19. .list
  20. .cref
  21. DATARES SEGMENT PUBLIC BYTE ;AC000;
  22. EXTRN pipeflag:byte
  23. DATARES ENDS
  24. TRANDATA SEGMENT PUBLIC BYTE ;AC000;
  25. EXTRN ACRLF_PTR:WORD
  26. EXTRN BadCD_Ptr:WORD
  27. EXTRN Badmkd_ptr:word
  28. EXTRN BADRMD_PTR:WORD
  29. EXTRN Extend_buf_ptr:word ;AN000;
  30. EXTRN Extend_buf_sub:byte ;AN022;
  31. EXTRN MD_exists_ptr:word ;AN006;
  32. EXTRN msg_disp_class:byte ;AC000;
  33. EXTRN NOSPACE_PTR:WORD
  34. EXTRN parse_chdir:byte ;AC000;
  35. EXTRN parse_mrdir:byte ;AC000;
  36. EXTRN PIPEEMES_PTR:WORD
  37. EXTRN string_buf_ptr:word
  38. TRANDATA ENDS
  39. TRANSPACE SEGMENT PUBLIC BYTE ;AC000;
  40. EXTRN CURDRV:BYTE
  41. EXTRN DESTINFO:BYTE
  42. EXTRN DESTTAIL:WORD
  43. EXTRN DIRCHAR:BYTE
  44. EXTRN dirflag:byte ;AN015;
  45. EXTRN KPARSE:BYTE ;AC000; 3/3/KK
  46. EXTRN msg_numb:word ;AN022;
  47. EXTRN parse1_addr:dword ;AC000;
  48. EXTRN parse1_type:byte ;AC000;
  49. EXTRN PATHPOS:WORD
  50. EXTRN RESSEG:WORD
  51. EXTRN srcxname:byte ;AC000;
  52. EXTRN string_ptr_2:word
  53. EXTRN SWITCHAR:BYTE
  54. EXTRN USERDIR1:BYTE
  55. TRANSPACE ENDS
  56. TRANCODE SEGMENT PUBLIC byte
  57. ASSUME CS:TRANGROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING
  58. EXTRN cerror:near
  59. PUBLIC $chdir
  60. PUBLIC $mkdir
  61. PUBLIC $rmdir
  62. PUBLIC crlf2
  63. PUBLIC crprint
  64. PUBLIC delim
  65. PUBLIC error_output
  66. PUBLIC fcb_to_ascz
  67. PUBLIC pathchrcmp
  68. PUBLIC pathcrunch
  69. PUBLIC savudir
  70. PUBLIC savudir1
  71. PUBLIC scanoff
  72. PUBLIC strcomp
  73. break $Chdir
  74. ; ****************************************************************
  75. ; *
  76. ; * ROUTINE: $CHDIR
  77. ; *
  78. ; * FUNCTION: Entry point for CHDIR command. Parse the command
  79. ; * line. If path is found, CHDIR to path. If a drive
  80. ; * letter is found, get and display the current dir
  81. ; * of the specified drive. If nothing is found, get
  82. ; * and display the current dir of the default drive.
  83. ; *
  84. ; * INPUT: command line at offset 81H
  85. ; *
  86. ; * OUTPUT: none
  87. ; *
  88. ; ****************************************************************
  89. assume ds:trangroup,es:trangroup
  90. $CHDIR:
  91. mov si,81H
  92. mov di,offset trangroup:parse_chdir ;AN000; Get adderss of PARSE_CHDIR
  93. xor cx,cx ;AN000; clear cx,dx
  94. xor dx,dx ;AN000;
  95. invoke parse_with_msg ;AC018; call parser
  96. cmp ax,end_of_line ;AC000; are we at end of line?
  97. jz bwdJ ; No args
  98. cmp ax,result_no_error ;AC000; did we have an error?
  99. jnz ChDirErr ;AC018; yes - exit
  100. cmp parse1_type,result_drive ;AC000; was a drive entered?
  101. jnz REALCD ; no
  102. ;
  103. ; D: was found. See if there is anything more.
  104. ;
  105. mov di,offset trangroup:parse_chdir ;AC000; get address of parse_chdir
  106. xor dx,dx ;AC000;
  107. invoke parse_check_eol ;AC000; call parser
  108. jnz ChDirErr ;AC000;
  109. bwdJ:
  110. invoke build_dir_for_chdir ; Drive only specified
  111. call crlf2
  112. return
  113. REALCD:
  114. push si ;AN000; save position in line
  115. lds si,parse1_addr ;AN000; get address of filespec
  116. invoke move_to_srcbuf ;AN000; move to srcbuf
  117. pop si ;AN000; restore position in line
  118. mov di,offset trangroup:parse_chdir ;AC000; get address of parse_chdir
  119. xor dx,dx ;AC000;
  120. invoke parse_check_eol ;AC000; call parser
  121. jnz ChDirErr ;AC000;
  122. invoke SETPATH
  123. TEST [DESTINFO],2
  124. JNZ BadChdir
  125. MOV AH,CHDIR
  126. INT 21h
  127. retnc
  128. invoke get_ext_error_number ;AN022; get the extended error
  129. cmp ax,error_path_not_found ;AN022; see if path not found
  130. jz BadChDir ;AN022; yes - issue old message
  131. ;SR;
  132. ; We want to issue "Invalid Directory" message even if the path is valid
  133. ;but is not a directory. The extended error returns "Access denied" which
  134. ;is kind of confusing. Issue the old message if access denied error is
  135. ;returned
  136. ;
  137. cmp ax,error_access_denied
  138. jz BadChDir
  139. call Set_Ext_Error_Subst ;AN022;
  140. jmp short chdirerr ;AN022;
  141. BadChDir:
  142. MOV DX,OFFSET TRANGROUP:BADCD_ptr
  143. ChDirErr:
  144. invoke Std_Eprintf
  145. return
  146. break $Mkdir
  147. assume ds:trangroup,es:trangroup
  148. $MKDIR:
  149. CALL SETRMMK
  150. JC MkDirErr
  151. MOV AH,MKDIR
  152. INT 21h
  153. retnc
  154. invoke get_ext_error_number ;AN022; get the extended error
  155. cmp ax,error_path_not_found ;AN022; see if path not found
  156. jz MD_other_err ;AN022; yes - issue old message
  157. cmp ax,error_access_denied ;AN022; access denied?
  158. jz badmderr ;AN022; yes - see if file exists
  159. call Set_Ext_Error_Subst ;AN022;
  160. jmp short MkDirerr ;AC022; yes - go print it
  161. BADMDERR:
  162. mov dx,offset trangroup:srcxname ;AN006; Set Disk transfer address
  163. mov ah,Set_DMA ;AN006;
  164. int 21h ;AN006;
  165. MOV AH,Find_First ;AN006; see if file/dir exists
  166. mov cx,attr_directory ;AN006; search for directory
  167. INT 21h ;AN006;
  168. jc MD_other_err ;AN006; doesn't exist - must be something else
  169. mov dl,srcxname.find_buf_attr ;AN006; we found a file/dir
  170. test dl,attr_directory ;AN006; was it a directory?
  171. jz MD_other_err ;AN006; no - must have been a file
  172. mov dx,offset trangroup:MD_exists_ptr ;AN006; set up already exists error
  173. jmp short MkDirErr ;AN006; make sure we didn't have network error
  174. MD_other_err: ;AN006;
  175. MOV DX,OFFSET TRANGROUP:BADMKD_ptr
  176. MkDirErr:
  177. invoke Std_Eprintf
  178. return
  179. Break <Common MkDir/RmDir set up code>
  180. ;****************************************************************
  181. ;*
  182. ;* ROUTINE: SETRMMK
  183. ;*
  184. ;* FUNCTION: Parse routine for the internal MKDIR and RMDIR
  185. ;* commands. Parses the command line for a required
  186. ;* filespec.
  187. ;*
  188. ;* INPUT: command line at offset 81H
  189. ;*
  190. ;* OUTPUT: carry clear
  191. ;* DS:DX points to ASCIIZ argument
  192. ;* carry set
  193. ;* DS:DX has error message pointer
  194. ;*
  195. ;****************************************************************
  196. SETRMMK:
  197. mov si,81H
  198. mov di,offset trangroup:parse_mrdir ;AN000; Get adderss of PARSE_MRDIR
  199. xor cx,cx ;AN000; clear cx,dx
  200. xor dx,dx ;AN000;
  201. invoke parse_with_msg ;AC000; call parser
  202. cmp ax,result_no_error ;AC000; did we have an error?
  203. jnz NOARGERR ;AC000; yes - exit
  204. mov di,offset trangroup:srcxname ;AN000; get address of srcxname
  205. push di ;AN000; save address
  206. push si ;AN000; save position in line
  207. lds si,parse1_addr ;AN000; get address of path
  208. mrdir_move_filename: ;AN000; put filespec in srcxname
  209. lodsb ;get a char from buffer
  210. stosb ;AN000; store in srcxname
  211. cmp al,end_of_line_out ;AC000; it char a terminator?
  212. jnz mrdir_move_filename ;AC000; no - keep moving
  213. pop si ;AN000; get line position back
  214. ;
  215. ; we have scanned an argument. See if any args beyond.
  216. ;
  217. mov di,offset trangroup:parse_mrdir ;AC000; get address of parse_mrdir
  218. invoke parse_check_eol ;AC000; are we at end of line?
  219. pop dx ;AC000; get address of SRCXNAME
  220. retz ;yes - return no error
  221. NOARGERR:
  222. mov dx,offset TranGroup:Extend_Buf_ptr ;AC000; get extended message pointer
  223. XOR AX,AX
  224. STC
  225. return
  226. break $Rmdir
  227. assume ds:trangroup,es:trangroup
  228. $RMDIR:
  229. CALL SETRMMK
  230. JC RmDirErr
  231. JNZ BADRDERR
  232. MOV AH,RMDIR
  233. INT 21h
  234. retnc
  235. invoke get_ext_error_number ;AN022; get the extended error
  236. cmp ax,error_path_not_found ;AN022; see if path not found
  237. jz badrderr ;AN022; yes - issue old message
  238. cmp ax,error_access_denied ;AN022; access denied?
  239. jz badrderr ;AN022; yes - issue old message
  240. call Set_Ext_Error_Subst ;AN022;
  241. jmp short RmDirerr ;AC022; yes - go print it
  242. BADRDERR:
  243. MOV DX,OFFSET TRANGROUP:BADRMD_ptr
  244. RmDirErr:
  245. invoke STD_Eprintf
  246. return
  247. ;****************************************************************
  248. ;*
  249. ;* ROUTINE: Set_ext_error_subst
  250. ;*
  251. ;* FUNCTION: Sets up substitution for extended error
  252. ;*
  253. ;* INPUT: AX - extended error number
  254. ;* DX - offset of string
  255. ;*
  256. ;* OUTPUT: Extend_Buf_Ptr set up for STD_EPRINTF
  257. ;*
  258. ;****************************************************************
  259. Set_ext_error_subst proc near ;AN022;
  260. mov msg_disp_class,ext_msg_class ;AN022; set up extended error msg class
  261. mov string_ptr_2,dx ;AN022; get address of failed string
  262. mov Extend_buf_sub,one_subst ;AN022; put number of subst in control block
  263. mov dx,offset TranGroup:Extend_Buf_ptr ;AN022; get extended message pointer
  264. mov Extend_Buf_ptr,ax ;AN022; get message number in control block
  265. ret ;AN022; return
  266. Set_ext_error_subst endp ;AN022;
  267. Break <SavUDir - preserve the users current directory on a particular drive>
  268. ;
  269. ; SavUDir - move the user's current directory on a drive into UserDir1
  270. ; SavUDir1 - move the user's current directory on a drive into a specified
  271. ; buffer
  272. ;
  273. ; Inputs: DL has 1-based drive number
  274. ; ES:DI has destination buffer (SavUDir1 only)
  275. ; Outputs: Carry Clear
  276. ; DS = TranGroup
  277. ; Carry Set
  278. ; AX has error code
  279. ; Registers Modified: AX, SI
  280. ;
  281. SAVUDIR:
  282. MOV DI,OFFSET TRANGROUP:USERDIR1
  283. SAVUDIR1:
  284. MOV AL,DL
  285. ADD AL,'@'
  286. CMP AL,'@'
  287. JNZ GOTUDRV
  288. ADD AL,[CURDRV]
  289. INC AL ; A = 1
  290. GOTUDRV:
  291. STOSB
  292. MOV AH,[DIRCHAR]
  293. MOV AL,':'
  294. STOSW
  295. PUSH ES
  296. POP DS
  297. ASSUME DS:NOTHING
  298. MOV SI,DI
  299. MOV AH,CURRENT_DIR ; Get the Directory Text
  300. INT 21h
  301. retc
  302. PUSH CS
  303. POP DS
  304. ASSUME DS:TRANGROUP
  305. return
  306. CRLF2:
  307. PUSH DX
  308. MOV DX,OFFSET TRANGROUP:ACRLF_ptr
  309. PR:
  310. PUSH DS
  311. PUSH CS
  312. POP DS
  313. invoke std_printf
  314. POP DS
  315. POP DX
  316. return
  317. ;
  318. ; These routines (SCANOFF, DELIM) are called in batch processing when DS
  319. ; may NOT be TRANGROUP
  320. ;
  321. ASSUME DS:NOTHING,ES:NOTHING
  322. SCANOFF:
  323. LODSB
  324. CALL DELIM
  325. JZ SCANOFF
  326. DEC SI ; Point to first non-delimiter
  327. return
  328. ;
  329. ; Input: AL is character to classify
  330. ; Output: Z set if delimiter
  331. ; NZ set otherwise
  332. ; Registers modified: none
  333. ;
  334. DELIM:
  335. CMP AL,' '
  336. retz
  337. CMP AL,'='
  338. retz
  339. CMP AL,','
  340. retz
  341. CMP AL,';'
  342. retz
  343. CMP AL,9 ; Check for TAB character
  344. retz
  345. CMP AL,0ah ; Check for line feed character - BAS
  346. return
  347. ASSUME DS:TRANGROUP,ES:TRANGROUP
  348. FCB_TO_ASCZ: ; Convert DS:SI to ASCIZ ES:DI
  349. MOV CX,8
  350. MAINNAME:
  351. LODSB
  352. CMP AL,' '
  353. JZ SKIPSPC
  354. STOSB
  355. SKIPSPC:
  356. LOOP MAINNAME
  357. LODSB
  358. CMP AL,' '
  359. JZ GOTNAME
  360. MOV AH,AL
  361. MOV AL,dot_chr
  362. STOSB
  363. XCHG AL,AH
  364. STOSB
  365. MOV CL,2
  366. EXTNAME:
  367. LODSB
  368. CMP AL,' '
  369. JZ GOTNAME
  370. STOSB
  371. LOOP EXTNAME
  372. GOTNAME:
  373. XOR AL,AL
  374. STOSB
  375. return
  376. STRCOMP:
  377. ;
  378. ; Compare ASCIZ DS:SI with ES:DI.
  379. ; SI,DI destroyed.
  380. ;
  381. CMPSB
  382. retnz ; Strings not equal
  383. cmp byte ptr [SI-1],0 ; Hit NUL terminator?
  384. retz ; Yes, strings equal
  385. jmp short STRCOMP ; Equal so far, keep going
  386. CRPRINT:
  387. PUSH AX
  388. MOV AL,13
  389. PUSH CX
  390. PUSH DI
  391. MOV DI,DX
  392. MOV CX,-1
  393. PUSH ES
  394. PUSH DS
  395. POP ES
  396. REPNZ SCASB ; LOOK FOR TERMINATOR
  397. mov byte ptr [di-1],0 ; nul terminate the string
  398. POP ES
  399. mov string_ptr_2,dx
  400. mov dx,offset trangroup:string_buf_ptr
  401. invoke std_printf
  402. mov ds:byte ptr [di-1],13 ; now put the CR back
  403. JC ERROR_OUTPUT
  404. POP DI
  405. POP CX
  406. POP AX
  407. return
  408. ERROR_OUTPUT:
  409. PUSH CS
  410. POP DS
  411. ASSUME DS:TRANGROUP
  412. MOV ES,[RESSEG]
  413. ASSUME ES:RESGROUP
  414. MOV DX,OFFSET TRANGROUP:NOSPACE_ptr
  415. CMP [PIPEFLAG],0
  416. JZ GO_TO_ERROR
  417. invoke PipeOff
  418. MOV DX,OFFSET TRANGROUP:PIPEEMES_ptr
  419. GO_TO_ERROR:
  420. JMP CERROR
  421. ASSUME DS:TRANGROUP,ES:TRANGROUP
  422. PATHCHRCMP:
  423. ;---- Mod for path invocation ----
  424. PUBLIC pathchrcmp
  425. ;----
  426. push ax
  427. mov ah,'/'
  428. CMP [SWITCHAR],ah
  429. JZ NOSLASHT
  430. CMP AL,'/'
  431. jz pccont
  432. NOSLASHT:
  433. CMP AL,'\'
  434. pccont:
  435. pop ax
  436. return
  437. ;
  438. ; PATHCRUNCH -
  439. ;
  440. ; ENTRY FCB (in PSP) contains drive # to crunch on
  441. ; PathPos = ptr to string with pathname in it
  442. ; PathCnt = length of string
  443. ;
  444. ; EXIT PathPos = ptr after pathname (w/ NULL) in string
  445. ; PathCnt = length left in string
  446. ; DestIsDir = nonzero if pathname delimiter char's found in pathname
  447. ; DestInfo<bit1> = set if wildcard char's found in pathname
  448. ; If path crunched successfully,
  449. ; CY = clear
  450. ; Current directory is changed to directory in pathname
  451. ; UserDir1 contains previous directory for use by RestUDir
  452. ; RestDir = nonzero to flag later restoration of user's dir
  453. ; DestTail = ptr to beginning of filename
  454. ; If filename found in pathname,
  455. ; ZR = clear
  456. ; FCB filename fields contain filename
  457. ; If filename not found (pure directory path),
  458. ; ZR = set
  459. ; FCB filename fields are wildcarded with ?'s
  460. ; If pathcrunch failed (no ChDir's worked),
  461. ; CY = set
  462. ; Msg_Numb = extended error code
  463. ;
  464. ; NOTE DIR asks PathCrunch to forego parsing the filename into the
  465. ; FCB by setting DirFlag. In this case, the FCB is returned
  466. ; with the filename wildcarded.
  467. ;
  468. PATHCRUNCH:
  469. mov [msg_numb],0 ;AN022; Set up message flag
  470. MOV DL,DS:[FCB] ; DL = drive # (1 = A)
  471. CALL SAVUDIR ; save current directory in UserDir1
  472. jc pcrunch_cderrJ ;AN022; if error on current dir - report
  473. invoke SETPATH ; scan past switches, whitespace
  474. ; DX = ptr to pathname, NULL-terminated
  475. ; PathPos = ptr to byte after NULL at end of pathname
  476. TEST [DESTINFO],2 ; test if wildcards (? or *) seen
  477. JNZ TRYPEEL ; wildcard seen, peel filename
  478. MOV AH,CHDIR ; AH = DOS ChDir function code
  479. INT 21h ; call DOS
  480. jnc chdir_worked ;AN022; no error - continue
  481. invoke get_ext_error_number ;AN022; get the extended error
  482. cmp ax,error_path_not_found ;AN022; if path not found
  483. jz trypeel ;AC022; keep trying
  484. cmp ax,error_access_denied ;AN022; if access denied
  485. jz trypeel ;AC022; keep trying
  486. mov [msg_numb],ax ;AN022; set up message flag
  487. jmp peelfail ;AN022; exit with other error
  488. chdir_worked:
  489. invoke SETREST1 ; set 'Restore Directory' flag true
  490. MOV AL,'?' ; if pure dir, wildcard filename in FCB
  491. MOV DI,5DH
  492. MOV CX,11
  493. REP STOSB
  494. XOR AL,AL ; return carry clear, zero set
  495. return
  496. pcrunch_cderrj: ;AN022; need this for long jmp
  497. jmp pcrunch_cderr ;AN022;
  498. TRYPEEL:
  499. MOV SI,[PATHPOS]
  500. DEC SI ; SI = ptr to NULL at end of pathname
  501. MOV AL,[SI-1] ; AL = last char of pathname
  502. CMP [KPARSE],0
  503. JNZ DELSTRT ; Last char is second KANJI byte, might be '\'
  504. CALL PATHCHRCMP
  505. JZ PEELFAIL ; Trailing '/'
  506. DELSTRT:
  507. MOV CX,SI ; CX = ptr to NULL at end of pathname
  508. MOV SI,DX ; SI = ptr to start of pathname
  509. PUSH DX ; save ptr to pathname
  510. DELLOOP:
  511. CMP SI,CX
  512. JZ GOTDELE ; no char's left, we have what we have
  513. LODSB ; AL = next char of pathname
  514. invoke TESTKANJ
  515. JZ NOTKANJ8 ; not Kanji, move along
  516. INC SI
  517. JMP DELLOOP
  518. NOTKANJ8:
  519. CALL PATHCHRCMP
  520. JNZ DELLOOP ; not a path delimiter, keep looking
  521. MOV DX,SI
  522. DEC DX ; DX = ptr to last delimiter found
  523. JMP DELLOOP ; go look for more
  524. GOTDELE:
  525. MOV SI,DX ; SI = ptr to pathname or last delim
  526. POP DX ; DX = ptr to pathname
  527. CMP SI,DX
  528. JZ BADRET ; didn't find path delim
  529. MOV CX,SI ; CX = ptr to last path delimiter
  530. MOV SI,DX ; SI = ptr to pathname
  531. DELLOOP2: ; Set value of KPARSE
  532. CMP SI,CX
  533. JZ TRYCD ; roll up till SI meets CX
  534. MOV [KPARSE],0
  535. LODSB
  536. INVOKE TESTKANJ
  537. JZ DELLOOP2
  538. INC SI
  539. INC [KPARSE]
  540. JMP DELLOOP2
  541. TRYCD:
  542. push ax
  543. mov al,dot_chr ; AL = '.'
  544. CMP BYTE PTR [SI+1],al ; check for '.' after path delim
  545. ;M019; allow continuation if '. ' or
  546. ;M019; '..' is not found.
  547. jnz @F ;M019; '.' not found
  548. cmp BYTE PTR [SI+2],al ;M019; check for '..'
  549. jz @F ;M019; found '..'
  550. cmp BYTE PTR [SI+2],0 ;M019; check for '. ' (null terminated)
  551. @@: pop ax
  552. JZ PEELFAIL ; if . or .., pure cd should have worked
  553. mov al,[si-1]
  554. CMP al,':' ; Special case d:\file
  555. JZ BADRET
  556. CMP [KPARSE],0
  557. JNZ NOTDOUBLESL ; Last char is second KANJI byte, might be '\'
  558. CALL PATHCHRCMP
  559. JNZ NOTDOUBLESL
  560. PEELFAIL:
  561. STC ; //
  562. return
  563. NOTDOUBLESL:
  564. MOV BYTE PTR [SI],0
  565. MOV AH,CHDIR
  566. INT 21h
  567. JNC CDSUCC
  568. pcrunch_cderr:
  569. invoke get_ext_error_number ;AN022; get the extended error
  570. mov [msg_numb],ax ;AN022; set up message flag
  571. or si,si ;AN022; set up zero flag to not zero
  572. stc ;AN022; set up carry flag
  573. return
  574. BADRET:
  575. MOV AL,[SI]
  576. CALL PATHCHRCMP ; Special case 'DIRCHAR'file
  577. STC
  578. retnz
  579. XOR BL,BL
  580. XCHG BL,[SI+1]
  581. MOV AH,CHDIR
  582. INT 21h
  583. jc pcrunch_cderr ;AN022; go to error exit
  584. MOV [SI+1],BL
  585. CDSUCC:
  586. invoke SETREST1
  587. INC SI ; Reset zero
  588. MOV [DESTTAIL],SI
  589. pushf ;AN015; save flags
  590. cmp dirflag,-1 ;AN015; don't do parse if in DIR
  591. jz pcrunch_end ;AN015;
  592. MOV DI,FCB
  593. MOV AX,(PARSE_FILE_DESCRIPTOR SHL 8) OR 02H ; Parse with default drive
  594. INT 21h
  595. pcrunch_end:
  596. popf ;AN015; get flags back
  597. return
  598. trancode ends
  599. end
  600.