	NAME	mssker
; File MSSKER.ASM
	include mssdef.h
; Edit history
; 12 Jan 1995 version 3.14
; Last edit
; 12 Jan 1995
;****************************** Version 3.14 ***************************** 
; KERMIT, Celtic for "free" 
;
; The name "Kermit" is a registered trade mark of Henson Associates, Inc.,
; used by permission.
;
;	MS-DOS Kermit Program Version 3.14, 12 Jan 95
;	MS-DOS Kermit Program Version 3.13, 8 July 93
;	MS-DOS Kermit Program Version 3.12, Feb 1992
;	MS-DOS Kermit Program Version 3.11, 6 Sept 1991
;	MS-DOS Kermit Program Version 3.10, 2 March 1991
;	MS-DOS Kermit Program Version 3.02, development for 3.10, 1990-91
;	MS-DOS Kermit Program Version 3.01, 20 March 1990
;	MS-DOS Kermit Program Version 3.00, 16 Jan 1990 
;	Kermit-MS Program Version 2.32, 11 Dec 1988 
;	Kermit-MS Program Version 2.31, 1 July 1988
;       Kermit-MS Program Version 2.30, 1 Jan 1988
;	Kermit-MS Program Version 2.29, 26 May 1986, plus later revisions.
;       Kermit-MS Program Version 2.27, December 6,1984
;       Kermit-MS Program Version 2.26, July 27, 1984
;       PC-Kermit Program Version 1.20, November 4, 1983
;       PC-Kermit Program Version 1.0, 1982
; 
;       Based on the Columbia University KERMIT Protocol.
; 
;	Copyright (C) 1982, 1995, Trustees of Columbia University in the 
;	City of New York.  The MS-DOS Kermit software may not be, in whole 
;	or in part, licensed or sold for profit as a software product itself,
;	nor may it be included in or distributed with commercial products
;	or otherwise distributed by commercial concerns to their clients 
;	or customers without written permission of the Office of Kermit 
;	Development and Distribution, Columbia University.  This copyright 
;	notice must not be removed, altered, or obscured.
;
;       Original Authors (versions 1.0 through 2.28):
;         Daphne Tzoar, Jeff Damens
;         Columbia University Center for Computing Activities
;         612 West 115th Street
;         New York, NY  10025
;
;       Present author (version 2.29, 2.30, 2.31, 2.32, 3.00, 3.01, 3.02,
;			3.10, 3.11, 3.12, 3.13, 3.14):
;         Joe R. Doupnik
;	  Dept of EE, and CASS
;	  Utah State University
;	  Logan, UT  84322, USA
;	  E-Mail: JRD@CC.USU.EDU (Internet), JRD@USU (BITNET)
; 
; Special thanks to Christine Gianone, Frank da Cruz, Bill Catchings, 
; Bernie Eiben, Vace Kundakci, Terry Kennedy, Jack Bryans, and many many
; others for their help and contributions.

	public	dosnum, curdsk, fpush, isfile, sbrk, crun, errlev
	public	takrd, takadr, taklev, filtst, maxtry, dskspace, thsep, tdfmt
	public	lclsusp, lclrest, lclexit, cwdir, kstatus, verident, cdsr
	public	spath, patched, oldtak, getenv, psp, dosctty, patchid
	public	tcptos, emsrbhandle, emsgshandle, apctrap
	public	startup, cmdfile, inidir, tmpbuf, malloc, dostemp

env	equ	2CH			; environment address in psp
cline	equ	80H			; offset in psp of command line
braceop	equ	7bh			; opening curly brace
bracecl	equ	7dh			; closing curly brace

_STACK	SEGMENT				; our stack
	dw	1500 dup (0)		; for TCP code
	dw	200 dup(0)		; for main Kermit code
msfinal	label	word			; top of stack
_STACK	ENDS

data   segment
	extrn	buff:byte, comand:byte, flags:byte, trans:byte,	prmptr:word
	extrn	machnam:byte, decbuf:byte, rstate:byte, sstate:byte
	extrn	mcctab:byte, rdbuf:byte, takeerror:byte, macroerror:byte
	extrn	dos_bottom:byte

verident label	byte
	verdef
patchena db	'$patch level'
patchid	db	' 0 $'
copyright db	cr,lf
	db	'Copyright (C) Trustees of Columbia University 1982, 1995.'
	db	cr,lf,'$'
copyright2 db	cr,lf,lf
 db ' Copyright (C) 1982, 1995, Trustees of Columbia University in the'
 db	cr,lf
 db ' City of New York.  The MS-DOS Kermit software may not be, in whole' 
 db	cr,lf
 db ' or in part, licensed or sold for profit as a software product itself,'
 db	cr,lf
 db ' nor may it be included in or distributed with commercial products'
 db	cr,lf
 db ' or otherwise distributed by commercial concerns to their clients'
 db	cr,lf
 db ' or customers without written permission of the Office of Kermit' 
 db	cr,lf
 db ' Development and Distribution, Columbia University.  This copyright' 
 db	cr,lf
 db ' notice must not be removed, altered, or obscured.'
 db	cr,lf,'$'

hlpmsg	db 	cr,lf,'Type ? or HELP for help',cr,lf,'$'
crlf    db      cr,lf,'$'
patpmt	db	'Patch>',0
ermes1	db	cr,lf,'?More parameters are needed$'
ermes2	db	cr,lf,'?Unable to initialize memory$'
ermes3  db      cr,lf,'?Command canceled$'
ermes4	db	'?Unable to change directory',0			; asciiz
ermes5	db	cr,lf,'?Unable to complete initialization process$'
ermes6	db	cr,lf,'Ignoring patch file.'
	db	' Version number mismatch.',cr,lf,'$'
ermes7	db	cr,lf,'Patch file was not found',cr,lf,'$'
ermes8	db	cr,lf,'Fatal error in patch file! Please remove PATCH '
	db	'command.',cr,lf,'$'
erms30	db	cr,lf,'?Passed maximum nesting level for TAKE command$'
erms31	db	cr,lf,'?Cannot find Take-file: $'
erms34	db	cr,lf,'This program requires DOS 2.0 or above$'
erms37	db	cr,lf,'?Unable to execute command interpreter $'
erms38	db	cr,lf,' Not an 8250 UART at this COM port$'
erms39	db	cr,lf,' UART tests ok$'
badnam	db	cr,lf,'?No such file(s)$'

ifdef	no_graphics
nographics db	'  No graphics$'
endif	; no_graphics
ifdef	no_network
nonet	db	'  No network$'
else
ifdef	no_tcp
notcp	db	'  No tcp/ip$'
endif	; no_tcp
endif	; no_network

ifdef	no_terminal
noterm	db	'  No terminal$'
endif	; no_terminal

msggraph db	cr,lf,' Graphics is ',0
msgtcpip db	cr,lf,' TCP/IP is ',0
msgnetwork db	cr,lf,' Network is ',0
msgterm	db	cr,lf,' Terminal emulation is ',0
msgnot	db	'not ',0
msgavail db	'available$',0
data	ends

data1	segment
filmsg	db	' Filename$'
dskmsg	db	' disk drive letter or Return$'
pthmsg	db	' Name of new working directory and/or disk$'
runmsg	db	' program name and command line$'
pathlp	db	' optional path for file mskermit.pch$'
stophlp	db	'Status value to be returned  msg, nothing if no new value$'

tophlp	db	cr,lf
	db	'  Ask, Askq (read keybd to variable) '
	db	'  Mail     (file to host Mailer)     '
	db	cr,lf
	db	'  APC text  send App Prog Cmd to host'
	db	'  Open Read/Write/Append file  scripts'
	db	cr,lf
	db	'  Bye      (logout remote server)    '
	db	'  Output text     (for scripts)'
	db	cr,lf
	db      '  C or Connect  (become a terminal)  '
	db	'  Pause [seconds], MPause [millisec]'
	db	cr,lf
	db	'  Check (graphics, tcp/ip, networks) '
	db	'  Pop, End (exit current macro/Take file)'
	db	cr,lf
	db	'  Clear   (Input, comms recv buffer) '
	db	'  Push     (go to DOS, keep Kermit)'
	db	cr,lf
	db	'  Close    (logging and script file) '
        db      '  Quit     (leave Kermit)'
	db	cr,lf
	db	'  CLS (clear screen at command level)'
        db      '  R or Receive  (opt local filename)'
	db	cr,lf
	db	'  CWD or CD  (change dir &/or disk)  '
	db	'  Read (line from a file to variable)'
	db	cr,lf
	db	'  Decrement/Increment variable number'
	db	'  Reinput  (script Input, reread buffer)'
	db	cr,lf
	db	'  Define/Assign   (a command macro)  '
	db	'  Remote   (prefix for commands)'
	db	cr,lf
	db	'  Delete   (a file)                  '
	db	'  Replay   (file through term emulator)'
	db	cr,lf
	db	'  Dial     (phone number)            '
	db	'  Run      (a program)'
	db	cr,lf		 
	db	'  Directory (filepsec)               '
        db      '  S, Send, Resend, Psend  local new-name'
	db	cr,lf
	db	'  Disable  (selected server commands)'
	db	'  Server [timeout] (become a server)'
	db	cr,lf
	db	'  Echo text (show line on screen)    '
        db      '  Set      (most things)'
	db	cr,lf
	db	'  Enable   (selected server commands)'
	db	'  Show     (most things)'
	db	cr,lf
	db      '  EXIT     (leave Kermit)            '
	db	'  Sleep time  (wait, no comms echos)'
	db	cr,lf
	db	'  Finish   (to remote server)        '
	db	'  Space    (free on current disk)'
	db	cr,lf
	db	'  Get      (remote file opt new name)'
	db	'  Stop     (exit all Take files & macros)'
	db	cr,lf
	db	'  Getc  (read 1 byte from kbd to var)'
	db	'  Take     (do a command file)'
	db	cr,lf
	db	'  GetOK    (get Yes, OK, No response)'
	db	'  Telnet host port  NEW or RESUME'
	db	cr,lf
	db	'  Goto    (label, Take file or Macro)'
	db	'  Test COM1 ... COM4 (check for UART)'
	db	cr,lf
	db	'  Hangup   (drop DTR, hang up phone) '
	db	'  Transmit filespec [prompt] (raw upload)'
	db	cr,lf
	db	'  If [not] <condition> <command>     '
	db	'  Type     (a file)'
	db	cr,lf
	db	'  I or Input [timeout] text (scripts)'
	db	'  Version  (show version and copyright)'
	db	cr,lf
	db	'  INTRO  introduction to Kermit      '
	db	'  Wait [timeout] on modem \cd \cts \dsr'
	db	cr,lf
	db	'  Log (Packet, Session, Transaction) '
	db	'  Write  FILE or log file   text'
	db	'$'

qckhlp	db	cr,lf
	db	'MS-DOS Kermit 3.14, 12 Jan 1995, Copyright (C) 1982, 1995,'
	db	cr,lf
	db	'Trustees of Columbia University in the City of New York.'
	db	cr,lf,lf
	db	'Important commands (type the command, then press the'
	db	' Enter key):'
	db	cr,lf,lf
	db	'  INTRO    - For an introduction to MS-DOS Kermit.'
	db	cr,lf
	db	'  VERSION  - For version and copyright information.'
	db	cr,lf
	db	'  EXIT     - To leave MS-DOS Kermit.'
	db	cr,lf,lf

	db	'Press the question-mark (?) key for context-sensitive'
	db	' help'
	db	cr,lf
	db	'  at any point within a command.'
	db	cr,lf,lf

	db	'DOCUMENTATION:'
	db	cr,lf

	db	'  "Using MS-DOS Kermit" by Christine M. Gianone,'
	db	cr,lf
	db	'  Digital Press / Butterworth-Heinemann, 1992, ISBN'
	db	' 1-55558-082-3.'
	db	cr,lf
  	db	'  Please purchase this manual - it shows you how to use'
	db	' the software,'
	db	cr,lf
	db	'  it answers your questions, and its sales support the'
	db	' Kermit effort.'
	db	cr,lf
	db	'  To order, call +1 212 854-3703 or +1 800 366-2665.'
	db	cr,lf,lf
	
	db	'And see these files on your Kermit diskette for additional'
	db	' information:'
	db	cr,lf

	db	'  KERMIT.UPD - New features and updates.'
	db	cr,lf

	db	'  KERMIT.BWR - Hints and tips, troubleshooting information,'
	db	' etc.'
	db	cr,lf
	db	'  KERMIT.HLP - Concise descriptions of each command.'
	db	cr,lf,'$'

