	PAGE 60,132;
	TITLE EDLCMD1.ASM
;/*
; *                      Microsoft Confidential
; *                      Copyright (C) Microsoft Corporation 1991
; *                      All Rights Reserved.
; */

;======================= START OF SPECIFICATIONS =========================
;
; MODULE NAME: EDLCMD1.SAL
;
; DESCRIPTIVE NAME: EDLIN ROUTINES
;
; FUNCTION: THIS MODULE PROVIDES ROUTINES NEEDED FOR EDLIN'S EXECUTION.
;
; ENTRY POINT: ANY CALLED ROUTINE
;
; EXIT NORMAL: NA
;
; EXIT ERROR : NA
;
; INTERNAL REFERENCES:
;
; EXTERNAL REFERENCES:
;
;	ROUTINE: EDLCMD2 - ROUTINES MAY BE CALLED FROM EDLCMD2
;		 EDLMES  - ROUTINES MAY BE CALLED FROM EDLMES
;
; NOTES: THIS MODULE IS TO BE PREPPED BY SALUT WITH THE "PR" OPTIONS.
;	 LINK EDLIN+EDLCMD1+EDLCMD2+EDLMES+EDLPARSE
;
; REVISION HISTORY:
;
;	AN000	VERSION DOS 4.00 - REVISIONS MADE RELATE TO THE FOLLOWING:
;
;					- IMPLEMENT SYSPARSE
;					- IMPLEMENT MESSAGE RETRIEVER
;					- IMPLEMENT DBCS ENABLING
;					- ENHANCED VIDEO SUPPORT
;					- EXTENDED OPENS
;					- SCROLLING ERROR
;
; COPYRIGHT: "MS DOS EDLIN UTILITY"
;	     "VERSION 4.00 (C) COPYRIGHT 1988 Microsoft"
;
;======================= END OF SPECIFICATIONS ===========================

include syscall.inc
include edlequ.asm

SUBTTL	Contants and Data areas
PAGE


CODE	SEGMENT PUBLIC
CODE	ENDS

CONST	SEGMENT PUBLIC WORD
CONST	ENDS

cstack	segment stack
cstack	ends

DATA	SEGMENT PUBLIC WORD
DATA	ENDS

DG	GROUP	CODE,CONST,cstack,DATA

CONST	SEGMENT PUBLIC WORD
	EXTRN	DSKFUL:abs,READ_ERR_PTR:word
	EXTRN	NOSUCH:abs,TOOLNG:abs,EOF:abs
	extrn	txt1:byte,txt2:byte
CONST	ENDS

cstack	segment stack
cstack	ends

DATA	SEGMENT PUBLIC WORD
	extrn	ParamCt:WORD
	extrn	current:word,pointer:word,start:word,endtxt:word
	extrn	wrt_handle:word,editbuf:byte,path_name:byte,fname_len:word
	extrn	arg_buf:byte,arg_buf_ptr:word
	extrn	olddat:byte,oldlen:word,newlen:word,param1:word,param2:word
	extrn	srchflg:byte,srchmod:byte,comline:word,lstfnd:word,numpos:word
	extrn	lstnum:word,srchcnt:word,amnt_req:word,delflg:byte,lastlin:word
	extrn	three4th:word,one4th:word,last_mem:word,rd_handle:word,ending:byte
	extrn	haveof:byte
	extrn	Disp_Len:Byte

DATA	ENDS

CODE SEGMENT PUBLIC
ASSUME	CS:DG,DS:DG,SS:CStack,ES:DG

	extrn	findlin:near,shownum:near,loadbuf:near
	extrn	delbak:near,unquote:near,lf:near
	extrn	dispone:near,display:near,query:near
	extrn	quit:near,scanln:near,scaneof:near
	extrn	fndfirst:near,fndnext:near,replace:near,memerr:near
	extrn	std_printf:near,chkrange:near,comerr:near
	extrn	display_message:near

	public	zerror
	public	xerror,bad_read,append,nocom,pager,list
	public	delete,replac_from_curr,search_from_curr,ewrite,wrt

NOMOREJ:JMP	NOMORE

APPEND:
	CMP	ParamCt,1
	JZ	AppendOK
	JMP	ComErr
