;	SCCSID = @(#)msgfins.asm	6.2 91/03/26
;	SCCSID = @(#)msgfins.asm	6.2 91/03/26
;********************************************************************
;   Modification history
;
; new stuff for driver 2A:
;4-3	New call DosInsMessage
;4-3	DBCS consideration when searching for %n in message (Copy_Message)
;4-23	include error2.inc added
;5-14	fix when datalength is less than 2, don't output CR/LF
;6-5	DCR 244: add blank insert text for un_get_message, entry 1 in ivtable
;6-5	get error message text from msg.txt which is built using MKMSGF /asm
;6-5	DCR 244: add copy component id, message number and ': ' to callers buffer (DCR244)
;6-5	DCR 244: zero out insertion string when output error message
;6-5	include BASEMID.INC for getting error msg numbers
;6-6	implement DRC 320 - add parm and change parm order
;6-6	make fixes to support FamilyAPI (3X BOX)
;6-11	make fixes to replace DOSGETDBCSEV far call with CHECK_DBCS near call
;	to increase performance (ptm 580)
;6-18	REM out component ID and msg number for msg header code (move to 1.1)
;	in other words DISABLE DCR 244 ! (ptm 816)
;6-26	check for malformed Double Byte chars in variable insertion strings in
;	Text_Subst routine
;7-29 fix Family API version, so that 26 bytes of local variables are generated
;8-4  fix Family API version, so that MSGSEG gets loaded into DATA segment properly
;8-20 RE-enable dcr 244 display msg ID's only when Error or Warning message returned
;9-3  fix Family API version, so that 28 bytes of local variables are generated
;9-3  fix Family API version, to use RealEnter macro to setup stack frame
;9-15 fix main routine to return most recent error to caller
;12-11	Add Trace Hooks support in protect mode.
;12-11	changed cmp reg,0 -> or reg,reg to shrink code. (a tiny bit!!)
;12-12	changed getting the resident messages code (uses msg.inc)
;12-13	build error: made 'extrn msgtrace:near' protect mode only !!!
;1-26-87 convert single/double byte copy in COPY_MESSAGE to routines to
;	 reduce dup code (added copy_single, copy_double, is_lead routines)
;1-26-87 fix: if msginlength bisects a DBCS char, the malformed DBCS char
;	 is NOT copied into caller's buffer
;3-23-87 fix: if in DBCS mode, and message text too long, DO NOT backup
;	 2 chars and stick CR/LF at end of buffer!! (it may generate a
;	 malformed DBCS character).
;	 Also, decrement local counter (BX) in TEXT_SUBST routine.
;4-20-87 Use new and improved messages. Set ivcount = 0.
;	 Use new message include file msgfins.inc.
;5-11-87 Fix insertion text truncation at 128 bytes.
;	 Allow only (64K - pointer to string) bytes for string.
;6-15-87 Align all data segments on para boundary
;6-26-87 For REAL mode: use public symbols in MSGSEG message segment
;	 in API.LIB (msg.obj) to get error messages.
;5-23-89 Removed Pre and Post tracing code. WWM
;05-15-90 BHC Modified for DCR900.
;********************************************************************
.XALL
.LIST
IFNDEF FamilyAPI
.286c
ENDIF ; FamilyAPI
PAGE 60,132
TITLE Message Retriever - DosInsMessage - IBM CONFIDENTIAL
SUBTTL Externals
;/********************** START OF SPECIFICATIONS **********************/
;/*								      */
;/* SOURCE FILE NAME:  MSGFINS					      */
;/*								      */
;/* DESCRIPTIVE NAME:  RETURN SYSTEM MESSAGE WITH VARIABLE TEXT       */
;/*		       INSERTED 				      */
;/* COPYRIGHT: (TOP SOURCE FILE ONLY)				      */
;/*								      */
;/* STATUS: RELEASE 1 LEVEL 0					      */
;/*								      */
;/* FUNCTION:  INSERTS VARIABLE TEXT INTO THE BODY OF A MESSAGE       */
;/*								      */
;/* NOTES:							      */
;/*	    VARIABLE DATA INSERTION DOES NOT DEPEND ON BLANK	      */
;/*	    CHARACTER DELIMITERS NOR ARE BLANKS AUTOMATICALLY	      */
;/*	    INSERTED.						      */
;/*								      */
;/*    DEPENDENCIES: NONE					      */
;/*								      */
;/*    RESTRICTIONS: NONE					      */
;/*								      */
;/*    PATCH LABEL: (TOP SOURCE FILE ONLY)			      */
;/*								      */
;/* ENTRY POINTS:    DOSINSMESSAGE				      */
;/*								      */
;/* INTERNAL  REFERENCES:					      */
;/*								      */
;/*		   TEXT_SUBST - DO TEXT SUBSTITUTIONS		      */
;/*		   COPY_MESSSAGE - COPY MESSAGE TO BUFFER	      */
;/*		   copy_single	 - copy single byte to callers buffer */
;/*		   copy_double	 - copy double byte to callers buffer */
;/*		   is_lead	 - check for lead byte		      */
;/*								      */
;/*								      */
;/* EXTERNAL REFERENCES:					      */
;/* |2A 		  DOSGETDBCSEV				      */
;/*|2A		   CHK_LEAD_BYTE - CHECK LEAD BYTE		      */
;/*********************** END OF SPECIFICATIONS ***********************/
PAGE
;
;***********************************************************************
; EXTERNALS
;***********************************************************************
;
	extrn	chk_lead_byte:near
	extrn	check_dbcs:near
IFDEF FamilyAPI
	extrn	TXT_MSG_MR_IVCOUNT_ERROR:word	 ; DCR 704
ENDIF
;;	extrn	geterrmsg:near
;;	extrn	decdump:near
IFNDEF	FamilyAPI
	extrn	msgtrace:near
	EXTRN	DOSSYSTRACE:FAR
;***********************************************************************
; DCR900
;***********************************************************************
	EXTRN	DOSGETCP:FAR			 ; DCR900
	EXTRN	DOSGETMESSAGE:FAR		 ; DCR900
	EXTRN	RAMFIND:NEAR			 ; DCR900
ENDIF	;FamilyAPI
;
SUBTTL Includes
	PAGE
;---------------------------------------------------------
; INCLUDES HERE
;---------------------------------------------------------
	INCLUDE msgfequ.inc		;EQUATES
	INCLUDE msgfmac.inc		;MACROS
.XLIST
	INCLUDE error.inc		;ERROR CODES
	INCLUDE error2.inc		;new ERROR CODES
	INCLUDE basemaca.inc		;DOS MACROS
;;	INCLUDE basemid.inc		;symbols for error msg id's
	include dc1segs.inc			; DCR 704
.LIST
;
SUBTTL Data and Code Segment Definitions and Local Variables
;
;***********************************************************************
; THE FOLLOWING DATA SEGMENT DEFINITION IS USED FOR ACCESSING MESSAGES
; TO BE RETURNED WITH AN ERROR CODE.
;***********************************************************************
;
IFNDEF	 FamilyAPI
msgseg		segment
ELSE
msgseg		segment para public 'data'
ENDIF	;FamilyAPI
msgseg		ends
;
;;;;;DCR361
IFNDEF	FamilyAPI
iseg	SEGMENT
ELSE
iseg	SEGMENT 		para public 'data'
ENDIF	;FamilyAPI
	EXTRN	COUNTRY_CODE:DWORD
	EXTRN	FROM_GETMSG:BYTE
	EXTRN	DBCS_YES:BYTE
;***********************************************************************
;  DCR900
;***********************************************************************
IFNDEF	FamilyAPI				; DCR900
	EXTRN	err_component:BYTE		; DCR900
	EXTRN	err_filename:BYTE		; DCR900
ENDIF	;FamilyAPI				; DCR900
iseg	ENDS

;;;;;;;;;;;
;***********************************************************************
; CODE SEGMENT DEFINITION
;***********************************************************************
code	SEGMENT
	ASSUME	CS:code,DS:msgseg
;
;***********************************************************************
;	DECLARE DOSINSMESSAGE PUBLIC
;	DECLARE DOSINSMESSAGE PROC FAR
;***********************************************************************
	PROCEDURE	DOSINSMESSAGE,FAR
;
;***********************************************************************
; CREATE MASK FOR PARAMETERS PASSED ON THE STACK
;***********************************************************************
	ARGVAR	MSGLENGTH,DWORD 	;ADDRESS OF LENGTH OF MESSAGE RETURNED
	ARGVAR	DATALENGTH,WORD 	;MESSAGE BUFFER LENGTH
	ARGVAR	DATAAREA,DWORD		;MESSAGE BUFFER ADDRESS
	ARGVAR	MSGINLENGTH,WORD	;LENGTH OF INPUT MESSAGE
	ARGVAR	MSGINPUT,DWORD		;ADDRESS OF INPUT MESSAGE
	ARGVAR	IVCOUNT,WORD		;VARIABLE TEXT COUNT
	ARGVAR	IVTABLE,DWORD		;VARIABLE TEXT TABLE ADDRESS
;***********************************************************************
; END OF INPUT PARAMETERS
;***********************************************************************
;***********************************************************************
; CREATE LOCAL VARIABLE MASK ON THE STACK
;***********************************************************************
LOCALVAR	SYS_ERROR,WORD			;SYSTEM ERROR FIELD
LOCALVAR	SAV_ERROR,WORD			;SAVED ERROR FIELD
;
;***************************************************************************
LOCALVAR	IS_DBCS,BYTE			;DBCS FLAG: 1= DBCS, 0= NOT DBCS
;***********************************************************************
; SUPPORT FOR ERROR MESSAGE RETURNED
;
;;LOCALVAR	  ERR_IVTABLE,DWORD		  ;LOCAL IVTABLE FOR ERROR PROCESSING
;;LOCALVAR	  blank_string,BYTE		  ;blank message for output
;;LOCALVAR	  mid_string,6			  ;buffer for ascii message id for output
;;LOCALVAR	  COMPONENT,9			  ;storage for component id, message id, ': '
;
;***********************************************************************
; SUPPORT FOR DCR900
;***********************************************************************
;
IFNDEF	FamilyAPI				; DCR900
LOCALVAR	CPDATA,6			; DCR900
LOCALVAR	CPRET,WORD			; DCR900
ENDIF	;FamilyAPI				; DCR900
;
;***********************************************************************
; END OF LOCAL VARIABLES
;***********************************************************************
;
SUBTTL DosInsMessage Main Routine
PAGE
;/********************** START OF SPECIFICATIONS *********************/
;/*								     */
;/* SUBROUTINE NAME:  DOSINSMESSAGE				     */
;/*								     */
;/* DESCRIPTIVE NAME:  RETURN SYSTEM MESSAGE WITH VARIABLE TEXT      */
;/*		       INSERTED 				     */
;/*								     */
;/* FUNCTION: RETRIEVES A MESSAGE FROM THE SPECIFIED SYSTEM MESSAGE  */
;/*	      FILE AND INSERTS VARIABLE INFORMATION INTO THE BODY OF */
;/*	      THE MESSAGE.					     */
;/*								     */
;/* NOTES:							     */
;/*	    VARIABLE DATA INSERTION DOES NOT DEPEND ON BLANK	     */
;/*	    CHARACTER DELIMITERS NOR ARE BLANKS AUTOMATICALLY	     */
;/*	    INSERTED.						     */
;/*								     */
;/* RESTRICTIONS: NONE						     */
;/*								     */
;/* ENTRY POINT:  DOSINSMESSAGE 				     */
;/*    LINKAGE:   CALL FAR					     */
;/*								     */
;/* INPUT:  IVTABLE  =	VARIABLE TEXT TABLE ADDRESS  (DOUBLE WORD)   */
;/*	    IVCOUNT  =	VARIABLE TEXT COUNT  (WORD)		     */
;/*	    DATAAREA =	MESSAGE BUFFER ADDRESS	(DOUBLE WORD)	     */
;/*	    DATALENGTH = MESSAGE BUFFER LENGTH	(WORD)		     */
;/*	    MSGINLENGTH  = LENGTH OF INPUT MESSAGE (WORD)	     */
;/*	    MSGINPUT = ADDRESS OF INPUT MESSAGE (DOUBLE WORD)	     */
;/*|C	    MSGLENGTH = ACTUAL MESSAGE LENGTH RETURNED (DOUBLE WORD) */
;/*								     */
;/* EXIT-NORMAL:  AX = 0					     */
;/*								     */
;/*		  DATAAREA POINTS TO MESSAGE BUFFER WHICH CONTAINS   */
;/*		  MESSAGE WITH INSERTED TEXT.			     */
;/*|C		  MSGLENGTH POINTS TO ACTUAL LENGTH OF THE MESSAGE   */
;/*|C		  RETURNED.					     */
;/*								     */
;/* EXIT-ERROR:   AX = RETURN CODE				     */
;/*								     */
;/*								     */
;/*|D  ERROR_MR_MSG_TOO_LONG	 Message too long for buffer	     */
;/*|D  ERROR_MR_INV_IVCOUNT	 Invalid insertion variable count    */
;/*								     */
;/* EFFECTS:  NONE						     */
;/*								     */
;/* INTERNAL REFERENCES:					     */
;/*    ROUTINES:						     */
;/*		   COPY_MESSSAGE - COPY MESSAGE TO BUFFER	     */
;/*								     */
;/* EXTERNAL REFERENCES:					     */
;/*    ROUTINES:						     */
;/*|2A			  DOSGETDBCSEV				     */
;/*********************** END OF SPECIFICATIONS **********************/
;PAGE
;
IFNDEF	FamilyAPI
	ENTERPROC			;SETUP BP
ELSE
	RealEnter			;setup bp
ENDIF	;FamilyAPI
;
;-----------------------------------------------------------------
; SAVE CALLERS ENVIRONMENT
;-----------------------------------------------------------------
;
	SAVE_CALLER
;
	CLD				;CLEAR DIRECTION FLAG
;
;***DT	preDOSINSMESSAGE - DosInsMessage pre-invocation trace point
;
;    TRACE MINOR = <RAS_DC1_PRE_DOSINSMESSAGE>,
;	   TP=.PREINSMSG,
;	   TYPE=(PRE,API),
;	   GROUP = MSG,
;	   DESC="(OS) DosInsMessage Pre-Invocation",
;	   FMT="IV Count = %PW",
;	   FMT="Length of Input Message %PW",
;	   MEM=(RSS+BP+22,DIRECT,2),
;	   MEM=(RSS+BP+16,DIRECT,2),

IFNDEF	FamilyAPI
;****************************
;	Trace hook
;	PreInvocation - DosInsmessage

	push	TraceCode_PreInsMessage
	push	0
	push	msginlength
	push	ivcount
	call	MSGTRACE
;
;****************************
ENDIF	;FamilyAPI

	PUBLIC PREINSMSG

	MOV	SYS_ERROR,0		;INITIALIZE ERROR VARIABLE TO ZERO
PREINSMSG:
	MOV	SAV_ERROR,0		;INITIALIZE SAVED ERROR VARIABLE TO ZERO
;
;*************************************************************************
;  SUPPORT FOR DBCS
;
	MOV	IS_DBCS,0		      ;0 = NOT DBCS
;
; CALL DOSGETDBCSEV - GET DBCS ENVIRONMENT VECTOR;
;
;;;;;DCR361
	PUSH	DS			;Save DS
	MOV	AX,ISEG 		;Get addressability to iseg
	MOV	DS,AX
	ASSUME	DS:ISEG
	cmp	from_getmsg,1		;check call from DosGetMessage
	jne	cont1
	cmp	dbcs_yes,1		;check DBCS flag
	POP	DS
	ASSUME	DS:MSGSEG
	jne	continue
	jmp short cont2
cont1:
	POP	DS
;;;;;;;;;;;
	CALL	check_dbcs		;GET DBCS ENVIRONMENT VECTOR
	jnc	CONTINUE		;IF NOT CARRY, SINGLE BYTE
cont2:
	MOV	IS_DBCS,1		;ELSE DOUBLE BYTE
;
;***********************************************************************
CONTINUE:
;
;     GET IVCOUNT;
;	IVCOUNT IS IN IVCOUNT
;
	MOV	AX,IVCOUNT		   ;AX = IVCOUNT
;
;	  GET LOCAL_BUFFER POINTER FROM CALLER;
;
	LDS	SI,MSGINPUT			;DS:SI -> LOCAL_BUFFER
;
;	GET LOCAL_BUFFER_LEN FROM CALLER;
;	LOCAL_BUFFER_LEN = MSGINLENGTH
;	MSGINLENGTH IS IN SS:MSGINLENGTH
;
	MOV	BX,MSGINLENGTH			;BX = LOCAL_BUFFER_LEN
;
;
;     CALL COPY_MESSAGE - COPY MESSAGE INTO CALLER'S BUFFER;
	CALL	COPY_MESSAGE
;
; IF SYS_ERROR <> ERROR_MR_MSG_TOO_LONG AND SYS_ERROR <> 0 THEN
;   DO;
;    SAV_ERROR = ERRORR_MR_INV_IVCOUNT;
;    IVCOUNT = 0;
;    CALL COPY_MESSAGE;
;    SYS_ERROR = ERROR_MR_INV_IVCOUNT;
;   END;
;
	CMP	SYS_ERROR,0			;ERROR ?
	jnz	chk_err1			;yes, check which one
	jmp	GET_OUT 			;else get out
chk_err1:
	CMP	SYS_ERROR,ERROR_MR_MSG_TOO_LONG ;MSG TOO LONG FOR BUFFER ?
	jnz	chk_err2			;no, do error processing
	jmp	GET_OUT 			;else get out
chk_err2:
	MOV	AX,SYS_ERROR			;else save current error
	MOV	SAV_ERROR,AX			;
	MOV	SYS_ERROR,0			;reset sys_error

IFDEF	 FamilyAPI				; DCR900
;***********************************************************************
; The following code were for FamilyAPI only.  DCR900
;***********************************************************************
;						; DCR900
;	get resident error message		; DCR900
;						; DCR900
	mov	ax,msgseg			; DCR900
	mov	ds,ax				; DCR900
	assume	ds:msgseg			; DCR900
;						; DCR900
;	  GET ERROR MESSAGE LENGTH;		; DCR900
;	  GET ERROR MESSAGE TEXT;		; DCR900
;						; DCR900
	LEA	SI,TXT_MSG_MR_IVCOUNT_ERROR	; DCR900
	mov	bx,word ptr [si]		; DCR900
	add	si,2				; DCR900
ELSE						; DCR900
;***********************************************************************
; The following code were for NON-FamilyAPI only.  DCR900
;***********************************************************************
;						; DCR900
	mov	ax,CP_LENGTH			; DCR900
	push	ax				; DCR900
	lea	ax,CPDATA			; DCR900
	push	ss				; DCR900
	push	ax				; DCR900
	lea	ax,CPRET			; DCR900
	push	ss				; DCR900
	push	ax				; DCR900
	call	DOSGETCP			; DCR900
;						; DCR900
; setup to call RAMFIND 			; DCR900
;						; DCR900
	mov	ax,iseg 			; DCR900
	push	ax				; DCR900
	lea	ax,iseg:err_component		; DCR900
	push	ax				; DCR900
	mov	ax,iseg 			; DCR900
	push	ax				; DCR900
	lea	ax,iseg:err_filename		; DCR900
	push	ax				; DCR900
	mov	ax,seg DOSGETMESSAGE		; DCR900
	push	ax				; DCR900
	mov	ax,offset DOSGETMESSAGE 	; DCR900
	sub	ax,BOUNDHDRSIZE 		; DCR900
	push	ax				; DCR900
	push	SAV_ERROR			; DCR900
	push	ss				; DCR900
	lea	ax,CPDATA			; DCR900
	push	ax				; DCR900
	call	RAMFIND 			; DCR900
	jc	get_out 			; DCR900
	mov	bx,cx				; DCR900
;						; DCR900
ENDIF						; DCR900
;***********************************************************************
; The following code were common for both API. DCR900
;***********************************************************************
;
	sub	ax,ax				;ivcount = 0
	CALL	COPY_MESSAGE

	cmp	sys_error,0			;if new error
	jnz	get_out 			;then return new error
						;else
	MOV	AX,SAV_ERROR			;RESTORE old ERROR
	MOV	SYS_ERROR,AX
;
; ELSE;
;
GET_OUT:
;
	MOV	AX,SYS_ERROR			;GET ERROR CODE FOR RETURN

;
;***DT	postDOSINSMESSAGE - DosInsMessage post-invocation trace point
;
;    TRACE MINOR = <RAS_DC1_POST_DOSINSMESSAGE>,
;	   TP=.POSTINSMSG,
;	   TYPE=(POST,API),
;	   GROUP = MSG,
;	   DESC="(OS) DosInsMessage Post-Invocation",
;	   FMT="Return Code = %W",
;	   FMT="Message Length = %PW"
;	   REGS=(AX),
;	   MEM=(RSS+BP+6,INDIRECT,2),

IFNDEF	FamilyAPI
;****************************
;	Trace hook
;	PostInvocation - DosInsmessage

	push	TraceCode_PostInsMessage
	push	0
	lds	si,msglength
	mov	bx,[si]
	push	bx
	push	ax
	call	MSGTRACE
;
;****************************
ENDIF	;FamilyAPI

	PUBLIC POSTINSMSG
POSTINSMSG:

	RESTORE_CALLER	22
DOSINSMESSAGE	ENDP
;********************************************************************
SUBTTL Copy_Message Routine
PAGE
;/********************** START OF SPECIFICATIONS *********************/
;/*								     */
;/* SUBROUTINE NAME:  COPY_MESSAGE				     */
;/*								     */
;/* DESCRIPTIVE NAME:  COPY MESSAGE INTO CALLER'S BUFFER             */
;/*								     */
;/* FUNCTION: COPIES THE MESSAGE TEXT READ INTO THE LOCAL BUFFER     */
;/*	      TO CALLER'S BUFFER AND CALLS TEXT_SUBST TO DO          */
;/*	      VARIABLE TEXT INSERTIONS IF NEEDED.		     */
;/*								     */
;/* NOTES:  IF DBCS ENVIRONMENT THEN DB CHARACTERS ARE COPIED	     */
;/*	    WHEN SCANNING FOR % (INSERTION VARIABLE).		     */
;/*								     */
;/* RESTRICTIONS: NONE						     */
;/*								     */
;/* ENTRY POINT:  COPY_MESSAGE					     */
;/*    LINKAGE:   CALL NEAR					     */
;/*								     */
;/* INPUT:	  AX = ivcount					     */
;/*		  BX = msg length				     */
;/*		  DS:SI -> input message			     */
;/*								     */
;/* EXIT-NORMAL:  NONE						     */
;/*								     */
;/*								     */
;/* EXIT-ERROR:   SYS_ERROR = RETURN CODE			     */
;/*								     */
;/*								     */
;/* ERROR_MR_MSG_TOO_LONG   Message too long for buffer 	     */
;/*								     */
;/* EFFECTS:  NONE						     */
;/*								     */
;/* INTERNAL REFERENCES:					     */
;/*    ROUTINES:	  TEXT_SUBST				     */
;/*		  copy_single	- copy single byte to callers buffer */
;/*		  copy_double	- copy double byte to callers buffer */
;/*		  is_lead	- check for lead byte		     */
;/*								     */
;/* EXTERNAL REFERENCES:					     */
;/*|   ROUTINES:						     */
;/*			  CHK_LEAD_BYTE 			     */
;/*********************** END OF SPECIFICATIONS **********************/
 PAGE
COPY_MESSAGE	PROC	NEAR
;
;     GET CALLER'S_BUFFER POINTER;
;
	LES	DI,DATAAREA			;ES:DI -> CALLER'S_BUFFER
;
;     GET CALLER'S_BUFFER_LEN;
;
	MOV	CX,DATALENGTH			;CX = CALLER'S BUFFER LEN
;
;     CC = CURRENT_CHAR;
;	SI = OFFSET INTO LOCAL_BUFFER (CURRENT CHARACTER POINTER)
;
	XOR	DX,DX				;MSG_LEN = 0
;
;     IF IV_COUNT >= 0 AND IVCOUNT <= 9 THEN
;
	or	ax,ax
	jae	chek_9
	jmp	inv_ivcount
chek_9:
	CMP	AX,9
	jbe	ivcount_ok
	jmp	inv_ivcount
;
;     DO;
;|4   IVCOUNT_ASC = IVCOUNT CONVERTED TO ASCII;
;
ivcount_ok:

	MOV	AH,AL
	ADD	AH,30H			;AH = IVCOUNT_ASC
;
;|F<-	 DO WHILE LOCAL_BUFFER_LEN > 0;
;
COPY_LOOP:
	or	bx,bx			;BX = LOCAL_BUFFER_LEN
	jnz	copy_cont
	jmp	COPY_DONE
;
;	    IF CALLER'S_BUFFER_LEN > 0 THEN
;
copy_cont:
	or	cx,cx
	JZ	MESS_TOO_LONG
;
;************************************************************************
;  SUPPORT FOR DBCS
;
;	IF DBCS THEN
;
	call	Is_Lead 			; do we have a lead byte ?
	jnc	ins_parm_chk			; NO, SO SEE IF IT IS '%'
;
;	  IF LEAD BYTE AND CALLER_BUFFER_LEN >= 2 THEN
;	     MOVE 2 BYTES FROM LOCAL_BUFFER TO CALLER'S BUFFER;
;	     DECREMENT LOCAL_BUFFER_LEN;
;	     DECREMENT CALLER'S_BUFFER_LEN;
;	     BUMP POINTERS;
;
	call	Copy_Double			; copy dbcs charater
	jc	mess_too_long			; NOT ENOUGH ROOM FOR DB CHAR
	JMP	COPY_LOOP			; get next character
;
INS_PARM_CHK:
;***********************************************************************
;
;		 IF LOCAL_BUFFER(CC) *= "%" THEN
;
	CMP	BYTE PTR [SI],"%"
	JZ	SUBST_TEXT
;
;		  DO;
;		    MOVE BYTE FROM LOCAL_BUFFER TO CALLER'S_BUFFER;
;		    DECREMENT LOCAL_BUFFER_LEN;
;		    DECREMENT CALLER'S_BUFFER_LEN;
;		    BUMP POINTERS;
;
	call	Copy_Single			; copy single character
	JMP	COPY_LOOP			; get next character
;
;		  END;
;
SUBST_TEXT:
;
;		 ELSE
;|G		    IF LOCAL_BUFFER_LEN > 1 AND LOCAL_BUFFER(CC+1) >="1" AND
;|4   LOCAL_BUFFER(CC+1) <= IVCOUNT_ASC THEN
;
	CMP	BX,1
	JZ	NO_SUBST
	CMP	BYTE PTR [SI]+1,"1"
	JB	NO_SUBST
	CMP	BYTE PTR [SI]+1,AH
	JA	NO_SUBST
;
;		       DO;
;			REPLACE_PARM = LOCAL_BUFFER(CC+1);
;			CONVERT REPLACE_PARM TO INTEGER;
;			CALL TEXT_SUBST - DO TEXT SUBSTITUTION;
;
	MOV	AL,BYTE PTR [SI]+1
	SUB	AL,30H
	CALL	TEXT_SUBST
;
	JC	COPY_DONE			;MSG TOO LONG FOR BUFFER
	ADD	SI,2				;ELSE SKIP OVER %n CHAR SEQUENCE
	SUB	BX,2				;ADJUST LOCAL_BUFF_LEN
	JMP	COPY_LOOP
;
;		       END;
;
NO_SUBST:
;
;************************************************************************
;  SUPPORT FOR DBCS
;
;	IF DBCS THEN
;
	call	Is_Lead 			; do we have a lead byte ?
	jnc	skip_subst			; NO, SO SKIP SUBSTITUTION
;
;	  IF LEAD BYTE AND CALLER_BUFFER_LEN >= 2 THEN
;	     MOVE 2 BYTES FROM LOCAL_BUFFER TO CALLER'S BUFFER;
;	     DECREMENT LOCAL_BUFFER_LEN;
;	     DECREMENT CALLER'S_BUFFER_LEN;
;	     BUMP POINTERS;
;
	call	Copy_Double			; copy dbcs character
	jc	mess_too_long			; NOT ENOUGH ROOM FOR DB CHAR
	JMP	COPY_LOOP			; get next character
;
SKIP_SUBST:
;***********************************************************************
;
;		    ELSE
;		       DO;
;			MOVE BYTE FROM LOCAL_BUFFER TO CALLER'S_BUFFER;
;			DECREMENT LOCAL_BUFFER_LEN;
;			DECREMENT CALLER'S_BUFFER_LEN;
;			BUMP POINTERS;
;
	call	Copy_Single			; copy single character
	JMP	COPY_LOOP			; get next character
;
;		       END;
MESS_TOO_LONG:
;
;	    ELSE
;	      DO;
;|2A		ADD CR/LF TO BUFFER;
;|B<-		RETURN ERROR_MR_MSG_TOO_LONG;
;
	cmp	datalength,2			;is output buffer large enough fo CR/LF ?
	jb	no_cr_lf			;not enough room for CR/LF
	cmp	is_dbcs,1			; is dbcs turned ON?
	jz	no_cr_lf			; yes, so don't add CR/LF

	PUSH	AX
	SUB	DI,2
	MOV	AX,LF*256+CR	       ;PUT CR,LF IN AX
	STOSW			       ;STORE IT IN BUFFER
	POP	AX
;
no_cr_lf:

	MOV	SYS_ERROR,ERROR_MR_MSG_TOO_LONG
	JMP	COPY_DONE
;
;	      END;
;|F<-	 END;
;      END;
;
INV_IVCOUNT:
;
;     ELSE
;|D	 RETURN ERROR_MR_INV_IVCOUNT;
;
	MOV	SYS_ERROR,ERROR_MR_INV_IVCOUNT
;
;END COPY_MESSAGE;
COPY_DONE:
;
	LES	DI,MSGLENGTH				;ES:DI -> MSG_LEN
	MOV	AX,DX
	STOSW						;SAVE MSG_LEN
;
	RET
COPY_MESSAGE	ENDP
;********************************************************************
SUBTTL Text_Subst Routine
PAGE
;/********************** START OF SPECIFICATIONS *********************/
;/*								     */
;/* SUBROUTINE NAME:  TEXT_SUBST				     */
;/*								     */
;/* DESCRIPTIVE NAME:  DO TEXT SUBSTITUTIONS			     */
;/*								     */
;/* FUNCTION: COPY TEXT STRINGS POINTED BY IVTABLE ENTRIES INTO      */
;/*	      CALLER'S BUFFER, USING REPLACEABLE PARAMETER VALUE     */
;/*	      FOUND IN MESSAGE TEXT AS INDEX INTO IVTABLE.	     */
;/*								     */
;/* NOTES:  IF IVCOUNT IS SMALLER THAN THE VALUE OF THE REPLACEABLE  */
;/*	    PARAMETER FOUND IN THE MESSAGE TEXT, THEN STRING	     */
;/*	    INSERTION IS IGNORED.				     */
;/*								     */
;/*								     */
;/* RESTRICTIONS: NONE						     */
;/*								     */
;/* ENTRY POINT:  TEXT_SUBST					     */
;/*    LINKAGE:   CALL NEAR					     */
;/*								     */
;/* INPUT:  AH = IVCOUNT					     */
;/*	    AL = REPLACEABLE PARAMETER VALUE			     */
;/*	    CX = CALLER'S BUFFER LENGTH                              */
;/*								     */
;/*	    ES:DI = CALLER'S BUFFER POINTER                          */
;/* |L<--							     */
;/*								     */
;/* EXIT-NORMAL:  AX = 0					     */
;/*		  CRFL = 0					     */
;/*								     */
;/* EXIT-ERROR:   RETURN CODE IN SYS_ERROR			     */
;/*		  CRFL = 1					     */
;/*								     */
;/*|D  ERROR_MR_MSG_TOO_LONG	 Message too long for buffer	     */
;/*								     */
;/* EFFECTS:  NONE						     */
;/*								     */
;/* INTERNAL REFERENCES:					     */
;/*    ROUTINES:						     */
;/*		  is_lead	- check for lead byte		     */
;/*								     */
;/* EXTERNAL REFERENCES:					     */
;/* |  ROUTINES:  none						     */
;/*********************** END OF SPECIFICATIONS **********************/
 PAGE
TEXT_SUBST	PROC	NEAR
;      GET IVTABLE POINTER;
;	SS:IVTABLE  --> IVTABLE
;
;      GET CALLER'S_BUFFER POINTER;
;	ES:DI -> CALLER'S BUFFER
;
;      GET CALLER'S_BUFFER_LEN;
;	CX = CALLER'S BUFFER LEN
;
;      GET REPLACE_PARM;
;	AL = REPLACE PARM
;
;      INITIALIZE IVTABLE.REPLACE_PARM POINTER;
;
;	SAVE LOCAL_BUFFER POINTER
;
	PUSH	DS
	PUSH	SI
	PUSH	BX			;SAVE BX
;
;	CURRENT DESIGN:     IVTABLE --> OFFSET:SELECTOR --> STRING1
;					OFFSET:SELECTOR --> STRING2
;					 .	     .
;					 .	     .
;
	LDS	SI,IVTABLE		;DS:SI -> INSERTION TEXT TABLE
	MOV	BX,AX			;CONVERT REPLACE PARM INTO INDEX
	AND	BX,00FFH		;SINCE AL = REPLACE PARM
	DEC	BX			;ADJUST INDEX (1RST ENTRY = INDEX 0)
IFDEF	FamilyAPI
	shl	BX,1
	shl	BX,1
ELSE
	SHL	BX,2			;MULT BY ENTRY SIZE (4 bytes)
ENDIF	;FamilytAPI
	LDS	SI,[SI+BX]		;DS:SI -> REQUESTED STRING
;
	mov	bx, si			;set max string length
	not	bx			;64k - offset of start
;
;	   DO WHILE IVTABLE.REPLACE_PARM(CC) *= NUL;
;
SUBST_LOOP:
;
	or	bx,bx			; check for end of segment (truncate)
	JZ	SUBST_DONE
;
	CMP	BYTE PTR [SI],0
	JZ	SUBST_DONE
;
;	       IF CALLER'S_BUFFER_LEN > 0 THEN
;
	or	cx,cx
	JZ	TXT_TOO_LONG
;
;************************************************************************
;  SUPPORT FOR DBCS
;
;	IF DBCS THEN
;
	call	Is_Lead 			; do we have a lead byte ?
	jnc	single_byte_ins 		; NO, SO JMP TO SINGLE BYTE ROUTINE

; since we know that the character we are looking at must be a lead
; byte, the buffer must have a length of 2, else it is malformed. We attempt
; NOT to copy those malformed DBCS characters.

	cmp	bx,2				; do we have a valid DBCS char?
	jae	db_ok_txt			; yes, so copy character
	xor	bx,bx				; force wer'e done
	jmp	short subst_done		; exit

;
;	  IF LEAD BYTE AND CALLER_BUFFER_LEN >= 2 THEN
;	     MOVE 2 BYTES FROM IVTABLE.REPLACE_PARM(CC) TO CALLER'S BUFFER
;	     DECREMENT CALLER'S_BUFFER_LEN;
;	     BUMP POINTERS;
;
db_ok_txt:
	CMP	CX,2
	JB	TXT_TOO_LONG			;NOT ENOUGH ROOM FOR DB CHAR

	cmp	byte ptr [si+1],0		;is trailing byte End of String? (malformed DBCS?)
	jz	single_byte_ins 		;yes, so move byte only !

	MOVSW					;else good DB char
	sub	bx,2				; decrement insertion string len
	SUB	CX,2				; decrement caller's buffer len
	ADD	DX,2				;INCREMENT MSG_LEN
	JMP	SUBST_LOOP
;
;
SINGLE_BYTE_INS:
;***********************************************************************
;
;		 DO;
;		   MOVE BYTE FROM IVTABLE.REPLACE_PARM(CC) TO CALLER'S BUFFER;
;		   DECREMENT CALLER'S_BUFFER_LEN;
;		   BUMP POINTERS;
;
	MOVSB
	dec	bx				; decrement insertion string len
	DEC	CX				; decrement caller's buffer len
	INC	DX				;INCREMENT MSG_LEN
	JMP	SUBST_LOOP

;
;		 END;
;
TXT_TOO_LONG:
;
;	       ELSE
;		 DO;
;|2A		   ADD CR/LF TO BUFFER;
;|B<--		   RETURN ERROR_MR_MSG_TOO_LONG;
;
	cmp	is_dbcs,1			; is dbcs turned ON?
	jz	ins_ret_err			; yes, so don't add CR/LF

	PUSH	AX
	SUB	DI,2
	MOV	AX,LF*256+CR	       ;PUT CR,LF IN AX
	STOSW			       ;STORE IT IN BUFFER
	POP	AX

ins_ret_err:
	MOV	SYS_ERROR,ERROR_MR_MSG_TOO_LONG
	STC						;SET CARRY
;
;		 END;
;	  END;
SUBST_DONE:
;
;END TEXT_SUBST;
;
	POP	BX				;RESTORE BX
	POP	SI				;RESTORE LOCAL BUFFER POINTER
	POP	DS
	RET
TEXT_SUBST	ENDP
;********************************************************************
SUBTTL Copy_Single	Routine
PAGE
;/********************** START OF SPECIFICATIONS *********************/
;/*								     */
;/* SUBROUTINE NAME:  Copy_Single				     */
;/*								     */
;/* DESCRIPTIVE NAME:  copies a single byte from local_buffer to     */
;/*		       caller's buffer.                              */
;/* FUNCTION: copies a single byte from local_buffer into caller's  .*/
;/*	      buffer. Updates message length.			     */
;/* NOTE:							     */
;/*								     */
;/* RESTRICTIONS: NONE						     */
;/*								     */
;/* ENTRY POINT:  COPY_SINGLE					     */
;/*    LINKAGE:   CALL NEAR					     */
;/*								     */
;/* INPUT:							     */
;/*	       DS:SI -> character in local buffer		     */
;/*	       ES:DI -> caller's buffer                              */
;/*	       bx = msginlength 				     */
;/*	       cx = datalength					     */
;/*	       dx = msg_len					     */
;/*								     */
;/* EXIT-NORMAL:						     */
;/*		  none						     */
;/*								     */
;/* EXIT-ERROR: 						     */
;/*		  none						     */
;/*								     */
;/* EFFECTS:  advances SI, DI, decrements bx, cx, increments dx      */
;/*								     */
;/* INTERNAL REFERENCES:					     */
;/*    ROUTINES: NONE						     */
;/*								     */
;/* EXTERNAL REFERENCES:					     */
;/*    ROUTINES: none						     */
;/*********************** END OF SPECIFICATIONS **********************/
 PAGE
Copy_Single PROC    NEAR

;		  DO;
;		    MOVE BYTE FROM LOCAL_BUFFER TO CALLER'S_BUFFER;
;		    DECREMENT LOCAL_BUFFER_LEN;
;		    DECREMENT CALLER'S_BUFFER_LEN;
;		    BUMP POINTERS;
;
	MOVSB			; copy single character
	DEC	BX		; decrement local buffer len
	DEC	CX		; decrement datalength
	INC	DX		; increment msg_len

	RET

Copy_Single ENDP
;***************************************************************************
SUBTTL Copy_Double	Routine
PAGE
;/********************** START OF SPECIFICATIONS *********************/
;/*								     */
;/* SUBROUTINE NAME:  Copy_Double				     */
;/*								     */
;/* DESCRIPTIVE NAME:  copies a double byte from local_buffer to     */
;/*		       caller's buffer.                              */
;/* FUNCTION: copies a double byte from local_buffer into caller's  .*/
;/*	      buffer. Updates message length.			     */
;/* NOTE:							     */
;/*								     */
;/* RESTRICTIONS: NONE						     */
;/*								     */
;/* ENTRY POINT:  COPY_DOUBLE					     */
;/*    LINKAGE:   CALL NEAR					     */
;/*								     */
;/* INPUT:							     */
;/*	       DS:SI -> character in local buffer		     */
;/*	       ES:DI -> caller's buffer                              */
;/*	       bx = msginlength 				     */
;/*	       cx = datalength					     */
;/*	       dx = msg_len					     */
;/*								     */
;/* EXIT-NORMAL:						     */
;/*		  carry clear					     */
;/*								     */
;/* EXIT-ERROR: 						     */
;/*		  carry set - message too long			     */
;/*								     */
;/* EFFECTS:  advances SI, DI, decrements bx, cx, increments dx      */
;/*								     */
;/* INTERNAL REFERENCES:					     */
;/*    ROUTINES: NONE						     */
;/*								     */
;/* EXTERNAL REFERENCES:					     */
;/*    ROUTINES: none						     */
;/*********************** END OF SPECIFICATIONS **********************/
 PAGE
Copy_Double PROC    NEAR

;
; since we know that the character we are looking at must be a lead
; byte, the buffer must have a length of 2, else it is malformed. We attempt
; NOT to copy those malformed DBCS characters.

	cmp	bx,2				; do we have a valid DBCS char?
	jae	db_ok				; yes, so copy character
	xor	bx,bx				; force wer'e done  (carry clear)
	jmp	short no_db_room		; exit

;	  IF LEAD BYTE AND CALLER_BUFFER_LEN >= 2 THEN
;	     MOVE 2 BYTES FROM LOCAL_BUFFER TO CALLER'S BUFFER;
;	     DECREMENT LOCAL_BUFFER_LEN;
;	     DECREMENT CALLER'S_BUFFER_LEN;
;	     BUMP POINTERS;

db_ok:
	CMP	CX,2				; enough room for db character?
	JB	no_db_room			; no, so skip copy and ret (cy set)
	MOVSW					; copy character
	SUB	BX,2				; decrement local buffer len
	SUB	CX,2				; decrement datalength
	ADD	DX,2				; increment msg_len
	clc					; clear carry - success

no_db_room:

	RET

Copy_Double ENDP
;***************************************************************************
SUBTTL Is_Lead	       Routine
PAGE
;/********************** START OF SPECIFICATIONS *********************/
;/*								     */
;/* SUBROUTINE NAME:  Is_Lead					     */
;/*								     */
;/* DESCRIPTIVE NAME:  determines whether the current character      */
;/*		       is a lead byte				     */
;/* FUNCTION: calls Chk_Lead_byte to determine if the character is   */
;/*	      a lead byte.					     */
;/* NOTE:							     */
;/*								     */
;/* RESTRICTIONS: NONE						     */
;/*								     */
;/* ENTRY POINT:  IS_LEAD					     */
;/*    LINKAGE:   CALL NEAR					     */
;/*								     */
;/* INPUT:							     */
;/*	       ds:si -> character in local buffer		     */
;/*								     */
;/* EXIT-NORMAL:						     */
;/*		  carry clear - single byte			     */
;/*		  carry set - double byte			     */
;/*								     */
;/* EXIT-ERROR:   none						     */
;/*								     */
;/* EFFECTS:  none						     */
;/*								     */
;/* INTERNAL REFERENCES:					     */
;/*    ROUTINES:	chk_lead_byte - check for lead byte	     */
;/*								     */
;/* EXTERNAL REFERENCES:					     */
;/*    ROUTINES:						     */
;/* |2A 		 CHK_LEAD_BYTE				     */
;/*********************** END OF SPECIFICATIONS **********************/
 PAGE
Is_Lead     PROC    NEAR

;	if dbcs then
;
	cmp	is_dbcs,1			; is dbcs turned ON?
	jz	chk_char			; yes, so check if lead byte
	clc					; no, set single byte
	jmp	short is_not_lead		; and exit
;
;	  call chk_lead_byte - check for lead byte;
;
chk_char:
	push	ax			;save count
	mov	al,byte ptr [si]	;move character in al
	call	chk_lead_byte		;check for lead byte char
	pop	ax			;restore count

is_not_lead:

	RET				; cy = Lead byte / nc = single byte

Is_Lead     ENDP
;***************************************************************************
;
code		ENDS
		END