intrhlp	db cr,lf
	db '                    Introduction to MS-DOS Kermit',cr,lf
	db 'o An MS-Kermit command is a line of words separated by spaces and'
	db ' ending with',cr,lf,'  a carriage return <the Enter key>.'
	db '  Example: SET SPEED 2400<Enter>',cr,lf
	db 'o Most words can be abbreviated and can be completed by pressing'
	db ' the Esc key.',cr,lf
	db '  Example: SET SPE 24<Enter>  or even  SET SPE<Esc> 24<Esc>'
	db '<Enter>',cr,lf
	db 'o Help (detailed, specific): press the "?" key where a word would'
	db ' appear.',cr,lf
	db 'o Edit lines using the Backspace key to delete characters,'
	db ' Control-W to delete',cr,lf
	db '  words, and Control-U to delete the line.  Control-C cancels the'
	db ' command.',cr,lf
	db 'o Frequently used MS-Kermit commands:',cr,lf
	db '  EXIT           Leave the Kermit program. QUIT does the same'
	db ' thing.',cr,lf
	db '  SET            PORT, PARITY, SPEED, TERMINAL and many other'
	db ' parameters.',cr,lf
	db '  SHOW           Display groups of important parameters.'
	db ' SHOW ? for categories.',cr,lf,lf
	db '  CONNECT        Establish a terminal connection to a remote'
	db ' system or a modem.',cr,lf
	db '  Control-C    (Control-'
	db '  followed by "C")  Return to MS-Kermit> prompt.',cr,lf,lf
	db '  SEND filename  Send the file(s) to Kermit on the other'
	db ' computer.',cr,lf
	db '  RECEIVE        Receive file(s), SEND them from Kermit on the'
	db ' other computer.',cr,lf
	db '  GET filename   Ask the remote Kermit server to send the file(s)'
	db ' to us.',cr,lf
	db '  FINISH         Shut down remote Kermit but stay logged into'
	db ' remote system.',cr,lf
	db '  BYE            FINISH and logout of remote system and exit'
	db ' local Kermit.',cr,lf
	db 'o Common startup sequence: SET SPEED 9600, CONNECT, login, start'
	db ' remote Kermit,',cr,lf
	db '  put it into Server mode, escape back with Control-C, transfer'
	db ' files with',cr,lf
	db '  SEND x.txt, GET b.txt, BYE.'

	db	cr,lf,lf
	db ' MS-DOS Kermit commands, a functional summary:'
	db	cr,lf
	db	cr,lf,' Local file management:         '
	db	'Kermit program management:'
	db	cr,lf,'   DIR    (list files)          '
	db	'  EXIT     (from Kermit, return to DOS)'
	db	cr,lf,'   CD     (change directory)    '
	db	'  QUIT     (same as EXIT)'
	db	cr,lf,'   DELETE (delete files)        '      
	db	'  TAKE     (execute Kermit commands from file)'
	db	cr,lf,'   RUN    (a DOS command)       '
	db	'  CLS      (clear screen)'
	db	cr,lf,'   TYPE   (display a file)      '
	db	'  PUSH     (enter DOS, EXIT returns to Kermit)'
	db	cr,lf,'   SPACE  (show disk space)     '
	db	'  Ctrl-C   (interrupt a command)'
	db	cr,lf
	db	cr,lf,' Communication settings:        '
	db	'Terminal emulation:'
	db	cr,lf,'   SET PORT, SET SPEED          '
	db	'  CONNECT  (begin terminal emulation)'
	db	cr,lf,'   SET PARITY                   '
	db	'  HANGUP   (close connection)'
	db	cr,lf,'   SET FLOW-CONTROL             '
	db	'  Alt-X    (return to MS-Kermit> prompt)'
	db	cr,lf,'   SET LOCAL-ECHO               '
	db	'  SET KEY  (key mapping)'
	db	cr,lf,'   SET ? to see others          '
	db	'  SET TERMINAL TYPE, BYTESIZE, other parameters'
	db	cr,lf,'   SHOW COMMUNICATIONS, MODEM   '
	db	'  SHOW TERMINAL, SHOW KEY'
	db	cr,lf
	db	cr,lf,' File transfer settings:        '
	db	cr,lf,'   SET FILE CHARACTER-SET name  '
	db	'  SET TRANSFER CHARACTER-SET'
	db	cr,lf,'   SET FILE TYPE TEXT, BINARY   '
	db	'  SET SEND or RECEIVE parameters'
	db	cr,lf,'   SET FILE ? to see others     '
	db	'  SET WINDOWS (sliding windows)'
	db	cr,lf,'   SHOW FILE                    '
	db	'  SHOW PROTOCOL, SHOW STATISTICS'
	db	cr,lf,lf
	db	cr,lf,' Kermit file transfer:           '
	db	'ASCII file transfer:'
	db	cr,lf,'   SEND files (to RECEIVE)      '
	db	'  LOG SESSION, CLOSE SESSION (download)'
	db	cr,lf,'   RECEIVE    (from SEND)       '
	db	'  TRANSMIT (upload)'
	db	cr,lf,'   MAIL files (to RECEIVE)      '
	db	'  SET TRANSMIT parameters'
	db	cr,lf
	db	cr,lf,' Using a Kermit server:         '
	db	'Being a kermit server:'
	db	cr,lf,'   GET files    (from server)   '
	db	'  SET SERVER TIMEOUT or LOGIN'
	db	cr,lf,'   SEND or MAIL   (to server)   '
	db	'  ENABLE or DISABLE features'
	db	cr,lf,'   REMOTE command (to server)   '
	db	'  SERVER'
	db	cr,lf,'   FINISH, LOGOUT, BYE          '
	db	'  SHOW SERVER'
	db	cr,lf
	db	cr,lf,' Script programming commands:   '
	db	cr,lf,'   INPUT, REINPUT secs text     '
	db	'  :label, GOTO label'
	db	cr,lf,'   OUTPUT text                  '
	db	'  IF [ NOT ] condition command'
	db	cr,lf,'   DECREMENT or INCREMENT variable number'
	db	cr,lf,'   ASK or ASKQ variable prompt  '
	db	'  OPEN READ (or WRITE or APPEND) file'
	db	cr,lf,'   DEFINE variable or macro     '
	db	'  READ variable-name'
	db	cr,lf,'   ASSIGN variable or macro     '
	db	'  WRITE file-designator text'
	db	cr,lf,'   [ DO ] macro arguments       '
	db	'  CLOSE READ or WRITE file or logfile'
	db	cr,lf,'   ECHO text                    '
	db	'  END or POP from macro or file'
	db	cr,lf,'   PAUSE time                   '
	db	'  STOP all macros and command files'
	db	cr,lf,'   SLEEP time  no comms sampling'
	db	'  WRITE file-designator text'
	db	cr,lf,'   WAIT time modem-signals      '
	db	'  SHOW VARIABLES, SHOW SCRIPTS, SHOW MACROS'
	db	cr,lf
	db ' Use "?" within comands for help on what fits that word.$'

kpath	db	64 dup (0)		; Kermit's paths to Kermit files
data1	ends

data	segment

comtab  db	84 - 1			; COMND tables
	mkeyw	'APC',scapc
	mkeyw	'Asg',assign		; synonym
	mkeyw	'Ask',ask
	mkeyw	'Askq',askq
	mkeyw	'_assign',hide_assign	; hidden, expand destination name
	mkeyw	'Assign',assign
	mkeyw	'Bye',bye
	mkeyw	'C',telnet
	mkeyw	'CD',cwdir
	mkeyw	'Clear',scclr
	mkeyw	'Close',clscpt
	mkeyw	'Check',check
	mkeyw	'Comment',comnt
	mkeyw	'Connect',telnet
	mkeyw	'CLS',cls
	mkeyw	'CWD',cwdir
	mkeyw	'_define',hide_define	; hidden, expand destination name
	mkeyw	'Define',dodef
	mkeyw	'Decrement',decvar
	mkeyw	'Delete',delete
	mkeyw	'Dial',dial
	mkeyw	'Directory',direct
	mkeyw	'Disable',srvdsa
	mkeyw	'Do',docom
	mkeyw	'Echo',scecho
	mkeyw	'Enable',srvena
	mkeyw	'End',popcmd
	mkeyw	'Exit',exit
	mkeyw	'Finish',finish
	mkeyw	'Forward',sforward	; hidden "Forward" goto
	mkeyw	'Get',get
	mkeyw	'G',get			; hidden synomym for Get
	mkeyw	'Ge',get		; ditto
	mkeyw	'Getc',getc
	mkeyw	'Getok',getok
	mkeyw	'goto',sgoto
	mkeyw	'H',help
	mkeyw	'Hangup',dtrlow
	mkeyw	'Help',help
	mkeyw	'If',ifcmd
	mkeyw	'I',scinp
	mkeyw	'Increment',incvar
	mkeyw	'Input',scinp
	mkeyw	'INTRO',intro
	mkeyw	'Log',setcpt
	mkeyw	'Mail',mail
	mkeyw	'Mpause',scmpause
	mkeyw	'Open',vfopen
	mkeyw	'O',scout		; hidden synomym for OUTPUT
	mkeyw	'Output',scout
	mkeyw	'Pause',scpau
	mkeyw	'Pop',popcmd
	mkeyw	'Psend',psend
	mkeyw	'Push',dopush
	mkeyw	'Quit',exit
	mkeyw	'R',read
	mkeyw	'Read',vfread
	mkeyw	'Receive',read
	mkeyw	'Reinput',screinp
	mkeyw	'Remote',remote
	mkeyw	'Replay',replay
	mkeyw	'Resend',resend
	mkeyw	'Run',run
	mkeyw	'S',send
	mkeyw	'Send',send
	mkeyw	'Server',server
	mkeyw	'Set',setcom
	mkeyw	'Show',showcmd
	mkeyw	'Sleep',scsleep
	mkeyw	'Space',chkdsk
	mkeyw	'Statistics',shosta
	mkeyw	'Stay',stay
	mkeyw	'Stop',takeqit
	mkeyw	'Take',take
	mkeyw	'Test',testcom
	mkeyw	'Transmit',scxmit
	mkeyw	'xmit',scxmit		; hidden synonym
	mkeyw	'Type',typec
	mkeyw	'Version',prvers
	mkeyw	'Wait',scwait
	mkeyw	'Write',write
	mkeyw	':',comnt		; script labels, do not react
	mkeyw	'Patch',patch


ifdef	no_network
shotab	db	18 - 2			; SHOW keyword
else
ifndef	no_tcp
shotab	db	18			; SHOW keyword
else
shotab	db	18 - 1			; SHOW keyword
endif	; no_tcp
endif	; no_network
	mkeyw	'Communications',shcom
	mkeyw	'Control-prefixing',cntlsho
	mkeyw	'File',shfile
	mkeyw	'Key',shokey
	mkeyw	'Logging',shlog
	mkeyw	'Macros',shomac
	mkeyw	'Memory',shmem
	mkeyw	'Modem',shomodem
ifndef	no_network
	mkeyw	'Network',shownet
endif	; no_network
	mkeyw	'Protocol',shpro
	mkeyw	'Scripts',shscpt
	mkeyw	'Server',shserv
ifndef	no_tcp
	mkeyw	'Sessions',sesdisp	; TCP/IP
endif	; no_tcp
	mkeyw	'Statistics',shosta
	mkeyw	'Status',status
	mkeyw	'Terminal',shterm
	mkeyw	'Translation',shorx
	mkeyw	'Variables',shovar
					; Kermit initing from Environment
nulprmpt db	0,0,0			; null prompt
initab	db	8			; Environment phrase dispatch table
	mkeyw	'INPUT-buffer-length',setinpbuf ; Script INPUT buffer length
	mkeyw	'Rollback',setrollb	; number of Terminal rollback screens
	mkeyw	'Width',setwidth	; columns in rollback buffer, def=80
	mkeyw	'COM1',com1port
	mkeyw	'COM2',com2port
	mkeyw	'COM3',com3port
	mkeyw	'COM4',com4port
	mkeyw	'Path',mkkpath

featab	db	4		; Compiled-in feature list for CHECK cmd
	mkeyw	'graphics',1
	mkeyw	'networks',3
	mkeyw	'tcp',2
	mkeyw	'terminals',4

chktab	db	4		; table of comm ports for TEST
	mkeyw	'COM1',1
	mkeyw	'COM2',2
	mkeyw	'COM3',3
	mkeyw	'COM4',4

patched	db	1		; 1 = enable patching; 0 = disable or done

	even
lclsusp	dw	0		; address of routine to call when going to DOS
lclrest	dw	0		; address of routine to call when returning
lclexit	dw	0		; address of routine to call when exiting
tcptos	dw	0		; top of stack for TCP code
ssave	dd	0		; Original SS:SP when doing Command.com
in3ad	dw	0,0		; Original break interrupt addresses
ceadr	dd	0		; DOS Critical Error interrupt address
orgcbrk	db	0		; original Control-Break Check state
psp	dw	0		; segment of Program Segment Prefix
exearg	dw	0		; segment addr of environment (filled in below)
	dd	0		; ptr to cmd line (filled in below)
	dw	5ch,0,6ch,0	; our def fcb's; segment filled in later
emsrbhandle dw	-1		; EMS rollback handle, -1 means invalid
emsgshandle dw	-1		; EMS graphics handle, -1 means invalid
dosnum	dw	0		; dos version number, major=low, minor=high
dosctty	db	0		; !=0 if DOS attempts using our comms line
curdsk	db	0		; Current disk
origd	db	0		; Original disk
orgdir	db	64 dup (0)	; original directory on original disk
startup	db	64 dup (0)	; our startup directory
cmdfile db	64 dup (0)	; path and file of last TAKE
inidir	db	64 dup (0)	; mskermit.ini directory (ends on \)
taklev	db	0		; Take levels
oldtak	db	0		; Take level at start of command parse
takadr	dw	takstr-(size takinfo) ; Pointer into structure
takstr	db	(size takinfo) * maxtak dup(0)
cmdlinetake db	0		; non-zero if have DOS command line cmds
filtst	filest	<>		; file structure for procedure isfile
maxtry	db	defmxtry	; Retry limit for data packet send/rcv
ininm2	db	'MSKERMIT.INI',0 ; init file name

ifdef	nls_portuguese
ptchnam	db	'MSRP314.PCH',0	; Portuguese
else
ifdef	no_terminal+no_graphics+no_network
ptchnam	db	'MSRL314.PCH',0	; MSK Lite
else
ifdef	no_graphics+no_network
ptchnam	db	'MSRM314.PCH',0	; MSK medium
else
ptchnam	db	'MSR314.PCH',0	; main patch file name (Version dependent)
endif
endif
endif

ptchnam2 db	'MSKERMIT.PCH',0 ; alternate patch file name
delcmd	db	' del ',0	; delete command
dircmd	db	' dir ',0	; directory command
typcmd	db	' type ',0	; type command
kerenv	db	'KERMIT=',0,0	; Kermit= environment variable, + 2 nulls
pthnam	db	'PATH='		; Path environment variable
pthlen	equ	$-pthnam	;  length of that string
pthadr	dw	0		; offset of PATH= string
dostempname db	'TEMP='		; DOS TEMP= enviroment string
dostempnlen equ $ - dostempname ; string length
dostemp db	' > ',60 dup (0) ; " > " contents of TEMP=  "\$kermit$.tmp"
tmpname db	'$kermit$.tmp',0 ;   path must start on dostemp+3

slashc	db	' /c '		; slashc Must directly preceed tmpbuf
tmpbuf	db	128 dup (0)	; temp space for file names and comments
cmspnam	db	'COMSPEC='	; Environment variable
cmsplen	equ	$-cmspnam
cmspbuf	db	'\command.com',30 dup (0) ; default name plus additional space
shellnam db	'SHELL='	; Environment variable
shellen	equ	$-shellnam
shellbuf db	40 dup (0)	; buffer for name
eexit	db	cr,'exit',cr
leexit	equ	$-eexit
onexit	db	7,0,'ON_EXIT'	; <length>on_exit macro name
onexlen	equ	$-onexit-2	; length of name
mfmsg	db	'?Not enough memory to run Kermit$'
mf7msg	db	'?Attempted to allocate a corrupted memory area$'
spcmsg	db	' bytes available on drive '
spcmsg1	db	' :',cr,lf,0
spcmsg2	db	cr,lf,' Drive '
spcmsg3	db	' : is not ready',0
moremsg	db	'... more, press a key to continue ...$'
errlev	db	0		; DOS errorlevel to be returned
kstatus	dw	0		; command execution status (0 = success)
thsep	db	0		; thousands separator
tdfmt	db	0		; date/time format code
totpar	dw	0
apctrap	db	0		; disable command if done via APC
temp	dw	0
tempptr dw	0
seekptr	dw	0,0		; pointer for lseek in takeread

segstr	db	'ABCDEFG'	; segment "names" for patcher
lsegstr	equ	$-segstr
	even
segtab	dw	code		; segment values for patcher
	dw	code1
	dw	code2
	dw	data
	dw	data1
	dw	_TEXT
	dw	dgroup
data   ends
 
code	segment
	extrn	mail:near, shovar:near, scapc:near
	extrn	bye:near, telnet:near, finish:near, comnd:near, prompt:near
	extrn	read:near, remote:near, send:near, status:near, get:near
	extrn	serrst:near, setcom:near, dtrlow:near, cmblnk:near, getc:near
	extrn	clscpi:near, clscpt:near, scpini:near, setrollb:near
	extrn	dodef:near, setcpt:near, docom:near, shomodem:near
	extrn	server:near, lclini:near, shokey:near, shomac:near, shosta:near
	extrn	strlen:near, strcpy:near, shserv:near, initibm:near
	extrn	strcat:near, prtasz:near, shorx:near, lnout:near, lnouts:near
	extrn	scout:near,scinp:near,scpau:near,scecho:near,scclr:near
	extrn	scxmit:near, scwait:near, srvdsa:near, srvena:near
	extrn	shcom:near, shlog:near, shpro:near, shterm:near, shscpt:near
	extrn	shfile:near, takclos:near, ask:near, askq:near
	extrn	assign:near, sgoto:near, screinp:near, ifcmd:near, write:near
	extrn	setinpbuf:near, shmem:near, replay:near, atoi:near
	extrn	katoi:near, com1port:near, com2port:near, com3port:near
	extrn	com4port:near, popcmd:near, mprompt:near, locate:near
	extrn	vfopen:near, vfread:near, decvar:near, incvar:near
	extrn	fparse:near, setwidth:near, cnvlin:near, scmpause:near
	extrn	getok:near, cntlsho:near, shownet:near, ctlu:near, iseof:near
	extrn	resend:near, psend:near, tstport:near, scsleep:near
	extrn	takopen_file:far, takopen_macro:far, sforward:near
	extrn	hide_assign:near, hide_define:near, dial:near