AppendOK:
	TEST	BYTE PTR [HAVEOF],-1
	JNZ	NOMOREJ
	MOV	DX,[ENDTXT]
	CMP	[PARAM1],0	;See if parameter is missing
	JNZ	PARMAPP
	CMP	DX,[THREE4TH]	;See if already 3/4ths full
	jb	parmapp
	return			;If so, then done already
PARMAPP:
	MOV	DI,DX
	MOV	CX,[LAST_MEM]
	SUB	CX,DX		;Amount of memory available
	jnz	sj53
	jmp	memerr
sj53:
	MOV	DX,[ENDTXT]
	MOV	BX,[RD_HANDLE]
	mov	[amnt_req],cx	;Save number of chars requested
	MOV	AH,READ
	INT	21H		;Fill memory with file data
	CMP	CX,AX		;Did we read less than we asked for?
	JZ	SJ55
; Make sure this is an end-of-file by trying to read more
	PUSH	AX		;Save old byte count
	ADD	DX,AX		;Point to next open space in buffer
	MOV	CX,1		;Just one character past EOF
	MOV	AH,READ
	INT	21H
	CMP	AX,0		      ;Is it EOF?
	POP	AX
	JNZ	SJ54		      ;No -- we have one more character
	MOV	BYTE PTR [HAVEOF],1   ;Yes - set old style system call flag
	JMP	SHORT SJ55
SJ54:
	INC	AX		      ;Include one more char in byte count
sj55:
	MOV	CX,AX		      ;Want byte count in CX
	PUSH	CX		      ;Save actual byte count
	CALL	SCANEOF
	JNZ	NOTEND
	MOV	BYTE PTR [HAVEOF],1	;Set flag if 1AH found in file
NOTEND:
	XOR	DX,DX
	MOV	BX,[PARAM1]
	OR	BX,BX
	JNZ	COUNTLN
	MOV	AX,DI
	ADD	AX,CX		;First byte after loaded text
	CMP	AX,[THREE4TH]	;See if we made 3/4 full
	JBE	COUNTLN
	MOV	DI,[THREE4TH]
	MOV	CX,AX
	SUB	CX,DI		;Length remaining over 3/4
	MOV	BX,1		;Look for one more line
COUNTLN:
	CALL	SCANLN		;Look for BX lines
	CMP	[DI-1],AL	;Check for full line
	JZ	FULLN
	CMP	HavEof,1
	JNZ	DoBackScan
;
; We have an incomplete line in the buffer at end of file.  Fix it up to be
; pretty.
;
	MOV	BYTE PTR [DI],13	; CR
	MOV	BYTE PTR [DI+1],10	; LF
	ADD	DI,2			; length is 2 greater
	POP	CX
	ADD	CX,2
	PUSH	CX
	JMP	SHORT FULLN

DoBackScan:
	DEC	DI
	MOV	CX,[LAST_MEM]
	STD
	REPNE	SCASB			;Scan backwards for last line
	CLD
	INC	DI
	INC	DI
	DEC	DX
FULLN:
	POP	CX				    ;Actual amount read
	MOV	WORD PTR [DI],1AH		    ;Place EOF after last line
	SUB	CX,DI
	XCHG	DI,[ENDTXT]
	ADD	DI,CX				    ;Amount of file read but not used
; Must seek for old partial line
	OR	DI,DI
	JZ	FULLN1
	PUSH	DX
	PUSH	BX
	MOV	BX,[RD_HANDLE]
	MOV	DX,DI
	NEG	DX
	MOV	CX,-1
	MOV	AL,1
	MOV	AH,LSEEK
	INT	21H
	POP	BX
	POP	DX
	JC	BAD_READ
FULLN1:
	CMP	BX,DX
	JNZ	EOFCHK
	MOV	BYTE PTR [HAVEOF],0
	return
NOMORE:
	mov	ax,EOF
	call	display_message
ret3:	return

BAD_READ:
	MOV	DX,OFFSET DG:READ_ERR_ptr
	MOV	DI,offset dg:path_name
	ADD	DI,[FNAME_LEN]
	MOV	AL,0
	STOSB
	JMP	XERROR

EOFCHK:
	TEST	BYTE PTR [HAVEOF],-1
	JNZ	NOMORE
	TEST	BYTE PTR [ENDING],-1
	retnz			;Suppress memory error during End
	JMP	MEMERR

EWRITE:
	CMP	ParamCt,1
	JBE	EWriteOK
	JMP	ComErr
