	page ,132
;	SCCSID = @(#)tcode.asm	1.1 85/05/14
;	SCCSID = @(#)tcode.asm	1.1 85/05/14
TITLE	Part1 COMMAND Transient Routines
;/*
; *                      Microsoft Confidential
; *                      Copyright (C) Microsoft Corporation 1991
; *                      All Rights Reserved.
; */

;
;	Revision History
;	================
;
;	M025	SR	9/12/90	Removed calls to SetStdInOn,SetStdInOff
;				SetStdOutOn & SetStdOutOff.
;


.xlist
.xcref
	include comsw.asm
	include dossym.inc
	include syscall.inc
	include comseg.asm
	include comequ.asm
.list
.cref


CODERES 	SEGMENT PUBLIC BYTE	;AC000;
	EXTRN	EXEC_WAIT:NEAR
CODERES ENDS

DATARES 	SEGMENT PUBLIC BYTE	;AC000;
	EXTRN	BATCH:WORD
	EXTRN	CALL_BATCH_FLAG:byte
	EXTRN	CALL_FLAG:BYTE
	EXTRN	ECHOFLAG:BYTE
	EXTRN	envirseg:word
	EXTRN	EXTCOM:BYTE
	EXTRN	FORFLAG:BYTE
	EXTRN	IFFLAG:BYTE
	EXTRN	next_batch:word
	EXTRN	nullflag:byte
	EXTRN	PIPEFILES:BYTE
	EXTRN	PIPEFLAG:BYTE
	EXTRN	RE_OUT_APP:BYTE
	EXTRN	RE_OUTSTR:BYTE
	EXTRN	RESTDIR:BYTE
        EXTRN   SINGLECOM:WORD
        EXTRN   SEMIPERMCOM:WORD
	EXTRN	VERVAL:WORD
DATARES ENDS

TRANDATA	SEGMENT PUBLIC BYTE	;AC000;
	EXTRN	BadNam_Ptr:word 	;AC000;
TRANDATA	ENDS

TRANSPACE	SEGMENT PUBLIC BYTE	;AC000;
	EXTRN	APPEND_EXEC:BYTE	;AN041;
	EXTRN	ARG1S:WORD
	EXTRN	ARG2S:WORD
	EXTRN	ARGTS:WORD
	EXTRN	BYTCNT:WORD
	EXTRN	COMBUF:BYTE
	EXTRN	COMSW:WORD
	EXTRN	CURDRV:BYTE
	EXTRN	HEADCALL:DWORD
	EXTRN	IDLEN:BYTE
	EXTRN	INTERNATVARS:BYTE
	EXTRN	PARM1:BYTE
	EXTRN	PARM2:BYTE
	EXTRN	RE_INSTR:BYTE
	EXTRN	RESSEG:WORD
	EXTRN	SPECDRV:BYTE
	EXTRN	STACK:WORD
	EXTRN	SWITCHAR:BYTE
	EXTRN	TPA:WORD
	EXTRN	UCOMBUF:BYTE
	EXTRN	USERDIR1:BYTE
	IF  IBM_ROM_FIND
	EXTRN	ROM_CALL:BYTE
	EXTRN	ROM_CS:WORD
	EXTRN	ROM_IP:WORD
	ENDIF

TRANSPACE	ENDS

; ********************************************************************
; START OF TRANSIENT PORTION
; This code is loaded at the end of memory and may be overwritten by
; memory-intensive user programs.

TRANCODE	SEGMENT PUBLIC BYTE	;AC000;

ASSUME	CS:TRANGROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING

	EXTRN	$EXIT:NEAR
	EXTRN	DRVBAD:NEAR
	EXTRN	EXTERNAL:NEAR
	EXTRN	FNDCOM:NEAR
	EXTRN	FORPROC:NEAR
	EXTRN	PIPEPROC:NEAR
	EXTRN	PIPEPROCSTRT:NEAR

	PUBLIC	COMMAND
	PUBLIC	DOCOM
	PUBLIC	DOCOM1
	PUBLIC	NOPIPEPROC
	PUBLIC	TCOMMAND

	IF  IBM_ROM_FIND
	PUBLIC	ROM_EXEC
	PUBLIC	ROM_SCAN
	ENDIF

	ORG	0
ZERO	=	$

	ORG	100H				; Allow for 100H parameter area

SETDRV:
	MOV	AH,SET_DEFAULT_DRIVE
	INT	21h
;
; TCOMMAND is the recycle point in COMMAND.  Nothing is known here.
; No registers (CS:IP) no flags, nothing.
;

TCOMMAND:
	MOV	DS,[RESSEG]
ASSUME	DS:RESGROUP
	MOV	AX,-1
	XCHG	AX,[VERVAL]
	CMP	AX,-1
	JZ	NOSETVER2
	MOV	AH,SET_VERIFY_ON_WRITE		; AL has correct value
	INT	21h

NOSETVER2:
	CALL	[HEADCALL]			; Make sure header fixed
	XOR	BP,BP				; Flag transient not read
	CMP	[SINGLECOM],-1
	JNZ	COMMAND

$EXITPREP:
	PUSH	CS
	POP	DS
	JMP	$EXIT				; Have finished the single command
ASSUME	DS:NOTHING
;
; Main entry point from resident portion.
;
;   If BP <> 0, then we have just loaded transient portion otherwise we are
;   just beginning the processing of another command.
;

COMMAND:

;
; We are not always sure of the state of the world at this time.  We presume
; worst case and initialize the relevant registers: segments and stack.
;
	ASSUME	CS:TRANGROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING
	CLD
	MOV	AX,CS
	CLI
	MOV	SS,AX
ASSUME	SS:TRANGROUP
	MOV	SP,OFFSET TRANGROUP:STACK
	STI
	MOV	ES,AX
	MOV	DS,AX				;AN000; set DS to transient
ASSUME	ES:TRANGROUP,DS:TRANGROUP		;AC000;
	invoke	TSYSLOADMSG			;AN000; preload messages
	mov	append_exec,0			;AN041; set internal append state off

	MOV	DS,[RESSEG]
ASSUME	DS:RESGROUP

	MOV	[UCOMBUF],COMBUFLEN		; Init UCOMBUF
	MOV	[COMBUF],COMBUFLEN		; Init COMBUF (Autoexec doing DATE)
;
; If we have just loaded the transient, then we do NOT need to initialize the
; command buffer.  ????  DO WE NEED TO RESTORE THE USERS DIRECTORY ????  I
; guess not:  the only circumstances in which we reload the command processor
; is after a transient program execution.  In this case, we let the current
; directory lie where it may.
;
	OR	BP,BP				; See if just read
	JZ	TESTRDIR			; Not read, check user directory
	MOV	WORD PTR [UCOMBUF+1],0D01H	; Reset buffer
	JMP	SHORT NOSETBUF

TESTRDIR:
	CMP	[RESTDIR],0
	JZ	NOSETBUF			; User directory OK
	PUSH	DS
;
; We have an unusual situation to handle.  The user *may* have changed his
; directory as a result of an internal command that got aborted.  Restoring it
; twice may not help us:  the problem may never go away.  We just attempt it
; once and give up.
;
	MOV	[RESTDIR],0			; Flag users dirs OK
	PUSH	CS
	POP	DS
ASSUME	DS:TRANGROUP
	MOV	DX,OFFSET TRANGROUP:USERDIR1
	MOV	AH,CHDIR
	INT	21h			; Restore users directory
	POP	DS
ASSUME	DS:RESGROUP

NOSETBUF:
	CMP	[PIPEFILES],0
	JZ	NOPCLOSE			; Don't bother if they don't exist
	CMP	[PIPEFLAG],0
	JNZ	NOPCLOSE			; Don't del if still piping
	INVOKE	PIPEDEL

NOPCLOSE:
	MOV	[EXTCOM],0			; Flag internal command
	MOV	AX,CS				; Get segment we're in
	MOV	DS,AX
ASSUME	DS:TRANGROUP

	PUSH	AX
	MOV	DX,OFFSET TRANGROUP:INTERNATVARS
	MOV	AX,INTERNATIONAL SHL 8
	INT	21H
	POP	AX
	SUB	AX,[TPA]			; AX=size of TPA in paragraphs
	PUSH	BX
	MOV	BX,16
	MUL	BX				; DX:AX=size of TPA in bytes
	POP	BX
	OR	DX,DX				; See if over 64K
	JZ	SAVSIZ				; OK if not
	MOV	AX,-1				; If so, limit to 65535 bytes

SAVSIZ:
;
; AX is the number of bytes free in the buffer between the resident and the
; transient with a maximum of 64K-1.  We round this down to a multiple of 512.
;
	CMP	AX,512
	JBE	GotSize
	AND	AX,0FE00h			; NOT 511 = NOT 1FF

GotSize:
	MOV	[BYTCNT],AX			; Max no. of bytes that can be buffered
	MOV	DS,[RESSEG]			; All batch work must use resident seg.
ASSUME	DS:RESGROUP

	TEST	[ECHOFLAG],1
	JZ	GETCOM				; Don't do the CRLF
	INVOKE	SINGLETEST
	JB	GETCOM
	TEST	[PIPEFLAG],-1
	JNZ	GETCOM
	TEST	[FORFLAG],-1			; G  Don't print prompt in FOR
	JNZ	GETCOM				; G
	TEST	[BATCH], -1			; G  Don't print prompt if in batch
	JNZ	GETCOM				; G
	INVOKE	CRLF2

GETCOM:
	MOV	CALL_FLAG,0			; G Reset call flags
	MOV	CALL_BATCH_FLAG,0		; G
	MOV	AH,GET_DEFAULT_DRIVE
	INT	21h
	MOV	[CURDRV],AL
	TEST	[PIPEFLAG],-1			; Pipe has highest presedence
	JZ	NOPIPE
	JMP	PIPEPROC			; Continue the pipeline

NOPIPE:
	TEST	[ECHOFLAG],1
	JZ	NOPDRV				; No prompt if echo off
	INVOKE	SINGLETEST
	JB	NOPDRV
	TEST	[FORFLAG],-1			; G  Don't print prompt in FOR
	JNZ	NOPDRV				; G
	TEST	[BATCH], -1			; G  Don't print prompt if in batch
	JNZ	TESTFORBAT			; G
	INVOKE	PRINT_PROMPT			; Prompt the user

NOPDRV:
	TEST	[FORFLAG],-1			; FOR has next highest precedence
	JZ	TESTFORbat
	JMP	FORPROC 			; Continue the FOR

TESTFORBAT:
	MOV	[RE_INSTR],0			; Turn redirection back off
	MOV	[RE_OUTSTR],0
	MOV	[RE_OUT_APP],0
	MOV	IFFlag,0			; no more ifs...
	TEST	[BATCH],-1			; Batch has lowest precedence
	JZ	ISNOBAT

;	Bugbug:	MULT_SHELL_GET no longer used?
	push	es				;AN000; save ES
	push	ds				;AN000; save DS
	mov	ax,mult_shell_get		;AN000; check to see if SHELL has command
	mov	es,[batch]			;AN000; get batch segment
	mov	di,batfile			;AN000; get batch file name
	push	cs				;AN000; get local segment to DS
	pop	ds				;AN000;
	mov	dx,offset trangroup:combuf	;AN000; pass communications buffer
	int	2fh				;AN000; call the shell
	cmp	al,shell_action 		;AN000; does shell have a commmand?
	pop	ds				;AN000; restore DS
	pop	es				;AN000; restore ES
	jz	jdocom1 			;AN000; yes - go process command

	PUSH	DS				;G
	INVOKE	READBAT 			; Continue BATCH
	POP	DS				;G
	mov	nullflag,0			;G reset no command flag
	TEST	[BATCH],-1			;G
	JNZ	JDOCOM1 			;G if batch still in progress continue
	MOV	BX,NEXT_BATCH			;G
	CMP	BX,0				;G see if there is a new batch file
	JZ	JDOCOM1 			;G no - go do command
	MOV	BATCH,BX			;G get segment of next batch file
	MOV	NEXT_BATCH,0			;G reset next batch
JDOCOM1:
	PUSH	CS				;G
	POP	DS				;G
	JMP SHORT DoCom1			; echoing already done

ISNOBAT:
	CMP	[SINGLECOM],0
	JZ	REGCOM
        MOV     SI,[SEMIPERMCOM]
	XCHG	SI,[SINGLECOM]
	MOV	DI,OFFSET TRANGROUP:COMBUF + 2
	XOR	CX,CX

SINGLELOOP:
	LODSB
	STOSB
	INC	CX
	CMP	AL,0DH
	JNZ	SINGLELOOP
	DEC	CX
	PUSH	CS
	POP	DS
ASSUME	DS:TRANGROUP
	MOV	[COMBUF + 1],CL
;
; do NOT issue a trailing CRLF...
;
	JMP	SHORT DOCOM1

;
; We have a normal command.  
; Printers are a bizarre quantity.  Sometimes they are a stream and
; sometimes they aren't.  At this point, we automatically close all spool
; files and turn on truncation mode.
;

REGCOM:
	MOV	AX,(ServerCall SHL 8) + 9
	INT	21h
	MOV	AX,(ServerCall SHL 8) + 8
	MOV	DL,1
	INT	21h

	PUSH	CS
	POP	DS				; Need local segment to point to buffer
	MOV	DX,OFFSET TRANGROUP:UCOMBUF

;	Try to read interactive command line via DOSKey.
;	If that fails, use DOS Buffered Keyboard Input.

	mov	ax,4810h		; AX = DOSKey Read Line function
	int	2fh
	or	ax,ax
	jz	GotCom			; DOSKey gave us a command line

	mov	ah,STD_CON_STRING_INPUT	; AH = DOS Buffered Keyboard Input
	int	21h			; call DOS

GotCom:
	MOV	CL,[UCOMBUF]
	XOR	CH,CH
	ADD	CX,3
	MOV	SI,OFFSET TRANGROUP:UCOMBUF
	MOV	DI,OFFSET TRANGROUP:COMBUF
	REP	MOVSB				; Transfer it to the cooked buffer

;---------------

transpace   segment
    extrn   arg:byte				; the arg structure!
transpace   ends
;---------------


DOCOM:
	INVOKE	CRLF2

DOCOM1:
	INVOKE	PRESCAN 			; Cook the input buffer
	JZ	NOPIPEPROC
	JMP	PIPEPROCSTRT			; Fire up the pipe

nullcomj:
	jmp	nullcom

NOPIPEPROC:
	invoke	parseline
	jnc	OkParse 			; user error?  or maybe we goofed?

BadParse:
	PUSH	CS
	POP	DS
	MOV	DX,OFFSET TRANGROUP:BADNAM_ptr
	INVOKE	std_eprintf
	JMP	TCOMMAND

OkParse:
	test	arg.argv[0].argflags, MASK wildcard
	jnz	BadParse			; ambiguous commands not allowed
	cmp	arg.argvcnt, 0			; there WAS a command, wasn't there?
	jz	nullcomj
	cmp	arg.argv[0].arglen, 0		; probably an unnecessary check...
	jz	nullcomj			; guarantees argv[0] at least x<NULL>

	MOV	SI,OFFSET TRANGROUP:COMBUF+2
	MOV	DI,OFFSET TRANGROUP:IDLEN
	MOV	AX,(PARSE_FILE_DESCRIPTOR SHL 8) OR 01H ; Make FCB with blank scan-off
	INT	21h
	mov	BX, arg.argv[0].argpointer
	cmp	BYTE PTR [BX+1],':'             ; was a drive specified?
	jne	short drvgd			; no, use default of zero...

	mov	DL, BYTE PTR [BX]		; pick-up drive letter
	and	DL, NOT 20H			; uppercase the sucker
	sub	DL, capital_A			; convert it to a drive number, A=0

	CMP	AL,-1				; See what PARSE said about our drive letter.
	JZ	drvbadj2			; It was invalid.

	mov	DI, arg.argv[0].argstartel
	cmp	BYTE PTR [DI], 0		; is there actually a command there?
	jnz	drvgd				; if not, we have:  "d:", "d:\", "d:/"
	jmp	setdrv				; and set drive to new drive spec

drvbadj2:
	jmp	drvbad

DRVGD:
	MOV	AL,[DI]
	MOV	[SPECDRV],AL
	MOV	AL,' '
	MOV	CX,9
	INC	DI
	REPNE	SCASB				; Count no. of letters in command name
	MOV	AL,8
	SUB	AL,CL
	MOV	[IDLEN],AL			; IDLEN is truly the length
	MOV	DI,81H
	PUSH	SI

	mov	si, OFFSET TRANGROUP:COMBUF+2	; Skip over all leading delims
	invoke	scanoff
;SR;
; We are going to skip over the first char always. The logic is that the
;command tail can never start from the first character. The code below is 
;trying to figure out the command tail and copy it to the command line 
;buffer in the PSP. However, if the first character happens to be a switch
;character and the user given command line is a full 128 bytes, we try to
;copy 128 bytes to the PSP while it can take only 127 chars. This extra
;char overwrites the code and leads to a crash on future commands.
;

	inc	si

do_skipcom:
	lodsb					; move command line pointer over
	invoke	delim				; pathname -- have to do it ourselves
	jz	do_skipped			; 'cause parse_file_descriptor is dumb
	cmp	AL, 0DH 			; can't always depend on argv[0].arglen
	jz	do_skipped			; to be the same length as the user-
	cmp	AL, [SWITCHAR]			; specified command string
	jnz	do_skipcom

do_skipped:
	dec	SI
	XOR	CX,CX

COMTAIL:
	LODSB
	STOSB					; Move command tail to 80H
	CMP	AL,13
	LOOPNZ	COMTAIL
	DEC	DI
	MOV	BP,DI
	NOT	CL
	MOV	BYTE PTR DS:[80H],CL
	POP	SI

;-----
; Some of these comments are sadly at odds with this brave new code.
;-----
; If the command has 0 parameters must check here for
; any switches that might be present.
; SI -> first character after the command.

	mov	DI, arg.argv[0].argsw_word
	mov	[COMSW], DI			; ah yes, the old addressing mode problem...
	mov	SI, arg.argv[1 * SIZE argv_ele].argpointer  ; s = argv[1];
	OR	SI,SI				;   if (s == NULL)
	JNZ	DoParse
	MOV	SI,BP				;	s = bp; (buffer end)

DoParse:
	MOV	DI,FCB
	MOV	AX,(PARSE_FILE_DESCRIPTOR SHL 8) OR 01H
	INT	21h
	MOV	[PARM1],AL			; Save result of parse

	mov	DI, arg.argv[1*SIZE argv_ele].argsw_word
	mov	[ARG1S], DI
	mov	SI, arg.argv[2*SIZE argv_ele].argpointer    ; s = argv[2];
	OR	SI,SI				;   if (s == NULL)
	JNZ	DoParse2
	MOV	SI,BP				;	s = bp; (bufend)1

DoParse2:
	MOV	DI,FCB+10H
	MOV	AX,(PARSE_FILE_DESCRIPTOR SHL 8) OR 01H
	INT	21h			; Parse file name
	MOV	[PARM2],AL			; Save result

	mov	DI, arg.argv[2*SIZE argv_ele].argsw_word
	mov	[ARG2S], DI
	mov	DI, arg.argv[0].argsw_word
	not	DI				; ARGTS doesn't include the flags
	and	DI, arg.argswinfo		; from COMSW...
	mov	[ARGTS], DI

	MOV	AL,[IDLEN]
	MOV	DL,[SPECDRV]
	or	DL, DL				; if a drive was specified...
	jnz	externalj1			; it MUST be external, by this time
	dec	al				; (I don't know why -- old code did it)
	jmp	fndcom				; otherwise, check internal com table

externalj1:
	jmp	external

nullcom:
	MOV	DS,[RESSEG]
ASSUME	DS:RESGROUP
	TEST	[BATCH], -1			;G Are we in a batch file?
	JZ	nosetflag			;G only set flag if in batch
	mov	nullflag,nullcommand		;G set flag to indicate no command

nosetflag:
	CMP	[SINGLECOM],-1
	JZ	EXITJ
	JMP	GETCOM

EXITJ:
	JMP	$EXITPREP

IF IBM_ROM_FIND
	include vector.inc
	include pdb.inc
	include arena.inc
	include mshalo.asm
ENDIF

TRANCODE	ENDS
	END