ifndef	no_tcp
	extrn	sesdisp:near
endif	; no_tcp

extrn decout:near
        assume  cs:code, ds:data, ss:_stack, es:nothing
 
START	PROC	FAR
	mov	ax,data			; initialize DS
        mov	ds,ax
	mov	psp,es			; remember psp address
	mov	ah,dosver		; get DOS version number (word)
	int	dos
	xchg	ah,al			; major version to ah
	mov	dosnum,ax		; remember dos version
	cmp	ax,200h			; earlier than DOS 2.0?
	jge	start1			; ge = no
	mov	ah,prstr
	mov	dx,offset erms34	; complain
	int	dos
	push	psp			; set up exit for DOS 1
	xor	ax,ax			; and the IP
	push	ax			; make return addr of psp:0 for DOS 1
	ret				; and return far to exit now
start1:	call	memini			; initialize our memory usage
	mov	ah,setdma		; set disk transfer address
	mov	dx,offset buff
	int	dos
	call	far ptr setint		; ^C, DOS critical error interrupts
	mov	ah,gcurdsk		; get current disk
	int	dos
	inc	al			; make 1 == A (not zero)
	mov	curdsk,al
	mov	origd,al		; remember original disk we started on
	mov	si,offset orgdir     ; place for directory path w/o drive code
	add	al,'A'-1		; make al alphabetic disk drive again
	mov	[si],al			; put it into original path descriptor
	inc	si
	mov	byte ptr [si],':'	; add drive specifier too
	inc	si
	mov	byte ptr [si],'\'	; add root indicator as well
	inc	si
	mov	ah,gcd			; get current directory (path really)
	xor	dl,dl			; use current drive
	int	dos
	call	getpath			; get the path from the environment
	call	gettsep			; get thousands separator, t/date code
	mov	ah,gswitch
	xor	al,al			; pick up switch character
	int	dos
	mov	slashc+1,dl
	and	maxtry,3fh		; limit # packet retries
	mov	bx,4			; PRN handle for DOS
	mov	ah,ioctl
	mov	al,0			; get info to 
	int	dos
	or	dl,20h			; turn on binary mode
	xor	dh,dh
	mov	ah,ioctl
	mov	al,1			; set info
	int	dos
	call	getcsp			; get comspec from environment
	call	getssp			; get shellspec from environment
	call	getdostemp		; get DOS TEMP= string
	mov	dx,offset dostemp
	call	strlen			; length so far
	cmp	cx,3			; just ' > '?
	je	start1c			; e = yes, no TEMP= in environment
	mov	bx,dx			; string so far
	add	bx,cx			; last byte + 1
	cmp	byte ptr [bx-1],'\'	; ends on slash?
	je	start1c			; e = yes
	mov	word ptr [bx],'\'+0	; append slash and null terminator
start1c:mov	di,dx			; destination of dostemp
	mov	si,offset tmpname	; redirection filename
	call	strcat			; append

	call	getargv			; get directory where we started
	call	getparm			; read "KERMIT=" Environment line
	jc	start1b			; c = fatal error
	xor	cl,cl			; counter, starts at 0
start1a:mov	bx,offset kerenv+6	; append "<digit>="  to "KERMIT"
	mov	[bx],cl			; binary digit
	inc	cl
	add	byte ptr [bx],'0'	; to ascii
	mov	byte ptr [bx+1],'='	; append equals sign
	call	getparm			; read "KERMITn=" Environment line
	jc	start1b			; c = fatal error
	cmp	cl,9			; done all digits?
	jbe	start1a			; be = no
	call	scpini			; initialize script routines
	jc	start1b			; c = fatal error
	call	lclini			; do local initialization
	cmp	flags.extflg,0		; exit now?
	je	start2			; e = no
start1b:mov	ah,prstr		; announce our premature exit
	mov	dx,offset ermes5	; can't complete initialization
	int	dos
	jmp	krmend			; quit immediately
start2:	mov	word ptr comand.cmrprs,offset krmend ; offset of reparse addr
	mov	ax,cs			; our current code segment
	mov	word ptr comand.cmrprs+2,ax ; segment of reparse address
	mov	comand.cmostp,sp	; save for reparse too
	call	gcmdlin			; read command line
	cmp	taklev,0		; in a Take file?
	jne	start3			; ne = yes, skip help msg
	mov	ah,prstr
	mov	dx,offset machnam	; display machine name
	int	dos
        mov	dx,offset verident	; display version header
        int	dos
	mov	dx,offset copyright	; display copyright notice
	int	dos
ifdef no_graphics
	mov	dx,offset nographics
	int	dos
endif
ifdef no_network
	mov	dx,offset nonet
	int	dos
else
ifdef no_tcp
	mov	dx,offset notcp
	int	dos
endif
endif
ifdef	no_terminal
	mov	dx,offset noterm
	int	dos
endif
ifdef no_graphics + no_tcp + no_network
	mov	dx,offset crlf
	int	dos
endif

	mov	dx,offset hlpmsg
	int	dos
start3:	mov	patchena,' '		; let patch level show
	call	serrst			; reset serial port (if active)
	call	initibm			; define IBM macro
	call	rdinit			; read kermit init file
	push	es
	mov	di,ds
	mov	es,di
	mov	di,offset inidir	; remember path to mskermit.ini
	mov	si,offset cmdfile	; last Take file path+name
	call	strcpy			; copy whole string
	mov	dx,di
	call	strlen			; length of complete path+filename
	add	di,cx			; last byte +1
	mov	al,'\'			; look for this separator
	std
	repne	scasb
	cld
	mov	byte ptr [di+2],0	; terminate after separator
	pop	es

 ; This is the main KERMIT loop.  It prompts for and gets the users commands

kermit:	mov	ax,ds
	mov	es,ax			; convenient safety measure
	mov	dx,prmptr		; get prompt string address
	call	mprompt          	; set master reparse address to here
	cmp	flags.cxzflg,'C'	; did someone want out?
	jne	kermt4			; ne = no
kermt2:	cmp	taklev,0		; are we in a Take file?
	je	kermt4			; e = no, ignore the signal
	call	takclos			; close take file, release buffer
	jmp	short kermt2		; close any other take files
kermt4:	mov	flags.cxzflg,0		; reset each time
	and	flags.remflg,not dserver ; turn off server mode bit
	cmp	dosctty,0		; is DOS using our comms line?
	je	kermt1			; e = no
	and	flags.remflg,not(dquiet+dregular+dserial)
	or	flags.remflg,dquiet	; set display to quiet mode
	call	serrst			; close port so CTTY can run
kermt1:	mov	dx,offset comtab
	mov	bx,offset tophlp
	cmp	flags.extflg,0		; exit flag set?
	jne	krmend			; ne = yes, jump to KRMEND
	mov	comand.cmcr,1		; allow bare CR's
        mov	ah,cmkey
	mov	comand.impdo,1		; allow implied "DO macro"
	call	comnd
	jc	kermt3			; c = failure
	mov	comand.impdo,0		; only on initial keyword, not here
	mov	comand.cmcr,0		; no more bare CR's
	call	bx              	; call the routine returned in BX
	jc	kermt3			; c = failure
	cmp	flags.extflg,0		; exit flag set?
	jne	krmend			; ne = yes, jump to KRMEND
	jmp	short kermt5		; do idle loop cleanup
 
kermt3:	cmp	flags.cxzflg,'C'	; got here via Control-C?
	jne	kermt7			; ne = no
	cmp	flags.extflg,0		; exit flag set?
	jne	kermt5			; ne = yes, skip msg, do cleanup
	mov	dx,offset ermes3	; say command not executed
	mov	ah,prstr		; print	the error message in dx
	int	dos
kermt5:	cmp	flags.cxzflg,'C'	; user Control-C abort?
	jne	kermt7			; ne = no, do normal operations
	cmp	taklev,0		; in a Take file?
	je	kermt7			; e = no		
	call	takclos			; close take file, release buffer
	jmp	short kermt5		; close any other take files
kermt7:	cmp	flags.extflg,0		; exit flag set?
	jne	krmend			; ne = yes, exit
	cmp	taklev,0		; in a Take file or Macro?
	je	kermt10			; e = no
	mov	al,oldtak		; take level at start of command parse
	cmp	al,taklev		; same?
	jne	kermt10			; ne = no, already closed
	mov	bx,takadr		; offset of this Take structure
	mov	al,[bx].taktyp		; kind, file or macro
	cmp	al,take_file		; type of Take, file?
	jne	kermt8			; ne = no (macro)
	cmp	takeerror,0		; is Take Error off?
	jne	kermt9			; ne = no, close Take file
kermt8:	cmp	al,take_macro		; regular macro?
	jne	kermt10			; ne = no, leaves internal macro
	cmp	macroerror,0		; is Macro Error off?
	je	kermt10			; e = yes, error is not fatal
kermt9:	call	takclos			; close Take file or Macro
kermt10:jmp	kermit			; e = no, get next command
 
krmend:	mov	flags.cxzflg,0		; reset each time
	mov	flags.extflg,0
	call	far ptr exmacro		; find on_exit macro
	jc	krmend2			; c = not found
					; perform ON_EXIT macro
krmend1:cmp	taklev,0		; finished with macros?
	je	krmend2			; e = yes
	mov	dx,prmptr		; get prompt string address
	call	mprompt          	; set master reparse address to here
	cmp	taklev,0		; still in on_exit?
	je	krmend2			; e = no, exit to DOS
	mov	flags.cxzflg,0		; reset each time
	and	flags.remflg,not dserver ; turn off server mode bit
	mov	dx,offset comtab	; keyword table
	xor	bx,bx			; no help
        mov	ah,cmkey
	mov	comand.impdo,1		; allow implied "DO macro"
	mov	comand.cmcr,1		; allow bare CR's
	call	comnd
	jc	krmend2			; c = failure
	mov	comand.impdo,0		; only on initial keyword, not here
	call	bx			; call the routine returned in BX
	jnc	krmend1			; nc = success, keep doing commands
					; end of ON_EXIT macro processing
krmend2:cmp	taklev,0		; in a Take file?
	je	krmend3			; e = no		
	call	takclos			; close take file, release buffer
	jmp	short krmend2		; close any other take files
krmend3:mov	bx,lclexit		; addr of sys dependent exit routine
	or	bx,bx			; sys dependent routines want service?
	jz	krmend4			; z = no
	call	bx			; call it
	jnc	krmend4			; nc = close
	jmp	kermit			; c = do not close
krmend4:call	serrst			; just in case the port wasn't reset
	call	clscpi			; close log files
	call	far ptr emsclose	; close and return EMS memory
	mov	dl,origd		; original disk drive
	dec	dl			; want A == 0
	mov	ah,seldsk		; reset original disk just in case
	int	dos
	mov	dx,offset orgdir	; restore original directory
	mov	ah,chdir
	int	dos
	push	ds			; save ds around these DOS calls
	mov	ax,cs			; compose full address of ^C routine
	mov	ds,ax			; segment is the code segment
	mov	dx,offset in3ad		; restore Control-C interrupt vector
	mov	al,23H			; interrupt 23H
	mov	ah,setintv		; set interrupt vector
	int	dos			; ah, that's better
	mov	dx,offset ceadr		; DOS's Critical Error handler
	mov	al,24h			; interrupt 24h
	mov	ah,setintv		; do replacement (put it back)
	int	dos
	pop	ds
	call	cbrestore		; restore state of Control-Break Chk
	mov	ah,4cH			; terminate process
	mov	al,errlev		; return error level
	int	dos
	ret
START	ENDP

; This is the 'EXIT' command.  It leaves KERMIT and returns to DOS
 
EXIT	PROC	NEAR
	mov	ah,cmeol
	call	comnd			; get a confirm
	jc	exit1			; c = failure
	mov	flags.extflg,1		; set the exit-Kermit flag
exit1:	cmp	taklev,0		; in a Take file?
	je	exit2			; e = no		
	call	takclos			; close take file, release buffer
	jmp	short exit1		; close any other take files
exit2:	clc
	ret
EXIT	ENDP
code	ends

code1	segment
	assume	cs:code1
exmacro	proc	far			; perform on_exit() macro
	push	bx
	push	cx
	push	si
	mov	bx,offset mcctab	; table of macro names
	mov	cl,[bx]			; number of names in table
	xor	ch,ch
	jcxz	exmacx			; z = empty table, do nothing
	inc	bx			; point to length of first name
exmac2:	mov	ax,[bx]			; length of this name
	cmp	ax,onexlen		; length same as desired keyword?
	jne	exmac3			; ne = no, search again
	mov	si,bx
	add	si,2			; point at first char of name
	push	cx			; save name counter
	push	di			; save reg
	mov	cx,onexlen		; length of name
	mov	di,offset onexit+2	; point at desired macro name text
	push	es			; save reg
	push	ds
	pop	es			; make es use data segment
	cld
	repe	cmpsb			; match strings
	pop	es			; need current si below
	pop	di
	pop	cx			; recover saved regs
	jne	exmac3			; ne = no match
	mov	onexit+2,0		; change name to be invisible
	mov	byte ptr [bx+2],0	; change macro table name too
	jmp	short exmac4		; e = matched
exmac3:	add	bx,ax			; step to next name, add name length
	add	bx,4			; + count and def word ptr
	loop	exmac2			; try next name
exmacx:	pop	si			; no macro, fail
	pop	cx
	pop	bx
	stc				; say failure
	ret

exmac4:	call	takopen_macro		; open a macro
	jc	exmacx			; c = failed
	mov	bx,takadr		; point to current macro structure
	mov	ax,ds			; text is in our data seg
	mov	[bx].takbuf,ax		; seg of definition string struc
	mov	[bx].takptr,offset onexit+2 ; where to read next command char
	mov	[bx].takcnt,onexlen	; number of chars in definition
	mov	[bx].takargc,0		; store macro argument count
	pop	si
	pop	cx
	pop	bx
	clc				; say success
	ret
exmacro	endp

; Close and return EMS memory, uses emsrbhandle and emsgshandle
emsclose proc	far
	mov	ah,45h			; release handle and memory
	mov	dx,emsrbhandle		; handle
	or	dx,dx			; is handle valid (not -1)?
	jl	emsclose1		; l = no
	int	67h			; ems interrupt
emsclose1:mov	emsrbhandle,-1
	mov	ah,45h			; release handle and memory
	mov	dx,emsgshandle		; handle
	or	dx,dx			; is handle valid (not -1)?
	jl	emsclose2		; l = no
	int	67h			; ems interrupt
emsclose2:mov	emsgshandle,-1
	ret
emsclose endp
code1	ends

code	segment
	assume	cs:code
; TAKE commands	from a file, and allow a path name
TAKE	PROC	NEAR
	mov	kstatus,kssuc		; global status, success
	cmp	taklev,maxtak		; at the limit?
	jl	take1			; l = no
	mov	ah,prstr
	mov	dx,offset erms30	; complain
	int	dos
	stc				; failure
	ret