EWriteOK:
	MOV	BX,[PARAM1]
	OR	BX,BX
	JNZ	WRT
	MOV	CX,[ONE4TH]
	MOV	DI,[ENDTXT]
	SUB	DI,CX		;Write everything in front of here
	JBE	RET3
	CMP	DI,OFFSET DG:START	;See if there's anything to write
	JBE	RET3
	XOR	DX,DX
	MOV	BX,1		;Look for one more line
	CALL	SCANLN
	JMP	SHORT WRTADD
WRT:
	INC	BX
	CALL	FINDLIN
WRTADD:
	CMP	BYTE PTR [DELFLG],0
	JNZ	WRTADD1
	PUSH	DI
	CALL	DELBAK			;Want to delete the .BAK file
					;as soon as the first write occurs
	POP	DI
WRTADD1:
	MOV	CX,DI
	MOV	DX,OFFSET DG:START
	SUB	CX,DX			;Amount to write
	retz
	MOV	BX,[WRT_HANDLE]
	MOV	AH,WRITE
	INT	21H
	JC	WRTERR
	CMP	AX,CX			; MZ correct full disk detection
	JNZ	WRTERR			; MZ correct full disk detection
	MOV	SI,DI
	MOV	DI,OFFSET DG:START
	MOV	[POINTER],DI
	MOV	CX,[ENDTXT]
	SUB	CX,SI
	INC	CX			;Amount of text remaining
	CLD
	REP	MOVSB
	DEC	DI			;Point to EOF
	MOV	[ENDTXT],DI
	MOV	[CURRENT],1
	return

WRTERR:
	MOV	BX,[WRT_HANDLE]
	MOV	AH,CLOSE
	INT	21H
	mov	ax,DSKFUL
zerror:
	push	cs
	pop	ds
	call	display_message
xerror1111:
	mov	al,0ffh
	mov	ah,exit
	int	21h

xERROR:
	push	cs
	pop	ds
	call	std_printf
	jmp	xerror1111

NOTFNDJ:JMP	NOTFND

replac_from_curr:
	CMP	ParamCt,2
	JBE	Replace1
	JMP	ComErr
Replace1:
	mov	byte ptr [srchmod],1   ;search from curr+1 line
	jmp	short sj6

REPLAC:
	mov	byte ptr [srchmod],0   ;search from beg of buffer
sj6:
	MOV	BYTE PTR [SRCHFLG],0
	CALL	FNDFIRST
	JNZ	NOTFNDJ
REPLP:
	MOV	SI,[NUMPOS]
	CALL	LOADBUF 	;Count length of line
	SUB	DX,[OLDLEN]
	MOV	CX,[NEWLEN]
	ADD	DX,CX		;Length of new line
	CMP	DX,254
;	jbe	len_ok
;	Jmp	TOOLONG
	ja	toolong
len_ok:
	MOV	BX,[LSTNUM]
	PUSH	DX
	CALL	SHOWNUM
	POP	DX
	MOV	CX,[LSTFND]
	MOV	SI,[NUMPOS]
	SUB	CX,SI		;Get no. of char on line before change
	DEC	CX
	mov	di,offset dg:arg_buf	;Initialize the output string buffer
	CALL	OUTCNT		;Output first part of line
	PUSH	SI
	MOV	SI,1+ OFFSET DG:TXT2
	MOV	CX,[NEWLEN]
	CALL	OUTCNT		;Output change
	POP	SI
	ADD	SI,[OLDLEN]	;Skip over old stuff in line
	MOV	CX,DX		;DX=no. of char left in line
	ADD	CX,2		;Include CR/LF
	CALL	OUTCNT		;Output last part of line
	xor	al,al
	stosb
	mov	dx,offset dg:arg_buf_ptr
	call	std_printf
	CALL	QUERY		;Check if change OK
	JNZ	REPNXT
	CALL	PUTCURS
	MOV	DI,[LSTFND]
	DEC	DI
	MOV	SI,1+ OFFSET DG:TXT2
	MOV	DX,[OLDLEN]
	MOV	CX,[NEWLEN]
	DEC	CX
	ADD	[LSTFND],CX	;Bump pointer beyond new text
	INC	CX
	DEC	DX
	SUB	[SRCHCNT],DX	;Old text will not be searched
	JAE	SOMELEFT
	MOV	[SRCHCNT],0
