page ,132 ; SCCSID = @(#)tenv2.asm 1.1 85/05/14 ; SCCSID = @(#)tenv2.asm 1.1 85/05/14 TITLE Part6 COMMAND Transient routines. ;/* ; * Microsoft Confidential ; * Copyright (C) Microsoft Corporation 1991 ; * All Rights Reserved. ; */ ; Environment utilities and misc. routines .xlist .xcref include comsw.asm include dossym.inc include syscall.inc include find.inc include comseg.asm include comequ.asm .list .cref DATARES SEGMENT PUBLIC BYTE ;AC000; EXTRN pipeflag:byte DATARES ENDS TRANDATA SEGMENT PUBLIC BYTE ;AC000; EXTRN ACRLF_PTR:WORD EXTRN BadCD_Ptr:WORD EXTRN Badmkd_ptr:word EXTRN BADRMD_PTR:WORD EXTRN Extend_buf_ptr:word ;AN000; EXTRN Extend_buf_sub:byte ;AN022; EXTRN MD_exists_ptr:word ;AN006; EXTRN msg_disp_class:byte ;AC000; EXTRN NOSPACE_PTR:WORD EXTRN parse_chdir:byte ;AC000; EXTRN parse_mrdir:byte ;AC000; EXTRN PIPEEMES_PTR:WORD EXTRN string_buf_ptr:word TRANDATA ENDS TRANSPACE SEGMENT PUBLIC BYTE ;AC000; EXTRN CURDRV:BYTE EXTRN DESTINFO:BYTE EXTRN DESTTAIL:WORD EXTRN DIRCHAR:BYTE EXTRN dirflag:byte ;AN015; EXTRN KPARSE:BYTE ;AC000; 3/3/KK EXTRN msg_numb:word ;AN022; EXTRN parse1_addr:dword ;AC000; EXTRN parse1_type:byte ;AC000; EXTRN PATHPOS:WORD EXTRN RESSEG:WORD EXTRN srcxname:byte ;AC000; EXTRN string_ptr_2:word EXTRN SWITCHAR:BYTE EXTRN USERDIR1:BYTE TRANSPACE ENDS TRANCODE SEGMENT PUBLIC byte ASSUME CS:TRANGROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING EXTRN cerror:near PUBLIC $chdir PUBLIC $mkdir PUBLIC $rmdir PUBLIC crlf2 PUBLIC crprint PUBLIC delim PUBLIC error_output PUBLIC fcb_to_ascz PUBLIC pathchrcmp PUBLIC pathcrunch PUBLIC savudir PUBLIC savudir1 PUBLIC scanoff PUBLIC strcomp break $Chdir ; **************************************************************** ; * ; * ROUTINE: $CHDIR ; * ; * FUNCTION: Entry point for CHDIR command. Parse the command ; * line. If path is found, CHDIR to path. If a drive ; * letter is found, get and display the current dir ; * of the specified drive. If nothing is found, get ; * and display the current dir of the default drive. ; * ; * INPUT: command line at offset 81H ; * ; * OUTPUT: none ; * ; **************************************************************** assume ds:trangroup,es:trangroup $CHDIR: mov si,81H mov di,offset trangroup:parse_chdir ;AN000; Get adderss of PARSE_CHDIR xor cx,cx ;AN000; clear cx,dx xor dx,dx ;AN000; invoke parse_with_msg ;AC018; call parser cmp ax,end_of_line ;AC000; are we at end of line? jz bwdJ ; No args cmp ax,result_no_error ;AC000; did we have an error? jnz ChDirErr ;AC018; yes - exit cmp parse1_type,result_drive ;AC000; was a drive entered? jnz REALCD ; no ; ; D: was found. See if there is anything more. ; mov di,offset trangroup:parse_chdir ;AC000; get address of parse_chdir xor dx,dx ;AC000; invoke parse_check_eol ;AC000; call parser jnz ChDirErr ;AC000; bwdJ: invoke build_dir_for_chdir ; Drive only specified call crlf2 return REALCD: push si ;AN000; save position in line lds si,parse1_addr ;AN000; get address of filespec invoke move_to_srcbuf ;AN000; move to srcbuf pop si ;AN000; restore position in line mov di,offset trangroup:parse_chdir ;AC000; get address of parse_chdir xor dx,dx ;AC000; invoke parse_check_eol ;AC000; call parser jnz ChDirErr ;AC000; invoke SETPATH TEST [DESTINFO],2 JNZ BadChdir MOV AH,CHDIR INT 21h retnc invoke get_ext_error_number ;AN022; get the extended error cmp ax,error_path_not_found ;AN022; see if path not found jz BadChDir ;AN022; yes - issue old message ;SR; ; We want to issue "Invalid Directory" message even if the path is valid ;but is not a directory. The extended error returns "Access denied" which ;is kind of confusing. Issue the old message if access denied error is ;returned ; cmp ax,error_access_denied jz BadChDir call Set_Ext_Error_Subst ;AN022; jmp short chdirerr ;AN022; BadChDir: MOV DX,OFFSET TRANGROUP:BADCD_ptr ChDirErr: invoke Std_Eprintf return break $Mkdir assume ds:trangroup,es:trangroup $MKDIR: CALL SETRMMK JC MkDirErr MOV AH,MKDIR INT 21h retnc invoke get_ext_error_number ;AN022; get the extended error cmp ax,error_path_not_found ;AN022; see if path not found jz MD_other_err ;AN022; yes - issue old message cmp ax,error_access_denied ;AN022; access denied? jz badmderr ;AN022; yes - see if file exists call Set_Ext_Error_Subst ;AN022; jmp short MkDirerr ;AC022; yes - go print it BADMDERR: mov dx,offset trangroup:srcxname ;AN006; Set Disk transfer address mov ah,Set_DMA ;AN006; int 21h ;AN006; MOV AH,Find_First ;AN006; see if file/dir exists mov cx,attr_directory ;AN006; search for directory INT 21h ;AN006; jc MD_other_err ;AN006; doesn't exist - must be something else mov dl,srcxname.find_buf_attr ;AN006; we found a file/dir test dl,attr_directory ;AN006; was it a directory? jz MD_other_err ;AN006; no - must have been a file mov dx,offset trangroup:MD_exists_ptr ;AN006; set up already exists error jmp short MkDirErr ;AN006; make sure we didn't have network error MD_other_err: ;AN006; MOV DX,OFFSET TRANGROUP:BADMKD_ptr MkDirErr: invoke Std_Eprintf return Break ;**************************************************************** ;* ;* ROUTINE: SETRMMK ;* ;* FUNCTION: Parse routine for the internal MKDIR and RMDIR ;* commands. Parses the command line for a required ;* filespec. ;* ;* INPUT: command line at offset 81H ;* ;* OUTPUT: carry clear ;* DS:DX points to ASCIIZ argument ;* carry set ;* DS:DX has error message pointer ;* ;**************************************************************** SETRMMK: mov si,81H mov di,offset trangroup:parse_mrdir ;AN000; Get adderss of PARSE_MRDIR xor cx,cx ;AN000; clear cx,dx xor dx,dx ;AN000; invoke parse_with_msg ;AC000; call parser cmp ax,result_no_error ;AC000; did we have an error? jnz NOARGERR ;AC000; yes - exit mov di,offset trangroup:srcxname ;AN000; get address of srcxname push di ;AN000; save address push si ;AN000; save position in line lds si,parse1_addr ;AN000; get address of path mrdir_move_filename: ;AN000; put filespec in srcxname lodsb ;get a char from buffer stosb ;AN000; store in srcxname cmp al,end_of_line_out ;AC000; it char a terminator? jnz mrdir_move_filename ;AC000; no - keep moving pop si ;AN000; get line position back ; ; we have scanned an argument. See if any args beyond. ; mov di,offset trangroup:parse_mrdir ;AC000; get address of parse_mrdir invoke parse_check_eol ;AC000; are we at end of line? pop dx ;AC000; get address of SRCXNAME retz ;yes - return no error NOARGERR: mov dx,offset TranGroup:Extend_Buf_ptr ;AC000; get extended message pointer XOR AX,AX STC return break $Rmdir assume ds:trangroup,es:trangroup $RMDIR: CALL SETRMMK JC RmDirErr JNZ BADRDERR MOV AH,RMDIR INT 21h retnc invoke get_ext_error_number ;AN022; get the extended error cmp ax,error_path_not_found ;AN022; see if path not found jz badrderr ;AN022; yes - issue old message cmp ax,error_access_denied ;AN022; access denied? jz badrderr ;AN022; yes - issue old message call Set_Ext_Error_Subst ;AN022; jmp short RmDirerr ;AC022; yes - go print it BADRDERR: MOV DX,OFFSET TRANGROUP:BADRMD_ptr RmDirErr: invoke STD_Eprintf return ;**************************************************************** ;* ;* ROUTINE: Set_ext_error_subst ;* ;* FUNCTION: Sets up substitution for extended error ;* ;* INPUT: AX - extended error number ;* DX - offset of string ;* ;* OUTPUT: Extend_Buf_Ptr set up for STD_EPRINTF ;* ;**************************************************************** Set_ext_error_subst proc near ;AN022; mov msg_disp_class,ext_msg_class ;AN022; set up extended error msg class mov string_ptr_2,dx ;AN022; get address of failed string mov Extend_buf_sub,one_subst ;AN022; put number of subst in control block mov dx,offset TranGroup:Extend_Buf_ptr ;AN022; get extended message pointer mov Extend_Buf_ptr,ax ;AN022; get message number in control block ret ;AN022; return Set_ext_error_subst endp ;AN022; Break ; ; SavUDir - move the user's current directory on a drive into UserDir1 ; SavUDir1 - move the user's current directory on a drive into a specified ; buffer ; ; Inputs: DL has 1-based drive number ; ES:DI has destination buffer (SavUDir1 only) ; Outputs: Carry Clear ; DS = TranGroup ; Carry Set ; AX has error code ; Registers Modified: AX, SI ; SAVUDIR: MOV DI,OFFSET TRANGROUP:USERDIR1 SAVUDIR1: MOV AL,DL ADD AL,'@' CMP AL,'@' JNZ GOTUDRV ADD AL,[CURDRV] INC AL ; A = 1 GOTUDRV: STOSB MOV AH,[DIRCHAR] MOV AL,':' STOSW PUSH ES POP DS ASSUME DS:NOTHING MOV SI,DI MOV AH,CURRENT_DIR ; Get the Directory Text INT 21h retc PUSH CS POP DS ASSUME DS:TRANGROUP return CRLF2: PUSH DX MOV DX,OFFSET TRANGROUP:ACRLF_ptr PR: PUSH DS PUSH CS POP DS invoke std_printf POP DS POP DX return ; ; These routines (SCANOFF, DELIM) are called in batch processing when DS ; may NOT be TRANGROUP ; ASSUME DS:NOTHING,ES:NOTHING SCANOFF: LODSB CALL DELIM JZ SCANOFF DEC SI ; Point to first non-delimiter return ; ; Input: AL is character to classify ; Output: Z set if delimiter ; NZ set otherwise ; Registers modified: none ; DELIM: CMP AL,' ' retz CMP AL,'=' retz CMP AL,',' retz CMP AL,';' retz CMP AL,9 ; Check for TAB character retz CMP AL,0ah ; Check for line feed character - BAS return ASSUME DS:TRANGROUP,ES:TRANGROUP FCB_TO_ASCZ: ; Convert DS:SI to ASCIZ ES:DI MOV CX,8 MAINNAME: LODSB CMP AL,' ' JZ SKIPSPC STOSB SKIPSPC: LOOP MAINNAME LODSB CMP AL,' ' JZ GOTNAME MOV AH,AL MOV AL,dot_chr STOSB XCHG AL,AH STOSB MOV CL,2 EXTNAME: LODSB CMP AL,' ' JZ GOTNAME STOSB LOOP EXTNAME GOTNAME: XOR AL,AL STOSB return STRCOMP: ; ; Compare ASCIZ DS:SI with ES:DI. ; SI,DI destroyed. ; CMPSB retnz ; Strings not equal cmp byte ptr [SI-1],0 ; Hit NUL terminator? retz ; Yes, strings equal jmp short STRCOMP ; Equal so far, keep going CRPRINT: PUSH AX MOV AL,13 PUSH CX PUSH DI MOV DI,DX MOV CX,-1 PUSH ES PUSH DS POP ES REPNZ SCASB ; LOOK FOR TERMINATOR mov byte ptr [di-1],0 ; nul terminate the string POP ES mov string_ptr_2,dx mov dx,offset trangroup:string_buf_ptr invoke std_printf mov ds:byte ptr [di-1],13 ; now put the CR back JC ERROR_OUTPUT POP DI POP CX POP AX return ERROR_OUTPUT: PUSH CS POP DS ASSUME DS:TRANGROUP MOV ES,[RESSEG] ASSUME ES:RESGROUP MOV DX,OFFSET TRANGROUP:NOSPACE_ptr CMP [PIPEFLAG],0 JZ GO_TO_ERROR invoke PipeOff MOV DX,OFFSET TRANGROUP:PIPEEMES_ptr GO_TO_ERROR: JMP CERROR ASSUME DS:TRANGROUP,ES:TRANGROUP PATHCHRCMP: ;---- Mod for path invocation ---- PUBLIC pathchrcmp ;---- push ax mov ah,'/' CMP [SWITCHAR],ah JZ NOSLASHT CMP AL,'/' jz pccont NOSLASHT: CMP AL,'\' pccont: pop ax return ; ; PATHCRUNCH - ; ; ENTRY FCB (in PSP) contains drive # to crunch on ; PathPos = ptr to string with pathname in it ; PathCnt = length of string ; ; EXIT PathPos = ptr after pathname (w/ NULL) in string ; PathCnt = length left in string ; DestIsDir = nonzero if pathname delimiter char's found in pathname ; DestInfo = set if wildcard char's found in pathname ; If path crunched successfully, ; CY = clear ; Current directory is changed to directory in pathname ; UserDir1 contains previous directory for use by RestUDir ; RestDir = nonzero to flag later restoration of user's dir ; DestTail = ptr to beginning of filename ; If filename found in pathname, ; ZR = clear ; FCB filename fields contain filename ; If filename not found (pure directory path), ; ZR = set ; FCB filename fields are wildcarded with ?'s ; If pathcrunch failed (no ChDir's worked), ; CY = set ; Msg_Numb = extended error code ; ; NOTE DIR asks PathCrunch to forego parsing the filename into the ; FCB by setting DirFlag. In this case, the FCB is returned ; with the filename wildcarded. ; PATHCRUNCH: mov [msg_numb],0 ;AN022; Set up message flag MOV DL,DS:[FCB] ; DL = drive # (1 = A) CALL SAVUDIR ; save current directory in UserDir1 jc pcrunch_cderrJ ;AN022; if error on current dir - report invoke SETPATH ; scan past switches, whitespace ; DX = ptr to pathname, NULL-terminated ; PathPos = ptr to byte after NULL at end of pathname TEST [DESTINFO],2 ; test if wildcards (? or *) seen JNZ TRYPEEL ; wildcard seen, peel filename MOV AH,CHDIR ; AH = DOS ChDir function code INT 21h ; call DOS jnc chdir_worked ;AN022; no error - continue invoke get_ext_error_number ;AN022; get the extended error cmp ax,error_path_not_found ;AN022; if path not found jz trypeel ;AC022; keep trying cmp ax,error_access_denied ;AN022; if access denied jz trypeel ;AC022; keep trying mov [msg_numb],ax ;AN022; set up message flag jmp peelfail ;AN022; exit with other error chdir_worked: invoke SETREST1 ; set 'Restore Directory' flag true MOV AL,'?' ; if pure dir, wildcard filename in FCB MOV DI,5DH MOV CX,11 REP STOSB XOR AL,AL ; return carry clear, zero set return pcrunch_cderrj: ;AN022; need this for long jmp jmp pcrunch_cderr ;AN022; TRYPEEL: MOV SI,[PATHPOS] DEC SI ; SI = ptr to NULL at end of pathname MOV AL,[SI-1] ; AL = last char of pathname CMP [KPARSE],0 JNZ DELSTRT ; Last char is second KANJI byte, might be '\' CALL PATHCHRCMP JZ PEELFAIL ; Trailing '/' DELSTRT: MOV CX,SI ; CX = ptr to NULL at end of pathname MOV SI,DX ; SI = ptr to start of pathname PUSH DX ; save ptr to pathname DELLOOP: CMP SI,CX JZ GOTDELE ; no char's left, we have what we have LODSB ; AL = next char of pathname invoke TESTKANJ JZ NOTKANJ8 ; not Kanji, move along INC SI JMP DELLOOP NOTKANJ8: CALL PATHCHRCMP JNZ DELLOOP ; not a path delimiter, keep looking MOV DX,SI DEC DX ; DX = ptr to last delimiter found JMP DELLOOP ; go look for more GOTDELE: MOV SI,DX ; SI = ptr to pathname or last delim POP DX ; DX = ptr to pathname CMP SI,DX JZ BADRET ; didn't find path delim MOV CX,SI ; CX = ptr to last path delimiter MOV SI,DX ; SI = ptr to pathname DELLOOP2: ; Set value of KPARSE CMP SI,CX JZ TRYCD ; roll up till SI meets CX MOV [KPARSE],0 LODSB INVOKE TESTKANJ JZ DELLOOP2 INC SI INC [KPARSE] JMP DELLOOP2 TRYCD: push ax mov al,dot_chr ; AL = '.' CMP BYTE PTR [SI+1],al ; check for '.' after path delim ;M019; allow continuation if '. ' or ;M019; '..' is not found. jnz @F ;M019; '.' not found cmp BYTE PTR [SI+2],al ;M019; check for '..' jz @F ;M019; found '..' cmp BYTE PTR [SI+2],0 ;M019; check for '. ' (null terminated) @@: pop ax JZ PEELFAIL ; if . or .., pure cd should have worked mov al,[si-1] CMP al,':' ; Special case d:\file JZ BADRET CMP [KPARSE],0 JNZ NOTDOUBLESL ; Last char is second KANJI byte, might be '\' CALL PATHCHRCMP JNZ NOTDOUBLESL PEELFAIL: STC ; // return NOTDOUBLESL: MOV BYTE PTR [SI],0 MOV AH,CHDIR INT 21h JNC CDSUCC pcrunch_cderr: invoke get_ext_error_number ;AN022; get the extended error mov [msg_numb],ax ;AN022; set up message flag or si,si ;AN022; set up zero flag to not zero stc ;AN022; set up carry flag return BADRET: MOV AL,[SI] CALL PATHCHRCMP ; Special case 'DIRCHAR'file STC retnz XOR BL,BL XCHG BL,[SI+1] MOV AH,CHDIR INT 21h jc pcrunch_cderr ;AN022; go to error exit MOV [SI+1],BL CDSUCC: invoke SETREST1 INC SI ; Reset zero MOV [DESTTAIL],SI pushf ;AN015; save flags cmp dirflag,-1 ;AN015; don't do parse if in DIR jz pcrunch_end ;AN015; MOV DI,FCB MOV AX,(PARSE_FILE_DESCRIPTOR SHL 8) OR 02H ; Parse with default drive INT 21h pcrunch_end: popf ;AN015; get flags back return trancode ends end