take1:	mov	dx,offset tmpbuf	; work buffer
	mov	tmpbuf,0
	mov	bx,offset filmsg	; Help in case user types "?"
	mov	ah,cmword		; get file name
	call	comnd
	jc	take1a			; c = failure
	mov	ah,cmeol
	call	comnd
	jc	take1a			; c = failure
	mov	ax,offset tmpbuf	; point to name again
	cmp	tmpbuf,0		; empty filespec?
	jne	take2			; ne = no
	mov	ah,prstr
	mov	dx,offset ermes1	; say more parameters needed
	int	dos
	stc
take1a:	ret
					; TAKE2: enter with ax=filename ptr
TAKE2:	call	spath			; is it around?
	jc	take3			; no, go complain
	mov	dx,ax			; point to name from spath
	mov	ah,open2		; open file
	xor	al,al			; 0 = open for reading
	cmp	dosnum,300h		; at or above DOS 3?
	jb	take2a			; b = no, so no shared access
	or	al,40h			; open for reading, deny none
take2a:	push	dx
	int	dos
	pop	dx
	jnc	take4			; nc = opened ok, keep going
	mov	ax,dx			; recover filename pointer
take3:	push	ax
	mov	ah,prstr
	mov	dx,offset erms31
	int	dos
	pop	ax
	mov	dx,ax			; asciiz file name
	call	prtasz			; display it
	mov	cmdfile,0		; clear latest cmd file info
	mov	kstatus,kstake		; status, Take failed
	clc				; we've done all error displays
	ret
					; TAKE4: enter with ax=filename ptr
TAKE4:	push	dx			; save filename string
	push	ax			; save pointer
	call	takopen_file		; open take file
	pop	ax
	pop	dx
	jc	take6			; c = failure
	call	save_cmdfile		; save path+name of file from dx
	push	bx
	mov	bx,takadr		; get current frame ptr
	mov	[bx].takhnd,ax		; save file handle
	pop	bx
	cmp	flags.takflg,0		; echoing Take files?
	je	take5			; e = no
	mov	ah,prstr
	mov	dx,offset crlf
	int	dos
take5:	call	takrd			; get a buffer full of data
	clc				; success
take6:	ret
TAKE	ENDP

TAKRD	PROC	NEAR
	push	ax
	push	bx
	push	cx	
	push	dx
	push	di
	push	es
	push	temp
	mov	bx,takadr
	cmp	[bx].taktyp,take_file	; get type of take (file?)
	je	takrd0			; e = take file, not macro
	jmp	takrd30

takrd0:	xor	ax,ax
	mov	[bx].takcnt,ax		; number of bytes to be read
	mov	[bx].takptr,ax		; offset of first new character
	mov	temp,ax			; prime the disk reader
	mov	tempptr,offset tmpbuf

takrd1:	mov	ax,[bx].takbuf		; segment of Take buffer
	mov	es,ax
	mov	cx,tbufsiz		; # of bytes to examine
	xor	dx,dx			; dl = 0 for store data (vs comments)
	xor	di,di			; offset in buffer where data starts

takrd2:	call	takrworker		; fill take buffer, return a byte
	jc	takrd30			; c = failure
	add	word ptr [bx].takseek,1
	adc	word ptr [bx].takseek+2,0 ; seek distance, bytes
	mov	bx,takadr
	cmp	al,TAB			; TAB?
	jne	takrd2a
	mov	al,' '			; convert to space
takrd2a:cmp	al,LF			; line terminator?
	je	takrd2			; e = yes, ignore it
	cmp	al,CR			; internal line terminator?
	je	takrd5			; e = yes, always write in buffer
	or	dl,dl			; store data (vs discard comments)?
	jnz	takrd2			; nz = no, discard, read comments
	cmp	al,';'			; start of comment indicator?
	jne	takrd5			; ne = no
	cmp	[bx].takcnt,0		; bytes examined in buffer, so far
	je	takrd4			; e = nothing, so no escape either
	mov	ah,byte ptr es:[di-1]	; preceeding char
	cmp	ah,'\'			; escaped?
	jne	takrd3			; ne = no
	dec	di			; overwrite '\' with ';'
	dec	[bx].takcnt
	dec	cx
	jmp	short takrd5

takrd3:	cmp	ah,' '			; whitespace precedessor?
	je	takrd4			; e = yes, ';' starts a comment
	cmp	ah,TAB			; this kind too?
	jne	takrd5			; ne = no, not a comment
takrd4:	mov	dl,1			; say start discarding comments
	jmp	short takrd2		; read more comments

takrd5:	inc	[bx].takcnt		; bytes accepted into buffer so far
	stosb				; store byte
	cmp	al,CR			; ending on CR?
	jne	takrd7			; ne = no
	or	dl,dl			; processing comment?
	jnz	takrd30			; nz = yes, CR ends comment line
	cmp	[bx].takcnt,1		; more than just CR?
	jbe	takrd30			; be = no
	cmp	byte ptr es:[di-2],'-'	; hyphenated line?
	jne	takrd30			; ne = no
	sub	[bx].takcnt,2		; remove '-' and CR from buffer
	sub	di,2			; back over both
	add	cx,2			; add back capacity
	xor	dl,dl			; end of comment
takrd7:	loop	takrd2

takrd30:mov	cx,[bx].takcnt		; trim trailing spaces. line count
	jcxz	takrd34			; z = empty line
	mov	di,cx
	cmp	byte ptr [di],' '	; ended on text?
	ja	takrd34			; a = yes, do not trim
	xor	di,di			; es:di(0) is buffer
	dec	cx			; back over final CR
	jcxz	takrd34			; only CR remained
	add	di,cx
	dec	di			; look at last byte - 1
	mov	al,' '			; scan for
	std
	repe	scasb
	cld
	jne	takrd32			; ne = does not over decrement
	dec	di
takrd32:inc	di
	inc	di
	mov	byte ptr es:[di],CR	; final CR goes here
	inc	di
	mov	[bx].takcnt,di		; new count
takrd34:pop	temp
	pop	es
	pop	di
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	ret
TAKRD	ENDP

; Read one byte from disk file into al.
; Return carry set if failure
takrworker proc	near
	push	bx
	push	cx
	push	dx
	push	es
	cmp	temp,0			; bytes to be read from tmpbuf
	jne	takrwork1		; ne = have things in the buffer
	mov	bx,takadr
	mov	dx,word ptr [bx].takseek
	mov	cx,word ptr [bx].takseek+2
	mov	bx,[bx].takhnd		; bx = file handle
	mov	ah,lseek		; seek
	mov	al,0			; from start of file
	int	dos
	mov	temp,0			; bytes remaining unread
	mov	cx,length tmpbuf	; # of bytes to read
	mov	dx,offset tmpbuf	; ds:dx = buffer, skip count word
	mov	tempptr,dx		; where to read from tmpbuf next time
	mov	ah,readf2		; read file
	int	dos
	jc	takrwork2		; c = error, preserve ax
	mov	temp,ax			; returned byte count
	or	ax,ax
	jz	takrwork2		; z = nothing left

takrwork1:
	mov	bx,tempptr		; where to read a byte
	mov	al,[bx]			; read the byte
	inc	tempptr			; where to read next time
	dec	temp			; say another byte read
	clc
	jmp	short takrwork3		; succeed

takrwork2:stc				; fail
takrwork3:pop	es
	pop	dx
	pop	cx
	pop	bx
	ret
takrworker endp

; TAKE-QUIT  (STOP)  Exit all Take files immediately but gracefully

TAKEQIT PROC	NEAR
	xor	ax,ax
	mov	errlev,al		; return value in ERRORLEVEL
	mov	kstatus,ax		; and in STATUS
	mov	ah,cmline		; get optional error value and msg
	mov	bx,offset rdbuf+1
	mov	rdbuf,'\'		; in case the user did not specify
	mov	dx,offset stophlp	; help on numerical argument
	mov	comand.cmcr,1		; bare c/r's allowed
	call	comnd
	mov	comand.cmcr,0		; restore normal state
	jc	takqit3			; c = failure
	mov	ah,cmeol		; confirm
	call	comnd
	jc	takqit3
	mov	si,offset rdbuf
	cmp	byte ptr [si+1],'\'	; user specified?
	jne	takqit1			; ne = no, use ours
	inc	si			; yes, use theirs
takqit1:call	katoi			; convert to number in ax
	jc	takqit2			; c = not a number
	mov	errlev,al		; return value in ERRORLEVEL
	mov	kstatus,ax		; and in STATUS
takqit4:lodsb				; read a msg char
	or	al,al			; null terminator?
	jz	takqit2			; z = empty string
	cmp	al,' '			; leading white space?
	je	takqit4			; be = leading white space
	dec	si			; backup to non-white char
	mov	di,si			; msg pointer, convert in-place
	call	cnvlin			; convert braces and numbers
	mov	dx,offset crlf
	mov	ah,prstr
	int	dos
	mov	dx,si			; message pointer
	call	prtasz
takqit2:xor	ch,ch
	mov	cl,taklev		; number of Take levels active
	jcxz	takqit3			; z = none
	cmp	cmdlinetake,cl		; have DOS level command line?
	jae	takqit3			; ae = yes, don't close it here
	call	takclos			; close current Take file
	jmp	short takqit2		; repeat until all are closed
takqit3:clc				; success
	ret
TAKEQIT	ENDP

; put mskermit.ini onto take stack if it exists.  Just like
; the take command, except it doesn't read a filename

rdinit	proc	near			; read Kermit init file
	mov	ax,offset ininm2	; default name to try
	cmp	decbuf,0		; alternate init file given?
	je	rdini1			; ne = no
	mov	ax,offset decbuf	; yes, use it
	call	spath			; is it around?
	jc	rdini2			; c = no
	call	take2			; let Take do error msgs
	clc				; force success
	ret
rdini1:	call	spath			; is it around?
	jc	rdini2			; c = no, ignore file
	mov	dx,ax			; point to name from spath
	mov	ah,open2		; open file
	xor	al,al			; 0 = open for reading
	cmp	dosnum,300h		; at or above DOS 3?
	jb	rdini1a			; b = no, so no shared access
	or	al,40h			; open for reading, deny none
rdini1a:int	dos
	jc	rdini2			; c = no ini file found, ignore
	call	take4			; use TAKE command to complete work
	clc				; ignore errors
rdini2:	ret
rdinit	endp

; Write path and filename of current Take file to buffer cmdfile. 
; Enter with current working filename offset in register dx.
save_cmdfile	proc near
	push	ax
	push	dx
	push	es
	mov	di,ds
	mov	es,di
	mov	di,offset cmdfile	; destination
	mov	si,dx			; source path
	mov	ax,[si]
	cmp	ah,':'			; drive specified?
	jne	save_c1			; ne = no
	and	al,not 20h		; upper case the drive letter
	add	si,2			; next byte to read
	jmp	short save_c2
save_c1:mov	al,curdsk		; current drive letter
	add	al,'A'-1		; number back to letter
	mov	ah,':'			; and letter
save_c2:cld
	stosw				; write to output
	xor	al,al
	mov	[di],al			; terminate
	cmp	byte ptr [si],'\'	; rooted path?
	je	save_c3			; e = yes
	mov	al,'\'			; get root indicator
	stosb				; write to output
	mov	dl,cmdfile		; get upper case drive letter
	sub	dl,'A'-1		; A = 1
	push	si
	mov	si,di			; gcd writes to si
	mov	ah,gcd			; get current directory (path really)
	int	dos
	mov	dx,si
	call	strlen
	add	di,cx
	pop	si
	jcxz	save_c3			; z = no directory, have \ already
	mov	al,'\'			; path separator
	stosb
	mov	byte ptr [di],0		; terminator
save_c3:call	strcpy			; copy rest of string
	pop	es
	pop	dx
	pop	ax
	ret
save_cmdfile	endp

; Patcher.  Patch file, MSRxxx.PCH or MSKERMIT.PCH has the following format:

; 301 \Xxxxx	Text to display upon successful patch load
; ;301 for V3.01.  For xxxx, see below
; ; optional comment lines may appear anywhere after the 1st
; ; xxxx in 1st line = total paragraphs in memory image.  Use \X if hex.
; DS:xxxx xx xx		; optional comment.  DS (or CS) are case insensitive
; CS:xxxx xx xx xx	; locations must be 4 hex chars, contents must be 2
;
; The 1st xx is the original value of the 1st byte @seg:offset for comparison.
; A 00 value says don't compare.  Subsequent xx's are replacement bytes.
; CS & DS lines may be intermixed.  AS & BS segments may be used when some
; external module sets words aseg & bseg to a seg-base.
; This mechanism expects file msscmd.obj to be linked first.

PATCH	proc
	mov	dx,offset rdbuf		; optional path prefix
	mov	rdbuf,0
	mov	bx,offset pathlp
	mov	ah,cmword
	call	comnd
	jc	patch1
	mov	ah,cmeol
	call	comnd
	jc	patch1
	xor	ax,ax
	xchg	al,patched		; clear and test patched
	test	al,al
	jz	patch1			; z = disabled or done
	xchg	ah,flags.takflg		; clear take flag, don't echo patches
	mov	byte ptr temp,ah	; but save it
	call	ptchr
	mov	al,byte ptr temp	; restore take flag
	mov	flags.takflg,al
	jc	patch2			; c = NG
patch1:	ret

patch2:	mov	dx,offset ermes8	; Fatal error
	mov	ah,prstr
	int	dos
	jmp	krmend			; force exit

ptchr:	mov	dx,offset rdbuf		; optional path
	call	strlen			; get length, if any
	jcxz	ptchr1			; z = nothing
	mov	si,dx
	add	si,cx
	cmp	byte ptr [si-1],'\'	; ends with path specifier?
	je	ptchr1			; e = yes
	cmp	byte ptr [si-1],':'	; or a drive specifier?
	je	ptchr1			; e = yes
	mov	word ptr [si],'\'+0	; add '\' + null
ptchr1:	mov	di,offset rdbuf+66	; path goes here
	mov	si,offset rdbuf+140	; filename goes here, discard
	mov	byte ptr [di],0		; clear
	call	fparse			; split optional path
	mov	si,offset ptchnam	; add name of patch file
	call	strcat
	mov	ax,di			; setup filename pointer for rdini1
	call	rdini1		; let rdini try to find it & do take stuff
	jnc	ptch1			; nc = file msrxxx.pch was found
	mov	di,offset rdbuf+66	; path goes here
	mov	si,offset rdbuf+140	; filename goes here, discard
	mov	byte ptr [di],0		; clear
	mov	dx,offset rdbuf		; source string again
	call	fparse			; split optional path
	mov	si,offset ptchnam2	; try alternate name
	mov	byte ptr [di],0		; insert terminator
	call	strcat			
	mov	ax,di			; setup filename pointer for rdini1
	call	rdini1		; let rdini try to find it & do take stuff
	jnc	ptch1			; nc = file msrxxx.pch was found
	mov	dx,offset ermes7	; say file not found
	mov	ah,prstr
	int	dos
	clc
	ret

ptch1:	mov	al,taklev		; remember initial take level
	mov	tmpbuf,al		; when it changes it is EOF & done
	mov	comand.cmcr,1		; bare cr's ok, to prevent prserr @EOF
	call	ptchrl			; read 1st line's 1st 'word'
	jc	ptch2			; c = trouble
	jz	ptch3			; z = EOF
	mov	si,offset tmpbuf+1
	xchg	ah,al			; line length to ah for atoi
	xor	al,al
	call	atoi			; convert version string to integer
	jc	ptch2			; c = bad number, or none
	cmp	ax,version		; does it match this version?
	je	ptch4			; e = yes