SOMELEFT:
	INC	DX
	CALL	REPLACE
REPNXT:
	CALL	FNDNEXT
	retnz
	JMP	REPLP

OUTCNT:
	JCXZ	RET8
OUTLP:
	LODSB
	stosb
	DEC	DX
	LOOP	OUTLP
RET8:	return

TOOLONG:
	mov	ax,TOOLNG
	JMP	SHORT PERR

search_from_curr:
	CMP	ParamCt,2
	JBE	Search1
	JMP	ComErr
Search1:
	mov	byte ptr [srchmod],1   ;search from curr+1 line
	jmp	short sj7

SEARCH:
	mov	byte ptr [srchmod],0   ;search from beg of buffer
sj7:
	MOV	BYTE PTR [SRCHFLG],1
	CALL	FNDFIRST
	JNZ	NOTFND
SRCH:
	MOV	BX,[LSTNUM]
	MOV	SI,[NUMPOS]
	CALL	DISPONE
	MOV	DI,[LSTFND]
	MOV	CX,[SRCHCNT]
	MOV	AL,10
	CLD
	REPNE	SCASB
	JNZ	NOTFND
	MOV	[LSTFND],DI
	MOV	[NUMPOS],DI
	MOV	[SRCHCNT],CX
	INC	[LSTNUM]
	CALL	QUERY
	JZ	PUTCURS1
	CALL	FNDNEXT
	JZ	SRCH
NOTFND:
	mov	ax,NOSUCH
PERR:
	jmp	display_message

;
; Replace enters here with LSTNUM pointing to the correct line.
;
PUTCURS:
	MOV	BX,[LSTNUM]
	jmp	short putcursor
;
; Search enters here with LSTNUM pointing AFTER the correct line
;
putcurs1:
	MOV	BX,[LSTNUM]
	DEC	BX			;Current <= Last matched line

putcursor:
	CALL	FINDLIN
	MOV	[CURRENT],DX
	MOV	[POINTER],DI
	return

;
; n,mD	    deletes a range of lines.  Allowable values for n are:
;   1 ... LAST_MEM.  Allowable values for m are:
;   1 ... LAST_MEM.
; nD	    deletes a single line
; D	    deletes the current line
;
DELETE:
	CMP	ParamCt,2		; at most two parameters specified.
	JA	ComErrJ
	MOV	BX,Param1
	OR	BX,BX			; default first arg?
	JNZ	DelParm2
	MOV	BX,Current		; use current as default
	MOV	Param1,BX
DelParm2:
	MOV	BX,Param2		; did we default second arg?
	OR	BX,BX
	JNZ	DelCheck		; no, use it.
	MOV	BX,Param1		; use param1 as default
	MOV	Param2,BX
DelCheck:
	MOV	BX,Param1
	CALL	ChkRange		; returns by itself if bad range
;
; BX is first line of range to be deleted. Param2 is last line in range to
; be deleted.  Get pointer to beginning of block.  Save location
;
	CALL	FINDLIN 		; Grab line
	retnz				; If not found => return
	PUSH	BX
	PUSH	DI
;
; Get pointer past end of block (Param2+1).
;
	MOV	BX,Param2
	INC	BX
	CALL	FINDLIN
;
; Set up pointers.  Compute number of chars to move.
;
	MOV	SI,DI			; move from second line+1
	POP	DI			; restore destination (first line)
	POP	Current 		; Current line is first param
	MOV	Pointer,DI		; internal current line
	MOV	CX,EndTxt		; compute count
	SUB	CX,SI
	JB	ComErrJ 		; should never occur: ChkRange
	INC	CX			; remember ^Z at end
	CLD
	REP	MOVSB			; move data
	DEC	DI
	MOV	EndTxt,DI		; reset end pointer
	return

COMERRJ:
	JMP	COMERR

PAGER:
	CMP	ParamCt,2
	JA	ComErrJ
	xor	bx,bx		;get last line in the buffer
	call	findlin
	mov	[lastlin],dx

	mov	bx,[param1]
	or	bx,bx		;was it specified?
	jnz	frstok		;yes, use it
	mov	bx,[current]
	cmp	bx,1		;if current line =1 start from there
	je	frstok
	inc	bx		;start from current+1 line
