	page ,132
;	SCCSID = @(#)tenv.asm	4.2 85/08/16
;	SCCSID = @(#)tenv.asm	4.2 85/08/16
TITLE	Part6 COMMAND Transient routines.
;/*
; *                      Microsoft Confidential
; *                      Copyright (C) Microsoft Corporation 1991
; *                      All Rights Reserved.
; */

;	Environment utilities and misc. routines
;
;	Revision History
;	================
;
;	M024	SR	9/5/90	Zero out comspec_flag to fix bug
;				#710 about comspec getting trashed.
;



	INCLUDE comsw.asm

.xlist
.xcref
	include dossym.inc
	include syscall.inc
	include arena.inc
	include comseg.asm
	include comequ.asm
	include doscntry.inc		;an000;
	include	resmsg.equ
.list
.cref


DATARES 	SEGMENT PUBLIC BYTE	;AC000;
	EXTRN	comdrv:byte
	EXTRN	comspec_end:word
	EXTRN	dbcs_vector_addr:dword	;AN000;
	EXTRN	ENVIRSEG:WORD
	EXTRN	fucase_addr:word	;AC000;
	EXTRN	PutBackDrv:byte
	EXTRN	PutBackComSpec:byte
	EXTRN	RESTDIR:BYTE
DATARES ENDS

TRANDATA	SEGMENT PUBLIC BYTE	;AC000;
	EXTRN	arg_buf_ptr:word
	EXTRN	comspec:byte
	EXTRN	comspec_flag:byte
	EXTRN	comspecstr:byte
	EXTRN	ENVERR_PTR:WORD
	EXTRN	PATH_TEXT:byte
	EXTRN	PROMPT_TEXT:byte
	EXTRN	SYNTMES_PTR:WORD
TRANDATA	ENDS

TRANSPACE	SEGMENT PUBLIC BYTE	;AC000;
	EXTRN	Arg_Buf:BYTE
	EXTRN	RESSEG:WORD
	EXTRN	USERDIR1:BYTE
TRANSPACE	ENDS

TRANCODE	SEGMENT PUBLIC byte

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

	EXTRN	cerror:near

	PUBLIC	add_name_to_environment
	PUBLIC	add_prompt
	PUBLIC	delete_path
	PUBLIC	find_name_in_environment
	PUBLIC	find_path
	PUBLIC	find_prompt
	PUBLIC	move_name
	PUBLIC	restudir
	PUBLIC	restudir1
	PUBLIC	scan_double_null
	PUBLIC	scasb2
	PUBLIC	store_char
	PUBLIC	Testkanj		;AN000;  3/3/KK
	PUBLIC	upconv

BREAK	<Environment utilities>
ASSUME DS:TRANGROUP

	break	Prompt command
assume	ds:trangroup,es:trangroup

ADD_PROMPT:
	CALL	DELETE_PROMPT			; DELETE ANY EXISTING PROMPT
	CALL	SCAN_DOUBLE_NULL

ADD_PROMPT2:
	PUSH	SI
	CALL	GETARG
	POP	SI
	retz					; PRE SCAN FOR ARGUMENTS
	CALL	MOVE_NAME			; MOVE IN NAME
	CALL	GETARG
	PUSH	SI
	JMP	SHORT ADD_NAME


	break	The SET command
assume	ds:trangroup,es:trangroup

;
; Input: DS:SI points to a CR terminated string
; Output: carry flag is set if no room
;	  otherwise name is added to environment
;

DISP_ENVj:
	jmp	DISP_ENV

ADD_NAME_TO_ENVIRONMENT:
	CALL	GETARG
	JZ	DISP_ENVj
;
; check if line contains exactly one equals sign
;
	XOR	BX,BX				;= COUNT IS 0
	PUSH	SI				;SAVE POINTER TO BEGINNING OF LINE

EQLP:
	LODSB					;GET A CHAR
	CMP	AL,13				;IF CR WE'RE ALL DONE
	JZ	QUEQ
	CMP	AL,'='                          ;LOOK FOR = SIGN
	JNZ	EQLP				;NOT THERE, GET NEXT CHAR
	INC	BL				;OTHERWISE INCREMENT EQ COUNT
	CMP	BYTE PTR [SI],13		;LOOK FOR CR FOLLOWING = SIGN
	JNZ	EQLP
	INC	BH				;SET BH=1 MEANS NO PARAMETERS
	JMP	EQLP				;AND LOOK FOR MORE

QUEQ:
	POP	SI				;RESTORE BEGINNING OF LINE
	DEC	BL				;ZERO FLAG MEANS ONLY ONE EQ
	JZ	ONEQ				;GOOD LINE
	MOV	DX,OFFSET TRANGROUP:SYNTMES_ptr
	JMP	CERROR

ONEQ:
	PUSH	BX
	CALL	DELETE_NAME_IN_ENVIRONMENT
	POP	BX
	DEC	BH
	retz

	CALL	SCAN_DOUBLE_NULL
	mov	bx,di				; Save ptr to beginning of env var name
	CALL	MOVE_NAME
	push	si
	xchg	bx,di				; Switch ptrs to beginning and end of
						;  env var name
;
; We want to special-case COMSPEC.  This is to reduce the amount of code
; necessary in the resident for re-reading the transient.  Let's look for
; COMSPEC=
;
	mov	comspec_flag,0			; clear flag ; M024
	mov	si,offset trangroup:comspecstr	; Load ptr to string "COMSPEC"
	mov	cx,4				; If the new env var is comspec, set
	repz	cmpsw				;  the comspec_flag
;
; Zero set => exact match
;
	jnz	not_comspec
	inc	comspec_flag			; comspec is changing ; M024

not_comspec:
	mov	di,bx				; Load ptr to end of env var name

ADD_NAME:					; Add the value of the new env var
	pop	si				;  to the environment.
	push	si

add_name1:
	LODSB
	CMP	AL,13
	jz	add_name_ret
	CALL	STORE_CHAR
	JMP	ADD_NAME1

add_name_ret:
	pop	si
	cmp	comspec_flag,0			; If the new env var is comspec,
	retz					;  copy the value into the
;
; We have changed the COMSPEC variable.  We need to update the resident
; pieces necessary to reread in the info.  First, skip all delimiters
;
	invoke	ScanOff
	mov	es,[resseg]			;  comspec var in the resident
	assume	es:resgroup
;
; Make sure that the printer knows where the beginning of the string is
;
	mov	di,offset resgroup:comspec
	mov	bx,di
;
; Generate drive letter for display
;
	xor	ax,ax				;g assume no drive first
	mov	comdrv,al			;g
	push	ax				;AN000;  3/3/KK
	mov	al,[si] 			;AN000;  3/3/KK
	call	testkanj			;AN000;  3/3/KK
	pop	ax				;AN000;  3/3/KK
	jnz	GotDrive
	cmp	byte ptr [si+1],':'             ; drive specified?
	jnz	GotDrive
	mov	al,[si] 			; get his specified drive
	call	UpConv				; convert to uppercase
	sub	al,'A'                          ; convert to 0-based
	add	di,2
	inc	al				; convert to 1-based number
	mov	comdrv,al
;
; Stick the drive letter in the prompt message.  Nothing special needs to be
; done here..
;

	add	al,'A'-1

GotDrive:					;g
	mov	PutBackComSpec.SubstPtr,di	;g point to beginning of name after drive
	mov	es:PutBackDrv,al
;
; Copy chars until delim
;

	mov	di,bx

copy_comspec:
	lodsb
	invoke	Delim
	jz	CopyDone
	cmp	al,13
	jz	CopyDone
	stosb
	jmp	short copy_comspec

CopyDone:
	xor	al,al				; Null terminate the string and quit
	stosb
	mov	comspec_flag,0
	dec	di
	mov	comspec_end,di

	ret

DISP_ENV:
	MOV	DS,[RESSEG]
ASSUME	DS:RESGROUP
	MOV	DS,[ENVIRSEG]
ASSUME	DS:NOTHING
	XOR	SI,SI

PENVLP:
	CMP	BYTE PTR [SI],0
	retz
	mov	di,offset trangroup:arg_buf

PENVLP2:
	LODSB
	stosb
	OR	AL,AL
	JNZ	PENVLP2
	mov	dx,offset trangroup:arg_buf_ptr
	push	ds
	push	es
	pop	ds
	invoke	printf_crlf
	pop	ds
	JMP	PENVLP

ASSUME	DS:TRANGROUP

DELETE_PATH:
	MOV	SI,OFFSET TRANGROUP:PATH_TEXT
	JMP	SHORT DELETE_NAME_IN_environment

DELETE_PROMPT:
	MOV	SI,OFFSET TRANGROUP:PROMPT_TEXT

DELETE_NAME_IN_environment:
;
; Input: DS:SI points to a "=" terminated string
; Output: carry flag is set if name not found
;	  otherwise name is deleted
;
	PUSH	SI
	PUSH	DS
	CALL	FIND				; ES:DI POINTS TO NAME
	JC	DEL1
	MOV	SI,DI				; SAVE IT
	CALL	SCASB2				; SCAN FOR THE NUL
	XCHG	SI,DI
;SR;
; If we have only one env string, then the double null is lost when the last
;string is deleted and we have an invalid empty environment with only a 
;single null. To avoid this, we will look for the double null case and then
;move an extra null char.
; Bugbug: The only possible problem is that the last pathstring 
;will be followed by a triple null. Is this really a problem?
;
	cmp	byte ptr es:[si],0			;null char?
	jnz	not_dnull			;no, we are at a double null
	dec	si				;point at the double null
not_dnull:

	CALL	GETENVSIZ
	SUB	CX,SI
	PUSH	ES
	POP	DS				; ES:DI POINTS TO NAME, DS:SI POINTS TO NEXT NAME
	REP	MOVSB				; DELETE THE NAME

DEL1:
	POP	DS
	POP	SI
	return

FIND_PATH:
	MOV	SI,OFFSET TRANGROUP:PATH_TEXT
	JMP	SHORT FIND_NAME_IN_environment

FIND_PROMPT:
	MOV	SI,OFFSET TRANGROUP:PROMPT_TEXT

FIND_NAME_IN_environment:
;
; Input: DS:SI points to a "=" terminated string
; Output: ES:DI points to the arguments in the environment
;	  zero is set if name not found
;	  carry flag is set if name not valid format
;
	CALL	FIND				; FIND THE NAME
	retc					; CARRY MEANS NOT FOUND
	JMP	SCASB1				; SCAN FOR = SIGN
;
; On return of FIND1, ES:DI points to beginning of name
;
FIND:
	CLD
	CALL	COUNT0				; CX = LENGTH OF NAME
	MOV	ES,[RESSEG]
ASSUME	ES:RESGROUP
	MOV	ES,[ENVIRSEG]
ASSUME	ES:NOTHING
	XOR	DI,DI

FIND1:
	PUSH	CX
	PUSH	SI
	PUSH	DI

FIND11:
	LODSB
	CALL	TESTKANJ
	JZ	NOTKANJ3
	DEC	SI
	LODSW
	INC	DI
	INC	DI
	CMP	AX,ES:[DI-2]
	JNZ	FIND12
	DEC	CX
	LOOP	FIND11
	JMP	SHORT FIND12

NOTKANJ3:
	CALL	UPCONV
	INC	DI
	CMP	AL,ES:[DI-1]
	JNZ	FIND12
	LOOP	FIND11

FIND12:
	POP	DI
	POP	SI
	POP	CX
	retz
	PUSH	CX
	CALL	SCASB2				; SCAN FOR A NUL
	POP	CX
	CMP	BYTE PTR ES:[DI],0
	JNZ	FIND1
	STC					; INDICATE NOT FOUND
	return

COUNT0:
	PUSH	DS
	POP	ES
	MOV	DI,SI

COUNT1:
	PUSH	DI				; COUNT NUMBER OF CHARS UNTIL "="
	CALL	SCASB1
	JMP	SHORT COUNTX

COUNT2:
	PUSH	DI				; COUNT NUMBER OF CHARS UNTIL NUL
	CALL	SCASB2

COUNTX:
	POP	CX
	SUB	DI,CX
	XCHG	DI,CX
	return

MOVE_NAME:
	CMP	BYTE PTR DS:[SI],13
	retz
	LODSB

;;;;	IFDEF	DBCS			3/3/KK
	CALL	TESTKANJ
	JZ	NOTKANJ1
	CALL	STORE_CHAR
	LODSB
	CALL	STORE_CHAR
	JMP	SHORT MOVE_NAME

NOTKANJ1:
;;;;	ENDIF				3/3/KK

	CALL	UPCONV
	CALL	STORE_CHAR
	CMP	AL,'='
	JNZ	MOVE_NAME
	return

GETARG:
	MOV	SI,80H
	LODSB
	OR	AL,AL
	retz
	invoke	SCANOFF
	CMP	AL,13
	return

;
; Point ES:DI to the final NULL string.  Note that in an empty environment,
; there is NO double NULL, merely a string that is empty.
;
SCAN_DOUBLE_NULL:
	MOV	ES,[RESSEG]
ASSUME	ES:RESGROUP
	MOV	ES,[ENVIRSEG]
ASSUME	ES:NOTHING
	XOR	DI,DI
;
; Top cycle-point.  If the string here is empty, then we are done
;
SDN1:
	cmp	byte ptr es:[di],0		; nul string?
	retz					; yep, all done
	CALL	SCASB2
	JMP	SDN1

SCASB1:
	MOV	AL,'='                          ; SCAN FOR AN =
	JMP	SHORT SCASBX
SCASB2:
	XOR	AL,AL				; SCAN FOR A NUL
SCASBX:
	MOV	CX,100H
	REPNZ	SCASB
	return
;Bugbug: This is Kanji stuff - put it in conditionals

TESTKANJ:
	push	ds				;AN000;  3/3/KK
	push	si				;AN000;  3/3/KK
	push	ax				;AN000;  3/3/KK
	mov	ds,cs:[resseg]			;AN000;  Get resident segment
	assume	ds:resgroup			;AN000;
	lds	si,dbcs_vector_addr		;AN000;  get DBCS vector
ktlop:						;AN000;  3/3/KK
	cmp	word ptr ds:[si],0		;AN000;  end of Table	3/3/KK
	je	notlead 			;AN000;  3/3/KK
	pop	ax				;AN000;  3/3/KK
	push	ax				;AN000;  3/3/KK
	cmp	al, byte ptr ds:[si]		;AN000;  3/3/KK
	jb	notlead 			;AN000;  3/3/KK
	inc	si				;AN000;  3/3/KK
	cmp	al, byte ptr ds:[si]		;AN000;  3/3/KK
	jbe	islead				;AN000;  3/3/KK
	inc	si				;AN000;  3/3/KK
	jmp	short ktlop			;AN000;  try another range ; 3/3/KK
Notlead:					;AN000;  3/3/KK
	xor	ax,ax				;AN000;  set zero 3/3/KK
	jmp	short ktret			;AN000;  3/3/KK
Islead: 					;AN000;  3/3/KK
	xor	ax,ax				;AN000;  reset zero  3/3/KK
	inc	ax				;AN000;  3/3/KK
ktret:						;AN000;  3/3/KK
	pop	ax				;AN000;  3/3/KK
	pop	si				;AN000;  3/3/KK
	pop	ds				;AN000;  3/3/KK
	return					;AN000;  3/3/KK
;-------------------------------------		;3/3/KK


; ****************************************************************
; *
; * ROUTINE:	 UPCONV     (ADDED BY EMG 4.00)
; *
; * FUNCTION:	 This routine returns the upper case equivalent of
; *		 the character in AL from the file upper case table
; *		 in DOS if character if above  ascii 128, else
; *		 subtracts 20H if between "a" and "z".
; *
; * INPUT:	 AL	      char to be upper cased
; *		 FUCASE_ADDR  set to the file upper case table
; *
; * OUTPUT:	 AL	      upper cased character
; *
; ****************************************************************

assume	ds:trangroup				;AN000;

upconv	proc	near				;AN000;

	cmp	al,80h				;AN000;  see if char is > ascii 128
	jb	oth_fucase			;AN000;  no - upper case math
	sub	al,80h				;AN000;  only upper 128 chars in table
	push	ds				;AN000;
	push	bx				;AN000;
	mov	ds,[resseg]			;AN000;  get resident data segment
assume	ds:resgroup				;AN000;
	lds	bx,dword ptr fucase_addr+1	;AN000;  get table address
	add	bx,2				;AN000;  skip over first word
	xlat	ds:byte ptr [bx]		;AN000;  convert to upper case
	pop	bx				;AN000;
	pop	ds				;AN000;
assume	ds:trangroup				;AN000;
	jmp	short upconv_end		;AN000;  we finished - exit

oth_fucase:					;AN000;
	cmp	al,small_a			;AC000; if between "a" and "z",
	jb	upconv_end			;AC000;     subtract 20h to get
	cmp	al,small_z			;AC000;    upper case equivalent.
	ja	upconv_end			;AC000;
	sub	al,20h				;AC000; Change lower-case to upper

upconv_end:					;AN000;
	ret

upconv	endp					;AN000;


;
; STORE A CHAR IN environment, GROWING IT IF NECESSARY
;
STORE_CHAR:
	PUSH	CX
	PUSH	BX
	PUSH	ES				;AN056;
	PUSH	DS				;AN056; Save local DS
	MOV	DS,[RESSEG]			;AN056; Get resident segment
	ASSUME	DS:RESGROUP			;AN056;
	MOV	ES,[ENVIRSEG]			;AN056; Get environment segment
	ASSUME	ES:NOTHING			;AN056;
	POP	DS				;AN056; Get local segment back
	ASSUME	DS:TRANGROUP			;AN056;
	CALL	GETENVSIZ
	MOV	BX,CX
	SUB	BX,2				; SAVE ROOM FOR DOUBLE NULL
	CMP	DI,BX
	JB	STORE1

	PUSH	AX
	PUSH	CX
	PUSH	BX				; Save Size of environment
	invoke	FREE_TPA
	POP	BX
	ADD	BX,2				; Recover true environment size

	CMP	BX, 8000H			; Don't let environment grow > 32K
	JB	ENVSIZ_OK
BAD_ENV_SIZE:					;AN056;
	STC
	JMP	SHORT ENVNOSET
ENVSIZ_OK:

	MOV	CL,4
	SHR	BX,CL				; Convert back to paragraphs
	INC	BX				; Try to grow environment by one para
	MOV	CX,ES				;AN056; Get environment segment
	ADD	CX,BX				;AN056; Add in size of environment
	ADD	CX,020H 			;AN056; Add in some TPA
	MOV	AX,CS				;AN056; Get the transient segment
	CMP	CX,AX				;AN056; Are we hitting the transient?
	JNB	BAD_ENV_SIZE			;AN056; Yes - don't do it!!!
	MOV	AH,SETBLOCK
	INT	21h
ENVNOSET:
	PUSHF
	PUSH	ES
	MOV	ES,[RESSEG]
	invoke	ALLOC_TPA
	POP	ES
	POPF
	POP	CX
	POP	AX
	JNC	STORE1
	POP	ES				;AN056;
	MOV	DX,OFFSET TRANGROUP:ENVERR_ptr
	JMP	CERROR
STORE1:
	STOSB
	MOV	WORD PTR ES:[DI],0		; NULL IS AT END
	POP	ES				;AN056;
	POP	BX
	POP	CX
	return

GETENVSIZ:
;Get size of environment in bytes, rounded up to paragraph boundry
;ES has environment segment
;Size returned in CX, all other registers preserved

	PUSH	ES
	PUSH	AX
	MOV	AX,ES
	DEC	AX				;Point at arena
	MOV	ES,AX
	MOV	AX,ES:[arena_size]
	MOV	CL,4
	SHL	AX,CL				;Convert to bytes
	MOV	CX,AX
	POP	AX
	POP	ES
	return


ASSUME	DS:TRANGROUP


RESTUDIR1:
	PUSH	DS
	MOV	DS,[RESSEG]
ASSUME	DS:RESGROUP
	CMP	[RESTDIR],0
	POP	DS
ASSUME	DS:TRANGROUP
	retz

RESTUDIR:
	MOV	DX,OFFSET TRANGROUP:USERDIR1
	MOV	AH,CHDIR
	INT	21h			; Restore users DIR
	XOR	AL,AL
	invoke	SETREST
RET56:
	return

trancode    ends
	    end