ptch2:	mov	al,tmpbuf		; if take level has changed,
	xor	al,taklev		;  we're already out of patch file
	jnz	ptch3			; nz = change in take level
	call	takclos			; close patch file
ptch3:	mov	dx,offset ermes6
	mov	ah,prstr		; issue warning msg
	int	dos
	clc
	ret

ptch4:	mov	dx,offset tmpbuf+1
	call	ptchrw			; read 2nd "word", 1st line
	jc	ptch2			; c = NG
	mov	si,offset tmpbuf+1
	call	katoi			; convert 2nd "magic number"
	jc	ptch2			; c = NG
	cmp	ax,totpar	; is it the total paragraphs memini computed?
	jne	ptch2			; ne = no
	mov	bx,offset buff	; place to stash 1st lines patch/version msg
	xor	dx,dx
	mov	ah,cmline		; read it
	call	comnd

ptch5:	call	ptchrl			; read CS:xxxx or DS:xxxx
	jc	ptch6
	jz	ptch7			; z = EOF
	cmp	ax,7			; were 7 chars read?
	jne	ptch6			; ne = no
	mov	si,offset tmpbuf+1
	and	word ptr[si],not 2020h	; convert to upper case
	cld
	lodsb				; get the seg char
	cmp	word ptr[si],':S' 	; S:, actually
	je	ptch8			; e = ok
ptch6:	stc				; error exit
	ret

ptch7:	test	flags.remflg,dquiet	; quiet display?
	jnz	ptch7a			; nz = yes, skip msg
	mov	ah,prstr
        mov	dx,offset verident	; display version header
        int	dos
ptch7a:	clc
	ret

ptch8:	push	ds
	pop	es
	mov	di,offset segstr
	mov	cx,lsegstr		; search for seg char in segstr
	repne	scasb
	jne	ptch6			; ne = not found
	sub	di,offset segstr+1	; distance spanned
	shl	di,1			; make a word index
	mov	bx,segtab[di]		; bx = seg-base
	or	bx,bx			; seg-base = 0, disabled for patching
	jz	ptch6			; z = 0, no patching
	mov	word ptr[si],'X\' 	; put '\X' in front of hex for katoi
	call	katoi			; convert location
	jc	ptch6			; c= NG
	push	bx			; save seg being patched
	push	ax			; save location being patched
	mov	tmpbuf+64,0		; clear replacement byte count
ptch9:	mov	dx,offset tmpbuf+4
	call	ptchrw			; read replacement byte follwing '\X'
	jnc	ptch11			; nc = OK
ptch10:	pop	ax			; clean stack & error return
	pop	bx
	stc
	ret

ptch11:	or	ax,ax			; EOL?
	jnz	ptch13			; nz = no
	mov	si,offset tmpbuf+64
	cld
	lodsb				; replacement byte count
	cmp	al,2			; gotta be at least 2
	jb	ptch10			; b = too few
	xor	ch,ch
	mov	cl,al			; replacement count
	pop	di			; patch location
	pop	es			; patch segment
	lodsb				; al = comparison byte
	or	al,al			; key value to ignore comparison?
	jz	ptch12			; z = 0, yes
	cmp	byte ptr es:[di],al 	; do read check on memory image
	jne	ptch6			; ne = no match, fail now
ptch12:	dec	cx			; adjust for comparison byte
	rep	movsb			; make patch
	jmp	ptch5			; loop to read next line

ptch13:	cmp	al,2			; 2 chars req'd for replacement byte
	jne	ptch10			; ne = bad
	mov	si,offset tmpbuf+2	; convert it
	call	katoi
	jc	ptch10			; c = bad number or none
	mov	si,offset tmpbuf+64	; --> replacement byte counted string
	inc	byte ptr[si]		; bump count
	mov	bl,[si]
	xor	bh,bh
	mov	byte ptr[si+bx],al	; stash replacement byte
	jmp	short ptch9		; loop for next byte

ptchrl:	mov	dx,offset patpmt	; read 1st word, next line to tmpbuf+1
	call	prompt
	mov	dx,offset tmpbuf+1
	call	ptchrw
	jc	ptchrb			; c = NG
	mov	dl,tmpbuf
	xor	dl,taklev
	jz	ptchra			; z = not EOF
	xor	ax,ax			; set z flag for EOF
	ret

ptchra:	or	ax,ax			; empty or comment line?
	jz	ptchrl			; z = empty or comment, ignore
ptchrb:	ret

ptchrw:	mov	ah,cmword
	mov	comand.cmper,1	; prohibit substitution variable expansion
	xor	bx,bx			; 'help' ptr
	call	comnd			; line length is in ax
	ret
PATCH	endp

; Get command line into a Take macro buffer. Allow "-f filspec" to override
; normal mskermit.ini initialization filespec, allow command "stay" to
; suppress automatic exit to DOS at end of command line execution. [jrd]

gcmdlin	proc	near
	mov	cmdlinetake,0		; flag for DOS command line Take
	mov	word ptr decbuf,0	; storage for new init filename
	push	es
	cld
	mov	es,psp			; address psp
	xor	ch,ch
	mov	cl,es:byte ptr[cline]	; length of cmd line from DOS
	jcxz	gcmdl1			; z = empty line
	mov	si,cline+1		; point to actual line
gcmdl0:	cmp	byte ptr es:[si],' '	; skip over leading whitespace
	ja	gcmdl2			; a = non-whitespace
	inc	si
	loop	gcmdl0			; fall through on all whitespace
gcmdl1:	jmp	gcmdl14			; common exit jump point
gcmdl2:	inc	cx			; include DOS's c/r
	call	takopen_macro		; open take as macro
	mov	bx,takadr
	mov	ax,150			; space needed: DOS line + ",stay"
	call	malloc			; hope it works
	mov	[bx].takbuf,ax		; memory segment
	or	[bx].takattr,take_malloc ; remember to dispose via takclos
	mov	es,ax			; segment of buffer
        mov     di,2                    ; skip count word
	push	psp

	pop	DS			; DS = PSP
	xor	dx,dx			; clear brace count
gcmdl3:	or	cx,cx			; anything left?
	jle	gcmdl10			; le = no
	lodsb				; get a byte from PSP's command line
	dec	cx			; one less char in input string
	cmp	al,','			; comma?
	jne	gcmdl4			; no, keep going
	or	dx,dx			; inside braces?
	jnz	gcmdl9			; nz = yes, retain embedded commas
	mov	al,cr			; convert to cr
	jmp	short gcmdl9		; store it
gcmdl4:	call	bracechk		; check for curly braces
	jc	gcmdl9			; c = found and counted brace
	or	dx,dx			; outside braces?
	jnz	gcmdl9			; nz = no, ignore flag
	cmp	al,'-'			; starting a flag?
	jne	gcmdl9			; ne = no
	mov	ah,[si]			; get flag letter
	or	ah,20h			; convert to lower case
	cmp	ah,'f'			; 'f' for init file replacement?
	jne	gcmdl9			; ne = no
	inc	si			; accept flag letter
	dec	cx
gcmdl5:	or	cx,cx			; anything to read?
	jle	gcmdl10			; le = exhausted supply
	lodsb				; get filespec char from psp
	dec	cx			; one less char in source buffer
	cmp	al,' '			; in whitespace?
	jbe	gcmdl5			; be = yes, scan it off
	dec	si			; backup to real text
	inc	cx
					; copy filspec to buffer decbuf
	push	es			; save current destination pointer
	push	di			;  which is in es:di (Take buffer)
	mov	di,data			; set es:di to regular decbuf
	mov	es,di
	lea	di,decbuf		; where filespec part goes
	mov	word ptr es:[di],0	; plant safety terminator
gcmdl6:	lodsb				; get filespec char
	dec	cx			; one less available
	cmp	al,' '			; in printables?
	jbe	gcmdl7			; be = no, all done
	cmp	al,','			; comma command separator?
	je	gcmdl7			; e = yes, all done
	stosb				; store filespec char
	or	cx,cx			; any chars left?
	jg	short gcmdl6		; g = yes
gcmdl7:	mov	byte ptr es:[di],0	; end filespec on a null
	pop	di			; recover destination pointer es:di
	pop	es
gcmdl8:	or	cx,cx			; strip trailing whitespace
	jle	gcmdl10			; le = nothing left
	lodsb
	dec	cx
	cmp	al,' '			; white space?
	jbe	gcmdl8			; be = yes, strip it
	cmp	al,','			; at next command?
	je	gcmdl10			; e = yes, skip our own comma
	dec	si			; back up to reread the char
	inc	cx
	jmp	gcmdl3			; read more command text
					; end of flag analysis
gcmdl9:	stosb				; deposit most recent char
gcmdl10:or	cx,cx			; anything left to read?
	jg	gcmdl3			; g = yes, loop
					;
	mov	ax,data			; restore segment registers
	mov	DS,ax
	mov	si,[bx].takbuf		; get segment of Take buffer
	mov	es,si
	mov	si,2			; skip count word
	mov	cx,di			; current end pointer, (save di)
	sub	cx,si			; current ptr minus start offset
	mov	[bx].takcnt,cx		; chars in buffer so far
        mov     es:word ptr [0],cx      ; store count word
	xor	dx,dx			; brace count
	or	cx,cx
	jg	gcmdl11			; g = material at hand
	call	takclos			; empty take file
	jmp	short gcmdl14		; finish up
					; scan for command "stay"
gcmdl11:mov	ax,es:[si]		; get 2 bytes, cx and si are set above
	inc	si			; increment by only one char
	dec	cx
	call	bracechk		; check for braces
	jc	gcmdl12			; c = brace found
	cmp	al,' '			; separator?
	jbe	gcmdl12			; be = yes, keep looking
	cmp	al,',' 			; comma separator?
	je	gcmdl12			; e = yes
	or	dx,dx			; within braces?
	jnz	gcmdl12			; nz = yes, skip STAY search
	or	ax,2020h		; convert to lower case
	cmp	ax,'ts'			; first two letters of stay
	jne	gcmdl12			; ne = no match
	mov	ax,es:[si+1]		; next two letters (stay vs status)
	or	ax,2020h		; convert to lower case
	cmp	ax,'ya'			; same as our pattern?
	jne	gcmdl12			; ne = no match
	add	si,3			; char after "stay"
	sub	cx,3
					; check for separator or end of macro
	cmp	byte ptr es:[si],' '	; next char is a separator?
	jbe	gcmdl13			; be = yes, found correct match
	cmp	byte ptr es:[si],','	; or comma separator?
	je	gcmdl13			; e = yes
	or	cx,cx			; at end of macro?
	jle	gcmdl13			; yes, consider current match correct
gcmdl12:or	cx,cx			; done yet? ("stay" not found)
	jg	gcmdl11			; g = not yet, look some more
	mov	cmdlinetake,1		; remember doing DOS cmd line Take
	mov	si,offset eexit		; append command "exit"
	mov	cx,leexit		; length of string "exit"
	add	[bx].takcnt,cx
	rep	movsb			; copy it into the Take buffer
gcmdl13:mov     [bx].takptr,2           ; init buffer ptr
        mov     cx,[bx].takcnt          ; count of bytes in buffer
        mov     es:[0],cx               ; count of bytes in Take buffer
gcmdl14:pop	es
	ret
gcmdlin	endp

; Curly brace checker. Examine (and preserve) char in AL. Count up/down
; braces in dx because DS is unknown here
bracechk proc	near
	cmp	al,braceop		; opening brace?
	jne	bracech1		; ne = no
	inc	dx			; count up braces
	stc				; say brace seen
	ret
bracech1:cmp	al,bracecl		; closing brace
	jne	bracech3		; ne = no
	sub	dx,1			; count down with sign
	jns	bracech2		; ns = no underflow
	xor	dx,dx			; don't go below zero
bracech2:stc				; say brace detected
	ret
bracech3:clc				; say brace not found
	ret
bracechk endp

; Enter with ax pointing to file name.  Searches path for given file,
; returns with ax pointing to whole name, or carry set if file can't be found.
SPATH	proc	near
	call	isfile			; does it exist as it is?
	jc	spath0			; c = no, prepend path elements
	test	byte ptr filtst.dta+21,10H ; subdirectory name?
	jnz	spath0			; nz = yes, not desired file
	push	di
	mov	di,ax
	push	di
	push	ax
	mov	al,'\'			; look for path separator
	mov	dx,di			; get string length
	call	strlen
	cld
	repne	scasb			; scan for separator
	pop	ax
	pop	di
	je	spath14			; e = found, use as-is
	cmp	byte ptr [di+1],':'	; drive already given?
	jne	spath10			; ne = no
spath14:pop	di
	clc
	ret				; path stuff is already in ax

spath10:push	bx
	mov	bx,[di]
	and	bx,not 2020h		; to upper
	cmp	bx,'UN'			; look for DOS 5 NUL
	jne	spath11			; ne = mismatch
	mov	bx,[di+2]
	and	bl,not 20h
	cmp	bx,'L'+0
spath11:pop	bx
	jne	spath12			; ne = mismatch
	pop	di
	clc
	ret

spath12:push	ax
	mov	di,offset decbuf+64	; where results will be returned
	mov	ah,gcurdsk		; get current disk
	int	dos
	inc	al			; make 1 == A (not zero)
	add	al,'A'-1		; make al alphabetic disk drive again
	mov	[di],al			; put it into original path descriptor
	inc	di
	mov	word ptr [di],'\:'	; add drive specifier too
	add	di,2
	push	si
	mov	si,di
	mov	ah,gcd			; get current directory (path really)
	xor	dl,dl			; use current drive
	int	dos
	pop	si
	push 	dx
	mov	dx,di			; find end of string
	call	strlen
	pop	dx
	add	di,cx			; step after path
	pop	ax
	push	si
	mov	dx,ax			; this is user's file name
	mov	si,offset decbuf+200	; filename goes here, temp
	push	di			; preserve di from above
	mov	di,offset decbuf+220	; far away, path, temp
	call	fparse			; get filename to ds:si
	pop	di
	cmp	byte ptr [di-1],2fh	; does path end with switch char?
	je	spath13			; yes, don't put one in
	cmp	byte ptr [di-1],5ch	; how about this one?
	je	spath13			; yes, don't put it in
	mov	byte ptr [di],5ch	; else add one
	inc	di
spath13:lodsb				; get filename character
	mov	byte ptr [di],al	; copy filename char to output buffer
	inc	di
	or	al,al			; end of string?
	jnz	spath13			; nz = no, copy rest of name
	pop	si			; restore postion in path string
	pop	di
	mov	ax,offset decbuf+64	; return results in ax
	clc
	ret

spath0:	push	es			; save es around work
	push	bx
	push	si
	push	di
	mov	bx,ax			; save filename pointer in bx
	mov	si,ax
	xor	dl,dl			; no '\' seen yet
	cld
spath1:	lodsb
	cmp	al,2fh			; contains fwd slash path characters?
	je	spath1a
	cmp	al,5ch			; or backslash?
	jne	spath2			; ne = no, keep going
spath1a:mov	dl,1			; remember we've seen them
spath2:	or	al,al
	jnz	spath1			; copy name in
	or	dl,dl			; look at flag
	jz	spath3			; no path, keep looking
	jmp	short spath9		; embedded path, fail

spath3:	call	skpath			; search kermit's path
	jnc	spath8a			; nc = located file
	mov	si,pthadr		; offset of PATH= string in environment
	mov	es,psp
	mov	di,es:word ptr[env]	; pick up environment segment
	mov	es,di