frstok:
	cmp	bx,[lastlin]	;check that we are in the buffer
	jbe	frstok1
	return			;if not just quit
frstok1:
	mov	dx,[param2]
	or	dx,dx		;was param2 specified?
	jnz	scndok		;yes,....
	mov	dx,bx		;no, take the end line to be the
				;    start line + length of active display

;=========================================================================
; This modification is to provide support for screens larger than
; 24 lines.
;
;	Date	   : 6/10/87
;=========================================================================

	push	ax		;an000;save affected registers

	mov	ah,00h		;an000;zero out high byte
	mov	al,dg:disp_len	;an000;set ax to active display length
	sub	ax,2		;an000;adjust for length of screen & current
				;      line
	add	dx,ax		;an000;this gives us the last line to be
				;      printed
	pop	ax		;an000;restore affected registers

;=========================================================================

scndok:
	inc	dx
	cmp	dx,[lastlin]	;check that we are in the buffer
	jbe	infile
	mov	dx,[lastlin]	;we are not, take the last line as end
infile:
	cmp	dx,bx		;is param1 < param2 ?
	retz
	ja	sj33
	jmp	comerr		;yes, no backwards listing, print error
sj33:
	push	dx		;save the end line
	push	bx		;save start line
	mov	bx,dx		;set the current line
	dec	bx
	call	findlin
	mov	[pointer],di
	mov	[current],dx
	pop	bx		;restore start line
	call	findlin 	;get pointer to start line
	mov	si,di		;save pointer
	pop	di		;get end line
	sub	di,bx		;number of lines
	jmp	short display_lines


LIST:
	CMP	ParamCt,2
	JBE	ListOK
	JMP	ComERR
ListOK:
	MOV	BX,[PARAM1]
	OR	BX,BX
	JNZ	CHKP2
	MOV	BX,[CURRENT]
	SUB	BX,11
	JA	CHKP2
	MOV	BX,1
CHKP2:
	CALL	FINDLIN
	retnz
	MOV	SI,DI
	MOV	DI,[PARAM2]
	INC	DI
	SUB	DI,BX
	JA	DISPLAY_lines

;=========================================================================
; This modification is to provide support for screens larger than
; 24 lines.
;
;	Date	   : 6/10/87
;=========================================================================

	push	ax			;an000;save affected registers

	mov	ah,00h			;an000;zero out high byte
	mov	al,dg:disp_len		;an000;set ax to active display length	       dec     ax		       ;an000;allow room at bottom for
					;      messages
	mov	di,ax			;an000;number of lines to print an
					;      entire screen less 1.
	pop	ax			;an000;restore affected registers

;=========================================================================

display_lines:
	call	DISPLAY
	return

Break <NOCOM - edit a single line>

;
; NOCOM is called when there is a single line being edited.  This occurs when
; the command letter is CR or is ;.
;
NOCOM:
	CMP	ParamCt,2
	JB	NoComOK
	JMP	ComErr
NoComOK:
	DEC	[COMLINE]
	MOV	BX,[PARAM1]
	OR	BX,BX
	JNZ	HAVLIN
	MOV	BX,[CURRENT]
	INC	BX	;Default is current line plus one
	CALL	CHKRANGE
HAVLIN:
	CALL	FINDLIN
	MOV	SI,DI
	MOV	[CURRENT],DX
	MOV	[POINTER],SI
	jz	sj12
ret12:	return
sj12:
	CMP	SI,[ENDTXT]
	retz
	CALL	LOADBUF
	MOV	[OLDLEN],DX
	MOV	SI,[POINTER]
	CALL	DISPONE
	CALL	SHOWNUM
	MOV	AH,STD_CON_STRING_INPUT 	  ;Get input buffer
	MOV	DX,OFFSET DG:EDITBUF
	INT	21H
	CALL	lf
	MOV	CL,[EDITBUF+1]
	MOV	CH,0
	JCXZ	RET12
	MOV	DX,[OLDLEN]
	MOV	SI,2 + OFFSET DG:EDITBUF
;-----------------------------------------------------------------------
	call	unquote 		;scan for quote chars if any
;-----------------------------------------------------------------------
	mov	cl,[EditBuf+1]		;an000; dms;get new line length
	mov	ch,0			;an000; dms;clear high byte
	MOV	DI,[POINTER]
	JMP	Replace 		; MZ 11/30

CODE	ENDS
	END