spath4:	cmp	byte ptr es:[si],0	; end of PATH= string?
	je	spath9			; e = yes, exit loop
	mov	di,offset decbuf+64	; place to put name
spath5:	mov	al,byte ptr es:[si]	; get a byte from environment string
	inc	si
	cmp	al,';'			; end of this part?
	je	spath7			; yes, break loop
	or	al,al			; maybe end of string?
	jnz	spath6			; nz = no, keep going
	dec	si			; back up to null for later rereading
	jmp	short spath7		; and break loop
spath6:	mov	byte ptr [di],al	; else stick in dest string
	inc	di
	jmp	short spath5		; and continue
spath7:	push	si			; save this ptr
	mov	si,bx			; this is user's file name
	cmp	byte ptr [di-1],2fh	; does path end with switch char?
	je	spath8			; yes, don't put one in
	cmp	byte ptr [di-1],5ch	; how about this one?
	je	spath8			; yes, don't put it in
	mov	byte ptr [di],5ch	; else add one
	inc	di
spath8:	lodsb				; get filename character
	mov	byte ptr [di],al	; copy filename char to output buffer
	inc	di
	or	al,al			; end of string?
	jnz	spath8			; nz = no, copy rest of name
	pop	si			; restore postion in path string
	mov	ax,offset decbuf+64
	call	isfile			; is it a file?
	jc	spath4			; c = no, keep looking
	test	byte ptr filtst.dta+21,10H ; subdirectory name?
	jnz	spath4			; nz = yes
spath8a:pop	di
	pop	si
	pop	bx
	pop	es
	clc
	ret				; return success (carry clear)
spath9:	mov	ax,bx			; restore original filename pointer
	pop	di			; restore regs
	pop	si
	pop	bx
	pop	es
	stc				; no file found
	ret
spath	endp

; Search Kermit's path for file. Return carry clear if found, else carry set.
; Worker for spath above.
skpath	proc	near
	mov	si,seg kpath		; Kermit's path string
	mov	es,si
	mov	si,offset kpath
	mov	di,offset decbuf+64	; place to put name
skpath1:mov	al,es:[si]		; get a byte from string
	inc	si
	cmp	al,';'			; end of this part?
	je	skpath3			; yes, break loop
	or	al,al			; maybe end of string?
	jnz	skpath2			; nz = no, keep going
	dec	si			; back up to null for later rereading
	jmp	short skpath3		; and break loop
skpath2:mov	byte ptr [di],al	; else stick in dest string
	inc	di
	jmp	short skpath1		; and continue
skpath3:cld
	mov	si,bx			; this is user's file name
	cmp	byte ptr [di-1],2fh	; does path end with switch char?
	je	skpath4			; yes, don't put one in
	cmp	byte ptr [di-1],5ch	; how about this one?
	je	skpath4			; yes, don't put it in
	mov	byte ptr [di],5ch	; else add one
	inc	di
skpath4:lodsb				; get filename character
	mov	byte ptr [di],al	; copy filename char to output buffer
	inc	di
	or	al,al			; end of string?
	jnz	skpath4			; nz = no, copy rest of name
	mov	ax,offset decbuf+64
	push	bx
	call	isfile			; is it a file?
	pop	bx
	jnc	skpath5			; nc = yes
	ret
skpath5:test	byte ptr filtst.dta+21,10H ; subdirectory name?
	jnz	skpath6			; nz = yes
	clc				; report file found (AX as offset)
	ret
skpath6:stc				; report failure
	ret
skpath	endp

; Put Kermit's Enviroment Path indicator into string kpath
mkkpath	proc	near
	mov	dx,offset rdbuf
	mov	word ptr rdbuf,0
	xor	bx,bx
	mov	comand.cmblen,64	; 64 bytes max
	mov	ah,cmword		; get a word, with semicolons
	call	comnd
	jnc	mkkpath1		; nc = success
	ret
mkkpath1:mov	cx,ax			; get string length
	push	si
	push	di
	push	es
	mov	si,offset rdbuf		; source
	mov	di,seg kpath		; storage spot
	mov	es,di
	mov	di,offset kpath
	cld
	rep	movsb			; copy
	mov	word ptr es:[di],0	; terminate
	pop	es
	pop	di
	pop	si
	ret
mkkpath	endp

; Put offset of PATH= string in pthadr
getpath	proc	near
	push	bx
	push	cx
	push	dx
	mov	bx,offset pthnam	; thing	to find
	mov	cx,pthlen		; length of it
	mov	pthadr,0		; init offset to zero
	call	getenv			; get environment value
	mov	pthadr,dx
	pop	dx
	pop	cx
	pop	bx
	ret
getpath	endp

; getcsp: copy COMSPEC= environment string into cmspbuf
; getssp: copy SHELL=   environment string into shellbuf
; getdostemp: copy TEMP= environment string into dostemp
getcsp	proc	near
	mov	bx,offset cmspnam	; find COMSPEC=
	mov	cx,cmsplen		; its length
	mov	di,offset cmspbuf	; where to store string
	jmp	short getccom		; do common worker

getssp:	mov	bx,offset shellnam	; find SHELL=
	mov	cx,shellen		; its length
	mov	di,offset shellbuf	; where to store string
	jmp	short getccom		; do common worker

getdostemp:mov	bx,offset dostempname	; fine TEMP=
	mov	cx,dostempnlen		; its length
	mov	di,offset dostemp+3	; where to store string

getccom:push	es
	call	getenv			; get environment offset into dx
	jc	getcs3			; c = not found
	mov	si,dx			; address of COMSPEC= string
	mov	es,psp
	mov	bx,es:word ptr[env]	; pick up environment address
	mov	es,bx
	push	ds			; save ds
	push	ds			; make ds point to environment seg
	push	es			; make es point to data segment
	pop	ds
	pop	es
	cld
getcs1:	lodsb				; get a byte from environment
	cmp	al,' '			; space or less?
	jg	getcs2			; g = no, keep copying
	xor	al,al			; terminate string on spaces etc
getcs2:	stosb				; store it in destination
	or	al,al			; at end of string yet?
	jnz	getcs1			; nz = no, keep copying
	pop	ds			; recover ds
getcs3:	pop	es
	ret
getcsp	endp

; Get Kermit parameters from the Environment. Parameters are commands like
; regular commands except they do not appear in the SET main table and are
; separated from one another by semicolons. They appear after KERMIT=.
; Do not allow Take/Macros to be created by any of these commands.
; On fatal error exits with carry set and flags.extflg = 1
getparm	proc	near
	push	ax
	push	bx
	push	cx
	push	dx
	push	si
	push	di
	push	es
	mov	es,psp			; segment of our PSP
	mov	ax,es:word ptr[env]	; pick up environment address
	mov	es,ax
	mov	bx,offset kerenv	; Environment word to find, asciiz
	mov	dx,bx
	call	strlen			; length of its string to cx
	call	getenv			; return dx = offset in environment
	jnc	getpar1			; nc = success
	jmp	getpar9			; c = not found
getpar1:push	ds			; save regular DS
	push	dx			; push Environment offset, then seg
	push	es
	call	takopen_macro		; open Take buffer in macro space
	jnc	getpar2			; nc = success
getpar1a:mov	flags.extflg,1		; say exit now
	pop	es			; clean stack
	pop	dx
	pop	ds
	jmp	getparx			; exit with fatal error

getpar2:mov	ax,tbufsiz		; take buffer size (bytes)
	call	malloc			; get memory, seg returned in AX
	jc	getpar1a		; c = failed
	mov	bx,takadr		; bx = Take data structure
	mov	[bx].takbuf,ax		; segment of memory
	or	[bx].takattr,take_malloc ; remember to dispose via takclos
	mov	es,ax			; ES = segment of buffer
        mov     di,2                    ; skip count word field for es:di
	pop	ds			; pop Environment segment (was in ES)
	pop	si			;  and seg, DS:SI is now Environment
	xor	cx,cx			; line length counter
getpar3:lodsb				; read an Environment character
	or	al,al			; null (EOL)?
	jz	getpar5			; z = yes, stop here
	cmp	al,';'			; semicolon separator?
	jne	getpar4			; ne = no
	mov	al,CR			; replace semicolon with carriage ret
getpar4:stosb				; store char in Take buffer
	inc	cx			; count line length
	jmp	short getpar3		; get more text, until a null

getpar5:mov	al,CR			; terminate line, regardless
	stosb
	inc	cx			; count terminator
	pop	ds			; restore regular DS
	mov	[bx].takcnt,cx		; chars in Take/macro buffer
        mov     es:[0],cx               ; store count byte
        mov     [bx].takptr,2           ; init buffer read ptr to first char
	jcxz	getpar8			; z = nothing left, exit
					; parse each item as a command
getpar6:mov	comand.cmquiet,1	; no screen display
	mov	dx,offset nulprmpt	; set null prompt
	call	prompt
	cmp	flags.extflg,0		; exit flag set?
	jne	getpar8			; ne = yes, exit this routine now
	mov	dx,offset initab	; table of initialization routines
	xor	bx,bx			; no explict help text
	mov	comand.cmcr,1		; allow bare CR's
	mov	comand.impdo,0		; do not search Macro table
        mov	ah,cmkey		; match a keyword
	call	comnd
	jc	getpar7			; c = failure
	mov	comand.cmcr,0		; no more bare CR's
	call	bx			; call the routine returned in BX
					; ignore failures (carry bit set)
getpar7:cmp	taklev,0		; finished Take file?
	jle	getpar9			; le = yes
	cmp	flags.extflg,0		; exit flag set?
	je	getpar6			; e = no, finish all commands

getpar8:call	takclos			; close our take file, if open
getpar9:mov	flags.extflg,0		; do not leave this flag set
	clc				; clear for success
getparx:mov	comand.cmquiet,0	; regular screen echoing
	pop	es
	pop	di
	pop	si
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	ret
getparm	endp

; Locate string variable in Environment
; bx = variable to find (usually including =), cx = length of variable name.
; Returns dx = offset within Environment of char following the name and
; carry clear, else carry set and dx unchanged.
getenv	proc	near
	push	ax
	push	cx
	push	si
	push	di
	push	es
	mov	es,psp
	mov	ax,es:word ptr[env]	; pick up environment address
	mov	es,ax
	xor	di,di			; start at offset 0 in segment
geten1:	cmp	es:byte ptr [di],0	; end of environment?
	je	geten3			; yes, forget it
	push	cx			; save counter
	push	di			; and offset
	mov	si,bx
	cld
	repe	cmpsb			; search for name
	pop	di
	pop	cx			; restore these
	je	geten2			; found it, break loop
getenv5:push	cx			; preserve again
	mov	cx,0ffffh		; bogus length
	xor	al,al			; 0 = marker to look for
	repne	scasb			; search for it
	pop	cx			; restore length
	jmp	short geten1		; loop thru rest of environment
geten2:	add	di,cx			; skip to definition
geten6:	mov	si,bx			; name
	add	si,cx			; length
	cmp	byte ptr [si-1],'='	; caller wanted '=' as last char?
	je	geten7			; e = yes, and we found it
	mov	al,es:[di]		; get next char
	cmp	al,'='			; at the equals sign?
	je	geten7			; e = yes
	inc	di			; point at next char
	cmp	al,' '			; white space?
	jbe	geten6			; be = yes, skip over this
	dec	di			; backup
	jmp	short getenv5		; not a match, keep looking
geten7:	mov	dx,di			; store offset of string
	clc				; carry clear for success
	jmp	short geten4
geten3:	stc				; carry set for failure
geten4:	pop	es
	pop	di
	pop	si
	pop	cx
	pop	ax
	ret
getenv	endp

; Store drive:path\ of where Kermit was started into string startup
getargv	proc	near
	push	si
	push	di
	push	es
	mov	es,psp
	mov	ax,es:word ptr[env]	; pick up environment address
	mov	es,ax
	xor	di,di			; start at offset 0 in segment
	mov	cx,0ffffh		; bogus length
getargv1:xor	al,al			; 0 = marker to look for
	repne	scasb			; search for it
	cmp	es:byte ptr [di],0	; end of environment (double null)?
	jne	getargv1		; ne = no
	inc	di			; skip single null
	cmp	es:word ptr [di],1	; marker for argv[0]?
	jne	getargv5		; ne = no
	add	di,2			; skip word
	mov	si,offset startup	; startup string
	mov	cx,63			; max length of wanted string
getargv2:mov	al,es:[di]		; get char
	mov	[si],al			; store in startup string
	inc	si
	inc	di
	or	al,al			; null terminator?
	loopnz	getargv2		; nz = no
					; trim off filename part
	mov	cx,si
	sub	cx,offset startup
	dec	si
getargv3:cmp	byte ptr [si],':'	; back to drive terminator?
	jne	getargv3a		; ne = no
	mov	byte ptr [si+1],'\'	; make drive:\ syntax for root
	inc	si			; move over separators
	jmp	short getargv4		; done
getargv3a:cmp	byte ptr [si],'\'	; or path separator?
	je	getargv4		; e = yes
	dec	si			; backup
	loop	getargv3

getargv4:mov	byte ptr [si+1],0	; terminate string
getargv5:pop	es
	pop	di
	pop	si
	ret
getargv	endp

; Get thousands separator from DOS Country Information
gettsep	proc	near
	mov	ah,38h			; Get Country Information
	mov	dx,offset tmpbuf	; temp buffer
	int	dos
	mov	bx,7			; assume DOS 3+ position in buffer
	cmp	byte ptr dosnum+1,3	; DOS 3 or above?
	jae	gettse1			; ae = yes
	mov	bx,4			; for DOS 2.1
	cmp	dosnum,210h		; earlier than version 2.1?
	jae	gettse1			; ae = no
	mov	al,','			; use comma for old DOS's
gettse1:mov	al,tmpbuf[bx]		; get thousands separator char
	mov	thsep,al		; save it
	mov	al,tmpbuf		; get time/date format code
	mov	tdfmt,al		; save it
	ret
gettsep	endp

STAY	PROC	NEAR
	clc
	ret
STAY	ENDP

; CHECK <build features>, returns success if feature is present
CHECK	proc	near
	mov	kstatus,kssuc		; global status
	mov	ah,cmkey
	mov	dx,offset featab	; feature table
	xor	bx,bx			; table is help
	call	comnd
	jnc	check1			; nc = success
	mov	kstatus,ksgen		; general failure
	ret
check1:	push	bx
	mov	ah,cmeol
	call	comnd
	pop	bx
	jnc	check1a
	mov	kstatus,ksgen		; general failure
	ret
check1a:mov	di,offset tmpbuf
	cmp	bx,1			; graphics?
	jne	check2			; ne = no
	mov	si,offset msggraph
	call	strcpy
ifdef	no_graphics
	mov	kstatus,ksgen
	mov	si,offset msgnot
	call	strcat
endif
	jmp	short check5

check2:	cmp	bx,2			; tcp/ip?
	jne	check3			; ne = no
	mov	si,offset msgtcpip
	call	strcpy
ifdef	no_tcp
	mov	kstatus,ksgen
	mov	si,offset msgnot
	call	strcat
endif
	jmp	short check5

check3:	cmp	bx,3			; networks?
	jne	check4			; ne = no
	mov	si,offset msgnetwork
	call	strcpy
ifdef	no_network
	mov	kstatus,ksgen
	mov	si,offset msgnot
	call	strcat
endif
	jmp	short check5

check4:	mov	si,offset msgterm	; terminals
	call	strcpy
ifdef	no_terminal
	mov	kstatus,ksgen
	mov	si,offset msgnot
	call	strcat
endif

check5:	mov	si,offset msgavail
	call	strcat
	cmp	taklev,0		; at top level?
	jne	check6			; ne = no, be quiet
	mov	dx,offset tmpbuf	; show msg
	mov	ah,prstr
	int	dos
check6:	ret				; return status
CHECK	endp

TESTCOM	proc	near
	mov	kstatus,kssuc		; global status
	mov	ah,cmkey
	mov	dx,offset chktab	; check comm port table
	xor	bx,bx			; table is help
	call	comnd
	jnc	testc1			; nc = success
	mov	kstatus,ksgen		; general failure
	ret
testc1:	mov	al,flags.comflg		; save current comms port flag
	push	ax
	mov	flags.comflg,bl		; port number, 1..4
	call	tstport			; see if real UART, carry set if not
	pop	ax
	mov	flags.comflg,al		; restore flag
	mov	dx,offset erms39	; say UART
	jnc	testc2			; nc = real UART
	mov	dx,offset erms38	; say non-UART
	mov	kstatus,ksgen		; set failure state for non-UART
testc2:	cmp	taklev,0		; in a Take file or macro?
	je	testc3			; e = no, display
	cmp	flags.takflg,0		; Take echo off?
	je	testc4			; e = yes, do not display
testc3:	mov	ah,prstr
	int	dos
testc4:	ret
TESTCOM endp

CLS	proc	near			; Clear command level screen
	mov	ah,cmeol		; get a confirmation
	call	comnd
	jc	cls1			; c = failure
	call	cmblnk			; blank the screen
	call	locate			; put cursor at home position
cls1:	ret
CLS	endp

COMNT	PROC	NEAR			; COMMENT command
	mov	ah,cmline
	mov	bx,offset tmpbuf
	xor	dx,dx
	call	comnd
	jc	comnt1
	mov	ah,cmeol
	call	comnd
comnt1:	ret
COMNT	ENDP

; change working directory
cwdir	proc	near
	mov	kstatus,kssuc		; global status
	mov	ah,cmword
	mov	dx,offset tmpbuf
	mov	bx,offset pthmsg
	mov	word ptr tmpbuf,0
	call	comnd			; get drive/dir spec, if any
	mov	ah,cmeol
	call	comnd
	jnc	cwd1
	ret				; c = failure
cwd1:	mov	si,offset tmpbuf	; cdsr wants drive/path ptr in si
	mov	di,si
	call	cnvlin			; convert {} and \\
	call	cdsr			; common CD sub-routine
	jnc	cwd2			; nc = success
	mov	kstatus,ksgen		; global status for unsuccess
cwd2:	cmp	taklev,0		; in a Take file or macro?
	je	cwd3			; e = no, do echo
	cmp	flags.takflg,0		; ok to echo?
	je	cwd4			; e = no
cwd3:	push	dx
	mov	dx,offset crlf		; msgs from cdsr don't include this
	mov	ah,prstr		; so let's do it now
	int	dos
	pop	dx
	call	prtasz			; output current drive/path or err msg
cwd4:	clc
	ret
cwdir	endp

; CDSR processes both CD & REM CD.  Entered with si --> drive/path, it returns
; dx --> ASCIIZ current drive/path, w/carry clear, if successful, or error msg
; w/carry set, if not.
CDSR	PROC
	mov	kstatus,kssuc		; global status
	xor	cx,cx			; 0 for default drive, if none
	cmp	byte ptr[si],ch		; any drive/path?
	je	cdsr4			; e = no, format current drive/path
	cmp	byte ptr[si+1],':'	; is drive specified?
	jne	cdsr1			; ne = no
	mov	cl,[si]			; drive letter
;	cmp	byte ptr[si+2],ch	; any path?
;	jne	cdsr1			; ne = yes
;	mov	word ptr[si+2],'.'	; append dot+null as path to kludge DOS
cdsr1:	call	dskspace		; test for drive, spec'd by cl, ready
	jnc	cdsr2			; nc = ready
	mov	spcmsg3,cl		; insert drive letter ret'd by dskspace
	mov	dx,offset spcmsg2+2	; in err msg.  dx --> msg w/o cr,lf
	mov	kstatus,ksgen		; global status
	ret				; carry is set

cdsr2:	push	cx			; save drive letter part
	mov	dx,si
cdsr2a:	call	strlen
	mov	bx,cx			; look at last char+1
	dec	bx			; last byte
	cmp	cx,1			; if any bytes
	jbe	cdsr2b			; be = enough for 1 char
	cmp	byte ptr [si+bx],'\'	; ends on backslash?
	jne	cdsr2b			; ne = no
	cmp	byte ptr [si+bx-1],':'	; ends as ":\"?
	je	cdsr2b			; e = yes, leave intact for roots
	mov	byte ptr [si+bx],0	; trim trailing backslash
	jmp	short cdsr2a		; keep trimming
cdsr2b:	pop	cx			; restore drive letter
	mov	dl,cl			; uc drive letter ret'd by dskspace
	sub	dl,'A'			; A = 0 for seldsk
	mov	ah,seldsk
	int	dos
	inc	dl			; A = 1 for curdsk
	mov	curdsk,dl
	mov	dx,si			; where chdir wants it
	cmp	byte ptr [si+1],':'	; drive specified?
	jne	cdsr3			; ne = no, just path
	add	dx,2			; skip "drive:"
	cmp	byte ptr [si+2],0	; any path?
	je	cdsr4			; e = no
cdsr3:	mov	ah,chdir
	int	dos
	jnc	cdsr4			; nc = success
	mov	dx,offset ermes4	; ret carry set, dx --> err msg
	ret

cdsr4:	push	si			; use caller's buffer for cur dr/path
	mov	ax,':@'			; al = 'A' - 1, ah = ':'
	add	al,curdsk		; al = drive letter
	mov	[si],ax			; stash drive:
	inc	si
	inc	si
	mov	byte ptr[si],'\'	; add \
	inc	si
	mov	ah,gcd			; gcd fills in path as ASCIIZ
	xor	dl,dl			; use current drive
	int	dos
	pop	dx			; return caller's buffer pointer in dx
	clc
	ret
CDSR	ENDP

; Erase specified file(s). Add protection of ignore hidden, subdir, volume
; label and system files. 9 Jan 86 [jrd]
DELETE	PROC	NEAR			; includes paths and "?*" wildcards
	mov	kstatus,kssuc		; global status
	mov	si,offset delcmd	; del command
	mov	di,offset tmpbuf
	call	strcpy
	mov	dx,offset tmpbuf
	call	strlen			; get its length
	add	di,cx			; point at terminator
	mov	temp,di			; remember starting spot
	mov	ah,cmline		; get a line
	mov	bx,di			; where to place the file spec
	mov	dx,offset filmsg	; help message
	call	comnd
	jc	delet0			; c = failure
	push	ax
	mov	ah,cmeol
	call	comnd
	pop	ax
	jc	delet0
	cmp	apctrap,0		; disable from APC
	jne	delet0			; ne = yes
	or	ax,ax			; anything given?
	jnz	delet1			; nz = yes
	mov	ah,prstr
	mov	dx,offset ermes1	; say need something
	int	dos
	clc				; say success
delet0:	mov	kstatus,ksgen		; global status
	ret

delet1:	mov	si,offset tmpbuf	; source
	mov	di,temp			; start of filespec
	xor	cl,cl			; disk drive letter
	cmp	byte ptr [di+1],':'	; drive specified?
	jne	delet2			; ne = no
	mov	cl,[di]			; get drive letter
delet2:	call	dskspace		; compute space, get letter into CL
	jnc	delet3			; nc = success
	mov	spcmsg3,cl		; put drive letter in msg
	mov	dx,offset spcmsg2	; error message
	call	prtasz
	mov	kstatus,ksgen		; global status
	clc
	ret				; and ignore this command
delet3:	mov	si,offset tmpbuf	; del cmd
	jmp	crun			; join run cmd from there
DELETE	ENDP

; Space <optional drive letter>

CHKDSK	PROC	NEAR			; Space command
	mov	kstatus,kssuc		; global status
	mov	dx,offset tmpbuf	; buffer
	mov	tmpbuf,0		; init to null
	mov	bx,offset dskmsg	; help message
	mov	ah,cmword		; get optional drive letter
	call	comnd			; ignore errors
	mov	ah,cmeol
	call	comnd
	jnc	chkdsk1			; nc = success
	ret				; failure
chkdsk1:mov	cl,tmpbuf		; set drive letter
	call	dskspace		; compute space, get letter into CL
	jnc	chkdsk2			; nc = success
	and	cl,5fh			; to upper case
	mov	spcmsg3,cl		; insert drive letter
	mov	dx,offset spcmsg2	; say drive not ready
	call	prtasz
	mov	kstatus,ksgen		; global status
	clc
	ret
	
chkdsk2:mov	spcmsg1,cl		; insert drive letter
	mov	di,offset tmpbuf	; work space for lnout
	mov	word ptr[di],0a0dh	; cr/lf
	mov	word ptr[di+2],'  '	; add two spaces
	add	di,4
	call	lnouts			; use thousands separator
	mov	si,offset spcmsg
	call	strcat			; add text to end of message
	mov	dx,offset tmpbuf
	call	prtasz			; print asciiz string
	clc
	ret
CHKDSK	ENDP

; Compute disk free space (bytes) into long word dx:ax.
; Enter with disk LETTER in CL (use null if current disk).
; Returns uppercase drive letter in CL.
; Returns carry set if drive access error. Changes AX, DX.

DSKSPACE PROC	NEAR
	mov	dl,cl			; desired disk letter, or null
	or	dl,dl			; use current disk?
	jnz	dskspa1			; nz = no
	mov	ah,gcurdsk		; get current disk
	int	dos
	add	al,'A'			; make 0 ==> A
	mov	dl,al
dskspa1:and	dl,5fh			; convert to upper case
	mov	cl,dl			; return upper case drive letter in CL
	sub	dl,'A'-1		; 'A' is 1, etc
	push	bx
	push	cx
	mov	ah,36h			; get disk free space, bx=sect/cluster
	int	dos			; dx:ax=sectors, cx=bytes/sector
	cmp	ax,0ffffh		; error response?
	jne	dskspa2			; ne = no
	pop	cx
	pop	bx
	stc				; return error
	ret
dskspa2:mul	bx			; sectors/cluster * clusters = sectors
	mov	bx,dx			; save high word of sectors (> 64K)
	mul	cx			; bytes = sectors * bytes/sector
	push	ax			; save low word of bytes
	mov	ax,bx			; recall sectors high word
	mov	bx,dx			; save current bytes high word
	mul	cx			; high word sectors * bytes/sector
	add	ax,bx			; new high bytes + old high bytes
	mov	dx,ax			; store high word in dx
	pop	ax			; space is in dx:ax as a long word
	pop	cx
	pop	bx
	clc
	ret
DSKSPACE ENDP

; Get directory	listing
DIRECT	PROC	NEAR
	mov	kstatus,kssuc		; global status
	mov	si,offset dircmd	; dir command
	mov	di,offset tmpbuf
	call	strcpy
	mov	dx,offset tmpbuf
	call	strlen			; get its length
	add	di,cx			; point at terminator
	mov	temp,cx			; remember length
	mov	ah,cmline		; parse with cmline to allow switches
	mov	bx,di			; next available byte
	mov	dx,offset filmsg	; help message 
	call	comnd
	jnc	direct1			; nc = success
direct0:mov	kstatus,ksgen		; global status
	ret				; failure
direct1:mov	ah,cmeol
	call	comnd
	jc	direct0
	mov	word ptr [bx],0		; plant terminator
	mov	si,offset tmpbuf
	push	si
	add	si,temp			; user's text after ' dir '
	mov	cl,curdsk		; current drive number ('A'=1)
	add	cl,'A'-1		; make a letter
	cmp	byte ptr [si+1],':'	; drive specified?
	jne	direct2			; ne = no, use current drive
	mov	cl,[si]			; get drive letter from buffer
direct2:call	dskspace		; check for drive ready
	pop	si
	jnc	direct3			; nc = drive ready
	mov	spcmsg3,cl		; insert letter
	mov	dx,offset spcmsg2	; say drive is not ready
	call	prtasz
	mov	kstatus,ksgen		; global status
	stc
	ret
direct3:jmp	crun			; join run cmd from there
DIRECT	ENDP

; This is the 'HELP' command.  It gives a list of the commands
; And INTRO command
INTRO	proc	near
	mov	kstatus,kssuc		; global status
	mov	ah,cmeol
	call	comnd			; get a confirm
	jnc	intro1			; nc = success
	ret				; failure
intro1:	mov	si,offset intrhlp	; Intro text in seg data1
	xor	bx,bx			; line counter
	push	es
	mov	ax,seg intrhlp		; all Intro text is in data1
	mov	es,ax
	cld
 	jmp	short help2		; common tail
INTRO	endp

HELP	PROC	NEAR
	mov	kstatus,kssuc		; global status
	mov	ah,cmeol
	call	comnd			; get a confirm
	jnc	help1			; nc = success
	ret				; failure
help1:	mov	si,offset qckhlp	; help text in seg data1
	xor	bx,bx			; line counter
	push	es
	mov	ax,seg qckhlp		; all help text is in data1
	mov	es,ax
	cld

HELP2:	mov	al,es:[si]		; read a help msg byte
	inc	si
	cmp	al,'$'			; end of message?
	je	help3			; e = yes, stop
	mov	ah,conout
	mov	dl,al
	int	dos			; display byte
	cmp	dl,LF			; line break?
	jne	help2			; ne = no
	inc	bl			; count line
	cmp	bl,dos_bottom		; (24) time for a more msg?
	jbe	help2			; be = not yet
	xor	bl,bl			; reset line count
	call	iseof			; are we at EOF, such as from disk?
	jc	help2			; c = yes, ignore more msg
	push	es
	push	si
	mov	ah,prstr
	mov	dx,offset moremsg	; "... more..." msg
	int	dos
	pop	si
	pop	es
	mov	ah,coninq		; read the char from file, not device
	int	dos
	cmp	al,3			; a ^C?
	je	short help3		; e = yes, stop the display
	cmp	al,'?'			; query?
	je	helpquery		; e = yes
	push	bx			; save line counter
	push	es			; and read pointer
	push	si
	call	ctlu			; clear display's line, reuse it
	pop	si
	pop	es
	pop	bx
	jmp	short help2		; continue
help3:	pop	es
	clc
	ret

helpquery:pop	es
	mov	ah,prstr		; show help summary screen
	mov	dx,offset crlf		; a few blank lines
	int	dos
	int	dos
	int	dos
	push	ds
	mov	dx,seg tophlp
	mov	ds,dx
	mov	dx,offset tophlp	; show usual cryptic help
	int	dos
	pop	ds
	clc
	ret
HELP	ENDP

; the version command - print our version number
prvers	proc	near
	mov	kstatus,kssuc		; global status
	mov	ah,cmeol
	call	comnd
	jc	prvers1			; c = failure
	mov	ah,prstr
	mov	dx,offset crlf
	int	dos
	mov	ah,prstr
	mov	dx,offset machnam	; display machine name
	int	dos
	mov	ah,prstr		; display the version header
	mov	dx,offset verident
	int	dos
	mov	ah,prstr
	mov	dx,offset copyright2	; full copyright notice
	int	dos
	clc
prvers1:ret
prvers	endp

; SHOW command dispatcher
showcmd	proc	near
	mov	ah,cmkey
	mov	dx,offset shotab
	xor	bx,bx			; no canned help
	call	comnd
	jc	showc1			; c = failure
	jmp	bx			; execute the handler
showc1:	ret				; failure
showcmd	endp

; the type command - type out a file
typec	proc	near
	mov	kstatus,kssuc		; global status
	mov	si,offset typcmd	; type command
	mov	di,offset tmpbuf
	call	strcpy
	mov	dx,offset tmpbuf
	call	strlen			; get its length
	add	di,cx			; point at terminator
	mov	temp,di			; save place for later
	mov	ah,cmline		; parse with cmline, allows | more
	mov	bx,di			; next available byte
	mov	dx,offset filmsg	; In case user wants help
	call	comnd
	jc	typec1			; c = failure
	push	ax
	mov	ah,cmeol
	call	comnd
	pop	ax
	jc	typec1
	or	ax,ax			; any text given?
	jnz	typec2			; nz = yes
	mov	ah,prstr
	mov	dx,offset ermes1	; say need more info
	int	dos
	mov	kstatus,ksgen		; global status
	clc
typec1:	ret
typec2:	mov	byte ptr [bx],0		; plant terminator
	mov	si,temp			; start of filespec
	xor	cl,cl			; say local drive
	cmp	byte ptr [si+1],':'	; drive given?
	jne	typec3			; ne = no
	mov	cl,[si]			; get drive letter
typec3:	call	dskspace		; check for drive ready
	jnc	typec4
	mov	spcmsg3,cl		; put drive letter in msg
	mov	dx,offset spcmsg2	; error message
	call	prtasz
	mov	kstatus,ksgen		; global status
	clc
	ret				; and ignore this command
typec4:	mov	si,offset tmpbuf
	jmp	short crun		; join run cmd from there
typec	endp

; PUSH to DOS (run another copy of Command.com or equiv)
; entry fpush (fast push...) pushes without waiting for a confirm
dopush	proc	near
	mov	ah,cmeol
	call	comnd
	jnc	fpush			; nc = success
	ret				; failure
fpush:	mov	si,offset tmpbuf	; a dummy buffer
	mov	byte ptr [si],0		; plant terminator
	mov	dx,offset cmspbuf	; always use command.com
	cmp	shellbuf,0		; SHELL= present?
	je	crun4			; e = no, use COMSPEC= name
	mov	dx,offset shellbuf	; use SHELL= name
	jmp	short crun4		; go run it
	stc
	ret
dopush	endp

; Run a program from within Kermit
RUN	PROC	NEAR
	mov	ah,cmline		; get program name and any arguments
	mov	bx,offset tmpbuf	; place for user's text
	mov	dx,offset runmsg	; In case user wants help
	call	comnd
	jnc	run1			; nc = success
	ret				; failure
run1:	cmp	apctrap,0		; disable from APC
	je	run1a			; e = no
	stc				; yes, fail
	ret
run1a:	or	ax,ax			; byte count
	jnz	run2			; nz = have program name
	mov	ah,prstr		; else complain
	mov	dx,offset ermes1	; need more info
	int	dos
	clc
	ret
run2:	mov	si,offset tmpbuf	; source of text
	jmp	short crun
RUN	ENDP

; crun - run an arbitrary program.
; Enter with ordinary ASCIIZ command in SI (such as Dir *.asm)
; Append a c/r and a null terminator and then ask command.com to do it
; Set errlev with DOS errorlevel from subprocess.
CRUN	proc	near
	mov	ah,prstr		; output crlf before executing comnd
	mov	dx,offset crlf		; [lba]
	int	dos
	mov	di,si
	call	cnvlin			; convert {} and \\
	mov	di,offset tmpbuf	; where to put full command line text
	cmp	si,di			; same place?
	je	crun1			; e = yes, don't copy ourself
	call	strcpy			; si holds source text
crun1:	mov	si,offset slashc	; DOS command begins with slashc area
	mov	dx,offset slashc+1	; si points to /c part of command line
	call	strlen			; get its length into cx
	push	bx
	mov	bx,dx
	add	bx,cx
	mov	byte ptr [bx],cr	; end string with a c/r for dos
	inc	cx			; count the c/r
	mov	byte ptr [bx+1],0	; and terminate
	pop	bx
	mov	[si],cl			; put length of argument here
	mov	dx,offset cmspbuf	; always use command.com
crun4:	mov	exearg+2,si		; pointer to argument string
	mov	exearg+4,ds		; segment of same
	cmp	lclsusp,0		; sys dependent routine to call
	je	crun5			; e = none
	mov	bx,lclsusp		; address to call
	push	dx			; preserve name in dx
	call	bx			; call sys dependent suspend routine
	pop	dx
crun5:	push	dx			; preserve name in dx
	call	serrst			; reset serial port (if active)
	call	cbrestore		; restore state of Control-Break Chk
	pop	dx
	mov	es,psp			; point to psp again
	mov	exearg+8,es		; segment of psp, use our def fcb's
	mov	exearg+12,es		; segment of psp, ditto, for fcb 2
	mov	ax,es:word ptr [env]	; get environment ptr
	mov	exearg,ax		; put into argument block
	mov	ax,ds
	mov	es,ax			; put es segment back
	mov	bx,offset exearg	; es:bx points to exec parameter block
	mov	ax,ss			; save ss:sp
	mov	word ptr ssave+2,ax
	mov	word ptr ssave,sp
	xor	al,al			; 0 = load and execute (DX has name)
	mov	ah,exec
	int	dos			; go run command.com
	mov	bx,data			; restore segment registers
	mov	ds,bx			; reset data segment
	mov	es,bx			; and extra segment
	cli
	mov	bx,word ptr ssave+2
	mov	ss,bx			; and stack segment
	mov	sp,word ptr ssave	; restore stack ptr
	sti
	jnc	crun9			; nc = no error
	mov	ah,prstr		; failure, complain
	mov	dx,offset erms37
	int	dos
	mov	dx,offset cmspbuf	; path\name of command.com
	call	prtasz			; asciiz
	mov	kstatus,ksgen		; global status
crun9:	mov	ah,setdma		; restore dma buffer pointer
	mov	dx,offset buff
	int	dos			; restore dma address!!
	call	cboff			; turn off DOS BREAK check
	cmp	lclrest,0		; sys dependent routine to call?
	je	crun10			; e = none
	mov	bx,lclrest		; get routine's address
	call	bx			; call sys dependent restore routine
crun10:	clc
	ret
CRUN	ENDP
code	ends

code1	segment
	assume	cs:code1 
; Replace Int 23h and Int 24h with our own handlers
; Revised to ask DOS for original interrupt vector contents, as suggested by
; Jack Bryans. 9 Jan 1986 jrd
; Modified again 30 August 1986 [jrd]
SETINT	PROC	FAR
	push	es			; save registers
	mov	al,23H			; desired interrupt vector (^C)
	mov	ah,getintv		; Int 21H, function 35H = Get Vector
	int	dos			; get vector in es:bx
	mov	in3ad,bx		; save offset of original vector
	mov	in3ad+2,es		;   and its segment
	mov	al,24h			; DOS critical error, Int 24h
	mov	ah,getintv
	int	dos
	mov	word ptr ceadr,bx	; DOS's Critical Error handler, offset
	mov	word ptr ceadr+2,es	;  and segment address
	push	ds			; save ds around next DOS calls
	mov	ax,seg intbrk		; compose full address of ^C routine
	mov	ds,ax			; segment is the code segment
	mov	dx,offset intbrk	;   and offset is intbrk
	mov	al,23H			; on ^C, goto intbrk
	mov	ah,setintv		; set interrupt address from ds:dx
	int	dos
	mov	dx,offset dosce		; replacement Critical Error handler
	mov	al,24h			; interrupt 24h
	mov	ah,setintv		; replace it
	int	dos
	pop	ds
	mov	ax,3300h		; get state of Control-Break Check
	int	dos
	mov	orgcbrk,dl		; save state here
	pop	es
	ret
SETINT	ENDP

; Control Break, Interrupt 23h replacement
; Always return with a Continue (vs Abort) condition since Kermit will cope
; with failures. [jrd]
intbrk:	push	ax
	push	ds	
	mov	ax,data			; get Kermit's data segment
	mov	ds,ax
	mov	flags.cxzflg,'C'	; say we saw a ^C
	mov	rstate,'E'
	mov	sstate,'E'
	pop	ds
	pop	ax
	iret			   ; return to caller in a Continue condition

; Kermit's DOS Critical Error Handler, Int 24h. [jrd]
; Needed to avoid aborting Kermit with the serial port interrupt active and
; the Control Break interrupt redirected. See the DOS Tech Ref Manual for
; a start on this material; it is neither complete nor entirely accurate
; The stack is the Kermit's stack, the data segment is unknown, interrupts
; are off, and the code segment is Kermit's. Note: some implementations of
; MS DOS may leave us in DOS's stack. Called by a DOS Int 21h function
dosce:	test	ah,80h		; block device (disk drive)?
	jnz	dosce1		; nz = no; serial device, memory, etc
	mov	al,3		; tell DOS to Fail the Int 21h call
	iret			; return to DOS
dosce1:	add	sp,6		; pop IP, CS, Flags regs, from DOS's Int 24h
	pop	ax		; restore original callers regs existing
	pop	bx		;  just before doing Int 21h call
	pop	cx
	pop	dx
	pop	si
	pop	di
	pop	bp
	pop	ds
	pop	es		
	mov	al,0ffh		; signal failure (usually) the DOS 1.x way
	push	bp		; Kermit's IP, CS, and Flags are on the stack
	mov	bp,sp		;  all ready for an iret, but first a word ..
	or	word ptr[bp+8],1 ; set carry bit, signals failure DOS 2+ way
	pop	bp		; this avoids seeing the Interrupt flag bit
	iret			; return to user, simulate return from Int 21h
code1	ends

code	segment
	assume	cs:code

; Set DOS' Control-Break Check to off
cboff	proc	near
	mov	ax,3301h		; set Control-Break Chk state
	xor	dl,dl			; set state to off
	int	dos
	ret
cboff	endp

; Restore DOS's Control-Break Check to startup value
cbrestore proc	near
	push	dx
	mov	ax,3301h		; set Control-Break Chk state
	mov	dl,orgcbrk		; restore state to startup value
	int	dos
	pop	dx
	ret
cbrestore endp

ISFILE	PROC	NEAR
; Enter with ds:ax pointing at asciiz filename string
; Returns carry set if the file pointed to by ax does not exist, else reset
; Returns status byte, fstat, with DOS status and high bit set if major error
; Does a search-for-first to permit paths and wild cards
; Examines All kinds of files (ordinary, subdirs, vol labels, system,
;  and hidden). Upgraded to All kinds on 27 Dec 1985. Revised 30 Aug 86 [jrd]
; All registers are preserved
	push	bx
	push	ax
	mov	bx,ax
	mov	ax,[bx]
	and	ax,not 2020h		; to upper
	cmp	ax,'UN'			; look for DOS 5 NUL
	jne	isfil4			; ne = mismatch
	mov	ax,[bx+2]
	and	al,not 20h
	cmp	ax,'L'+0
	jne	isfil4			; ne = mismatch
	pop	ax
	pop	bx
	clc				; say success
	ret
isfil4:	pop	ax
	pop	bx
	push	dx			; save regs
	push	cx
	push	ax
	mov	byte ptr filtst.dta+21,0 ; clear old attribute bits
	mov	byte ptr filtst.fname,0	; clear any old filenames
	mov	filtst.fstat,0		; clear status byte
	mov 	cx,3fH			; look at all kinds of files
	mov	dx,offset filtst.dta	; own own temporary dta
	mov	ah,setdma		; set to new dta
	int	dos
	pop	dx			; get ax (filename string ptr)
	push	dx			; save it again
	mov	ah,first2		; search for first
	int	dos
	pushf				; save flags
	mov	dx,offset buff		; reset dma
	mov	ah,setdma
	int	dos
	popf				; recover flags
	jnc	isfil1			; nc = file found
	mov	filtst.fstat,al		; record DOS status
	cmp	al,2			; just "File Not Found"?
	je	isfil2			; e = yes
	cmp	al,3			; "Path not found"?
	je	isfil2			; e = yes
	cmp	al,18			; "No more files"?
	je	isfil2			; e = yes
	or	filtst.fstat,80h	; set high bit for more serious error
	jmp	short isfil2	
isfil1:	cmp	byte ptr filtst.fname,0	; did DOS fill in a name?
	je	isfil2			; z = no
	clc
	jmp	short isfil3
isfil2:	stc				; else set carry flag bit
isfil3:	pop	ax
	pop	cx
	pop	dx
	ret				; DOS sets carry if file not found
ISFILE	ENDP

; initialize memory usage by returning to DOS anything past the end of kermit
memini	proc	near
	push	es
	mov	bx,_TEXT
	sub	bx,psp
	mov	totpar,bx		; save for patcher's magic number
	mov	es,psp			; address psp segment again
	mov	bx,offset msfinal + 15	; end of pgm + roundup
	mov	cl,4
	shr	bx,cl			; compute # of paragraphs in last seg
	mov	ax,_STACK		; last segment
	sub	ax,psp			; minus beginning
	add	bx,ax			; # of paragraphs occupied
	mov	ah,setblk
	int	dos
	pop	es
	jnc	memin1
	mov	dx,offset ermes2
	mov	ah,prstr
	int	dos			; complain
	jmp	krmend			; exit Kermit now
memin1:	pop	dx			; save return address here
	mov	ax,_STACK		; move SS down to DGROUP, adj SP
	sub	ax,DGROUP		; paragraphs to move
	mov	cl,4			; convert to bytes
	shl	ax,cl
	mov	bx,SP			; current SP offset
	add	bx,ax			; new SP, same memory cell
	mov	ax,bx
	sub	ax,400			; top 400 bytes = Kermit
	mov	tcptos,ax		; report this as TCP's top of stack
	mov	ax,DGROUP		; new SS
	cli
	mov	SS,ax
	mov	SP,bx			; whew!
	sti
	push	dx			; push return address
	clc
	ret
memini	endp

; Allocate memory.  Passed a memory size in ax, allocates that many
; bytes (actually rounds up to a paragraph) and returns its SEGMENT in ax
; The memory is NOT initialized.  Written by [jrd] to allow memory to
; be allocated anywhere in the 1MB address space
malloc	proc	far
	push	bx
	push	cx
	mov	bx,ax			; bytes wanted
	add	bx,15			; round up
	mov	cl,4
	shr	bx,cl			; convert to # of paragraphs
	mov	cx,bx			; remember quantity wanted
	mov	ah,alloc		; DOS memory allocator
	int	dos
	jc	mallocx			; c = fatal
	cmp	cx,bx			; paragraphs wanted vs delivered
	jae	mallocx			; ae = enough
	push	es
	mov	es,ax
	mov	ah,freemem		; free the memory
	int	dos
	pop	es
	stc				; fail
mallocx:pop	cx
	pop	bx
	ret				; and return segment in ax
malloc	endp

sbrk	proc	near			; K & R, please forgive us
	call	malloc			; allocate memory
	jc	sbrkx			; c = failed
	ret				; success
sbrkx:	mov	dx,offset mfmsg		; assume not enough memory (ax = 8)
	cmp	ax,7			; corrupted memory (ax = 7)?
	jne	sbrkx1			; ne = no
	mov	dx,offset mf7msg	; corrupted memory found
sbrkx1:	mov	ah,prstr
	int	dos
	jmp	krmend			; exit Kermit now
sbrk	endp
code 	ends
	end	start
