2!		PROGRAM		: ATPK.BAS
5!		VERSION		: V10.1
6!		EDIT		: A
7!		EDIT DATE	: 10-MAY-91
10	EXTEND		! USE BASIC-PLUS EXTEND MODE
11!	&
  !	&
  !	&
  !			  C O P Y R I G H T &
  !	&
  !	&
  !		      Copyright (C) 1978, 1991 by &
  !	        Digital Equipment Corporation, Maynard, Mass. &
  !	&
  !	&
  !	This software is furnished under a license and may be used and &
  !	copied  only  in accordance with the terms of such license and &
  !	with the  inclusion  of  the  above  copyright  notice.   This &
  !	software  or  any  other copies thereof may not be provided or &
  !	otherwise made available to any other person.  No title to and &
  !	ownership of the software is hereby transferred. &
  !	&
  !	The information in this software is subject to change  without &
  !	notice  and should not be construed as a commitment by Digital &
  !	Equipment Corporation. &
  !	&
  !	DIGITAL assumes no responsibility for the use  or  reliability &
  !	of its software on equipment that is not supplied by DIGITAL. &
  !	&
  !******************************************************************* &

20	! &
	! &
	! &
	!	M O D I F I C A T I O N    H I S T O R Y &
	! &

21	! VER/ED	EDIT DATE	REASON &
	! &
	! V9.0-07	28-MAR-84	(KMF) Use SPAWN sys call to log in PK &
	! V9.0-09	02-NOV-84	(PRL) Update for multiple privileges &
	! V9.0-14	04-APR-85	(PRL) Allow PRELIN entry to spawn job &
	!				      at restricted PK without DEVICE &
	!				      privilege &
	! V9.5		25-JUN-87	(REG) If PK job detaches, do more CMD &
	!				      lines in case there's a $LOGIN &
	! V9.6		14-MAR-88	(REG) Use dynamic PK's &
	! &
	&

300	! &
	! &
	! &
	!	I / O    C H A N N E L S &
	! &
	! &
	! &
	!    CHANNEL #		USED FOR &
	! &
	!	1		PSEUDO KEYBOARD. &
	!	2		INPUT CONTROL FILE. &
	!	3		OUTPUT LOG FILE. &
	!	4		USER KB: INPUT. &
	!	5		INDIRECT CONTROL FILE. &
	! &

400	! &
	! &
	! &
	!	V A R I A B L E    D E F I N I T I O N S &
	! &
	! &
	! &
	!    VARIABLE NAME	DEFINITION &
	! &
	!	PRELIN.FLG%	-1= PRELIN ENTRY (LINE 30010); ALLOWS ATPK &
	!			    TO BE RUN FROM PK, ALSO DOES NOT REQUIRE &
	!			    DEVICE PRIVILEGE TO SPAWN TO RESTRICTED &
	!			    PK &
	!			 0= NOT A PRELIN ELNTY &
	!	ARG$		ARGUMENT OF SWITCH CURRENTLY BEING PROCESSED. &
	!  L	CHAIN.FLG%	CHAIN ON SUCCESSFUL EXIT. &
	!	CHAIN.LINE%	LINE # TO CHAIN TO ON SUCCESSFUL EXIT. &
	!	CHAIN.PROG$	PROGRAM TO CHAIN TO ON SUCCESSFUL EXIT. &
	!	CHR.6$		CONSTANT CHR$(6%). &
	!	COMMAND$(11)	VALID CONTROL FILE COMMANDS. &
	!	CR.LF$		CONSTANT <CR>-<LF>. &
	!	CR.LF.FF$	CONSTANT <CR>-<LF>-<FF>. &
	!	CTRL.LINE$	CURRENT CONTROL LINE BEING PROCESSED. &
	!  L	DELETE.CTL%	DELETE CONTROL FILE ON SUCCESSFUL COMPLETION. &
	!  L	DET.SW%		THE /DET SWITCH WAS SPECIFIED. &
	!  L	DETACHED%	WE ARE RUNNING DETACHED. &
	!	DEVOKB%		OFFSET OF KB0: IN DEVICE TABLES. &
	!	DEVPTR%		LOCATION OF DEVPTR TABLE. &
	!	DEVTBL%		LOCATION OF DEVTBL TABLE. &
	!	DUMMY$		TARGET FOR SYS CALLS THAT RETURN NOTHING &
	!			WE HAVE USE FOR.  ALWAYS MEANINGLESS. &
	!	ECHO%		ASCII OF LAST CHAR TYPED FROM KB: +1000% &
	!			=0% AFTER ECHO HAS BEEN PROCESSED. &
	!	ENTRY.TYP%	0 -> RUN ENTRY, 2 -> CCL ENTRY. &
	!	ERR.FLG%	<>0 -> POSSIBLE ERROR DETECTED AT PK. &
	!	ERR.MSG$	ATPK ERROR MESSAGE TO PRINT. &
	!  L	ERROR.CHECK%	CHECK FOR (AT LEAST FATAL) ERRORS. &
	!	EXT$		DEFAULT EXTENSION FOR REBUILDING FILESPEC. &
	!	F2$		FIRST TWO CHARACTERS OF PK BUFFER. &
	!	F3$		FIRST THREE CHARACTERS OF PK BUFFER. &
	!	F4$		FIRST FOUR CHARACTERS OF PK BUFFER. &
	!	FILESPEC$	FILE SPECIFICATION TO REBUILD. &
	!	IDENT.STG$	VERSION/EDIT NUMBER FOR HEADER. &
	!  L	IN.AT%		WE ARE PROCESSING A "$@" INDIRECT FILE. &
	!	IN.CH%		CHANNEL FOR INPUTTING CONTROL LINES. &
	!	IOB%		THE LOCATION OF OUR JOB'S I/O BLOCK. &
	!	JOB.NO%		OUR JOB NUMBER. &
	!	JOBTBL%		LOCATION OF JOB TABLE. &
	!	KB$		CONSOLE KEYBOARD NAME, EXPLICIT UNIT NUMBER. &
	!	KB.CH%		CHANNEL ON WHICH TO ACCESS OUR CONSOLE KB:. &
	!	KB.DDB%		THE DDB LOCATION OF OUR CONSOLE KEYBOARD. &
	!  L	KB.IN%		WE ARE NOW INPUTTING FROM OUR KEYBOARD. &
	!  L	KB.OUT%		THE LOG IS OUR KEYBOARD (NO LOG FILE). &
	!	LAST.CHR$	SAVED CHAR. FROM PK USED IN ERROR CHECKING. &
	!	LF$		CONSTANT <LF>. &
	!	LF.CR.0$	CONSTANT <LF>-<CR>-<NULL>. &
	!  L	LOG.OFF%	LOG REPORTING IS TURNED OFF NOW. &
	!	LOG.KB.OFF%	LOG REPORTING TO KB IS TURNED OFF NOW. &
	!  L	LOG.SW%		THE /LOG SWITCH WAS PRESENT. &
	!	LOGFILE$	FILESPEC OF LOG FILE. &
	!	M%(30)		USED TO HOLD SYS CALL DATA. &
	!	NL$		THE NULL STRING. &
	!  L	NO.REPROMPT%	DO NOT GO BACK TO "*" PROMPT ON ERROR/EXIT. &
	!	PK.JOB.NO%	JOB NUMBER OF JOB AT PK. &
	!	PK.KB%		KB NUMBER OF THE PK WE'RE USING. &
	!	PK.KB.DDB%	DDB OF THE KB ASSOCIATED WITH THE PK. &
	!	PK.STATUS%	LAST STATUS OF THE JOB AT THE PK: &
	!			  -2	NO JOB OR LOGGED OUT JOB. &
	!			  -1	JOB AT THE PK IS US. &
	!			   0	JOB IN MONITOR ^C STATE. &
	!			   3	JOB IN RUN STATE. &
	!			   5	LOGGED OUT JOB WE ARE TIMING OUT. &
	!			  28	JOB IN PROGRAM KB WAIT. &
	! &
	!	PK.TEXT$	DIALOGUE TEXT WE'RE PROCESSING FROM PK. &
	!	PPN%		PPN OF THE ACCOUNT WE'RE RUNNING UNDER. &
	!	PRIORITY%	/PRIORITY VALUE (DEFAULT = USE CALLER'S) &
	!	PRIV.OFF$	SYS(PRIV.OFF$) TEMPORARILY DROPS TEMP PRIV'S. &
	!	PRIV.ON$	SYS(PRIV.ON$) REGAINS TEMPORARY PRIV'S. &
	!  	PRIV.REQ$	[OPTIONAL] PRIVILEGE REQUIRED TO RUN ATPK. &
	!	PUT.COUNT%	LENGTH OF STRING WE'RE PUTTING TO PK. &
	!			SET = 0 AFTER ECHO LINE PROCESSED. &
	!	RTS1%		1ST HALF CONTROLLED JOB'S RTS (RADIX-50). &
	!	RTS2%		2ND HALF CONTROLLED JOB'S RTS (RADIX-50). &
	!	RUNBURST%	/RUNBURST VALUE (DEFAULT = USE CALLER'S) &
	!  L	STAMP.SW%	TIME STAMP SWITCH (/TIME) WAS PRESENT &
	!	START$		USER'S STARTUP COMMAND (OR PART OF IT). &
	!	SW$		STARTUP SWITCH WE ARE CURRENTLY PROCESSING. &
	!	SW.AND.ARG$	SWITCH AND ARGUEMENT WE ARE PROCESSING. &
	!	SWITCHES$	STARTUP SWITCHES WE HAVE YET TO PROCESS. &
	!	T0%		TEMPORARY VARIABLE. &
	!	T0$		TEMPORARY VARIABLE. &
	!	T1%		TEMPORARY VARIABLE. &
	!	T1$		TEMPROARY VARIABLE. &
	!	T2%		TEMPORARY VARIABLE. &
	!	T2$		TEMPORARY VARIABLE. &
	!	T3%		TEMPORARY VARIABLE. &
	!	TTINTF%		TERMINAL WE STARTED FROM INTERFACE TYPE. &
	!  L	WAIT.COMM%	WE ARE PROCESSING A $WAIT COMMAND. &
	!  L	WARNING.CHECK%	CHECK FOR WARNING ERRORS AT PK. &
	! &
	!  L IN THIS COLUMN INDICATES A LOGICAL VARIABLE. &
	!	THESE VARIABLES ARE ALWAYS -1 (TRUE) OR 0 (FALSE). &
	!	THE DEFINITION GIVEN IS THE MEANING WHEN THE VARIABLE IS TRUE. &
	! &

800	! &
	! &
	! &
	!	F U N C T I O N S    A N D    S U B R O U T I N E S &
	! &
	! &
	! &
	! &
	!	PROGRAM DEFINED SUBROUTINES &
	! &
	!	TITLE/LINE RANGE	DESCRIPTION &
	! &
	!	10000-10099		PUTS A LINE TO THE PK AND RECORDS &
	!	PK HANDLING ROUTINE	PK OUTPUT UNTIL THE JOB AT THE PK &
	!				ENTERS AN INPUT WAIT OR GOES AWAY. &
	! &
	!	10100-10199		SETS PK.STATUS% AS FOLLOWS: &
	!	PK STATUS CHECK		  -2	NO JOB OR LOGGED OUT JOB. &
	!				  -1	JOB AT THE PK IS US. &
	!				   0	JOB IN MONITOR ^C STATE. &
	!				   3	JOB RUNNING. &
	!				   5	JOB BEING TIMED OUT. &
	!				  28	JOB IN PROGRAM KB WAIT. &
	! &
	!	10200-10299		GETS AND LOGS TEXT FROM PK UNTIL &
	!	PROCESS TEXT FROM PK	NO MORE IMMEDIATELY PENDING. &
	! &
	!	10300-10399		PARSES TEXT FROM PK TO DETERMINE &
	!	CHECK PK OUTPUT		IF AN UNALLOWABLE ERROR HAS OCC- &
	!	FOR ERRORS		URRED AT THE PK JOB. ABORTS PRO- &
	!				GRAM IF ONE HAS. &
	! &
	!	11000-11030		SETS THE PARITY BIT ON ANY CHAR &
	!	PARSE FOR SPECIAL	FOLLOWING THE QUOTE CHARACTER. &
	!	CHARACTERS		DISCARDS LINE TERMINATOR FOR ANY &
	!				LINE CONTAINING ONLY ^<CHARACTER>. &
	!				CONVERTS UPARROW-CHARACTER COMB- &
	!				INATIONS TO CONTROL CHARACTERS. &
	! &
	!	11200-11299		SEES IF WE ARE INPUTTING FROM OUR &
	!	SET UP FOR KB: INPUT	CONSOLE KEYBOARD.  IF SO, FLAGS &
	!				IT AND SETS LOCAL ECHO AT PK'S KB:. &
	!				IF NOT, SETS FULL DUPLEX AT PK. &
	! &
	!	12000-12999		INSERTS DEFAULT EXTENSION EXT$ &
	!	CHECK FILESPEC		INTO FILESPEC$ IF NO EXTENSION &
	!	EXTENSION		EXISTS ALREADY IN FILESPEC$. &
	! &
	! &
	!	CONTROL FILE COMMAND SUBROUTINES &
	! &
	!	2200-2299		ENABLES LOG REPORTING. &
	!	$ENABLE LOGGING &
	! &
	!	2300-2399		DISABLES LOG REPORTING. &
	!	$DISABLE LOGGING &
	! &
	!	2400-2499		ENABLES CHECK FOR WARNING AND &
	!	$ALLOW NO ERRORS	FATAL ERRORS. &
	! &
	!	2500-2599		ENABLES CHECK ONLY FOR FATAL &
	!	$ALLOW WARNING ERRORS	ERRORS. &
	! &
	!	2600-2699		DISABLES ALL ERROR CHECKING. &
	!	$ALLOW FATAL ERRORS &
	! &
	!	2700-2799		WAITS FOR USER INPUT. &
	!	$WAIT &
	! &
	!	2800-2899		PROCESS INDIRECT COMMAND FILE. &
	!	$@ &
	! &
	!	2900-2999		LOG IN A NEW JOB. &
	!	$LOGIN &
	! &
	!	3000-3099		DETACH OURSELVES. &
	!	$DETACH &
	! &
	! &
	!	STARTUP SWITCH SUBROUTINES &
	! &
	!	4000-4009		PRINT SWITCH ERROR MESSAGE. &
	!	INVALID SWITCH ROUTINE &
	! &
	!	4010-4019		GET DEFAULT RTS FOR NEW JOB. &
	!	/RTS: SWITCH &
	! &
	!	4020-4029		FLAG TO DETACH. &
	!	/DET SWITCH &
	! &
	!	4030-4039		FLAG TO DELETE CONTROL FILE &
	!	/DEL SWITCH		ON SUCCESSFUL COMPLETION. &
	! &
	!	4040-4049		RUN THE JOB REGARDLESS OF WHETHER &
	!	/LOG SWITCH		LOGINS HAVE BEEN DISABLED. &
	! &
	!	4050-4059		CHAIN TO PROGRAM AT LINE NUMBER &
	!	/CHA: SWITCH		WITH CCM LOADED ON GOOD EXIT. &
	! &
	!	4070			FLAG TO TIME STAMP START/STOP &
	!	/TIM SWITCH		OF PROCESSING. &
	! &
	! &
	!	PROGRAM DEFINED FUNCTIONS &
	! &
	!	TITLE/LINE RANGE	DESCRIPTION &
	! &
	!	FNE$(T0%)		RETURNS SYSTEM ERROR MESSAGE &
	!	15000-15000		FOR ERROR NUMBER T0%. &
	! &
	!	FNTO.LOG%(T0$,T0%)	LOGS T0$. WILL NOT PRINT T0$ &
	!	16000-16000		AT KEYBOARD IF T0%<>0%. DOES &
	!				NOTHING IF LOG REPORTING IS &
	!				DISABLED.  RETURNS NOTHING. &
	! &
	!	FNPRIV%(PRIV$)		RETURNS -1 IF PRIVILEGE 'PRIV$' &
	!	16100-16100		ENABLED, OTHERWISE RETURNS 0. &
	! &
	!	FNDEL.TMP%(T0%)		DELETE ALL XXXXnn.TMP FILES &
	!	17000-17020		WHERE nn = T0%. &
	! &

900	! &
	! &
	! &
	!	D I M E N S I O N    S T A T E M E N T S &
	! &
	! &
	! &
	DIM M%(30%), &
		! ARRAY USED TO HOLD SYS CALL INFORMATION. &
	    COMMAND$(11%) &
		! ARRAY TO STORE VALID CONTROL FILE COMMANDS. &
	&

999	! &
	! &
	! &
	!	S T A R T    O F    P R O G R A M &
	! &
	! &
	! &

1000	ON ERROR GOTO 19000 &
		! SET UP STANDARD ERROR TRAP. &
	\ DUMMY$=SYS(CHR$(6%)+CHR$(-21%)+CHR$(255%)) &
		! DROP TEMPORARY PRIVILEGES. &

1010	IDENT.STG$="V10.1-A" &
		! SET UP VERSION/EDIT FOR HEADER. &

1030	PRINT IF CCPOS(0%) &
	\ PRINT "ATPK";CHR$(9%);IDENT.STG$;CHR$(9%);FNE$(0%) &
	    UNLESS ENTRY.TYP% &
		! PRINT HEADER IDENTIFYING PROGRAM. &
	&

1060	! &
	! &
	! &
	!	D E F I N E    P A R A M E T E R S &
	! &
	! &
	! &
	CHR.6$=CHR$(6%) &
		! FOR FIP SYS CALLS. &
	\ PRIV.ON$=CHR.6$+CHR$(-21%)+CHR$(0%) &
		! SYS(PRIV.ON$) WILL REGAIN TEMPORARY PRIVILEGES. &
	\ PRIV.OFF$=CHR.6$+CHR$(-21%)+CHR$(255%) &
		! SYS(PRIV.OFF$) WILL TEMPORARILY DROP TEMPORARY PRIVILEGES. &
	\ KB.CH%,CHAIN.FLG%,DELETE.CTL%,DET.SW%,LOG.SW%,STAMP.SW%=0% &
	\ PRIORITY%,RUNBURST%=0% &
		! INITIALIZE SOME VARIABLES. &
	\ NL$="" &
		! DEFINE THE NULL STRING. &
	\ LF$=CHR$(10%) &
		! DEFINE <LINE FEED> CONSTANT. &
	\ CR.LF$=CHR$(13%)+LF$ &
		! DEFINE <CR>-<LF>. &
	\ LF.CR.0$=LF$+CHR$(13%)+CHR$(0%) &
		! DEFINE <LF>-<CR>-<0>. &
	\ CR.LF.FF$=CR.LF$+CHR$(12%) &
		! DEFINE <CR>-<LF>-<FF> FOR LINE TERMINATOR SEARCHES. &
	&
	!\ PRIV.REQ$="SWCFG" &
		! DEFINE PRIVILEGE REQUIRED TO RUN ATPK. &
		! (REMOVE COMMENT PREFIX TO REQUIRE SWCFG PRIVILEGE.) &
	&
	\ CHANGE SYS(CHR.6$+CHR$(26%)) TO M% &
		! RETURN JOB STATUS. &
	\ JOB.NO%=M%(3%)/2% &
		! OUR JOB NUMBER. &
	\ PPN%=M%(21%)+SWAP%(M%(22%)) &
		! PPN UNDER WHICH WE ARE RUNNING. &
	\ RTS1%=M%(23%)+SWAP%(M%(24%)) &
	\ RTS2%=M%(25%)+SWAP%(M%(26%)) &
		! NAME OF CURRENT JOB DEFAULT RUN-TIME SYSTEM. &
		! THIS WILL NORMALLY BE THE NEW JOB'S RTS. &
		! RTS1% AND RTS2% MAY BE CHANGED BY THE /RTS: SWITCH. &
	\ CHANGE SYS(CHR.6$+CHR$(-3%)) TO M% &
		! GET MONITOR TABLES PART I. &
	\ DEVPTR%=M%(7%)+SWAP%(M%(8%)) &
		! DEVPTR TABLE ADDRESS. &
	\ JOBTBL%=M%(11%)+SWAP%(M%(12%)) &
		! JOBTBL ADDRESS. &
	\ CHANGE SYS(CHR.6$+CHR$(-12%)) TO M% &
		! GET MONITOR TABLES PART II. &
	\ DEVOKB%=M%(9%)+SWAP%(M%(10%)) &
		! GET OFFSET IN DEVPRT TABLE FOR KB0: DDB. &
	\ DUMMY$=SYS(PRIV.ON$) &
		! REGAIN TEMPORARY PRIVILEGES. &
	\ DEVTBL%=PEEK(DEVPTR%+DEVOKB%) &
		! DEVICE TABLE ADDRESS. &
	\ IOB%=PEEK(PEEK(520%)) &
		! OUR IO BLOCK LOCATION. &
	\ KB.DDB%=PEEK(IOB%) &
		! OUR CONSOLE KB: DDB LOCATION. &
	\ KB%=SWAP%(PEEK(KB.DDB%+2%)) AND 255% &
		! OUR CONSOLE KB: NUMBER. &
	\ TTINTF%=PEEK(KB.DDB%+30%) &
		! OUR CONSOLE KEYBOARD'S INTERFACE TYPE. &
	\ DUMMY$=SYS(PRIV.OFF$) &
		! DROP TEMPORARY PRIVILEGES. &
	\ KB$="_KB"+NUM1$(KB%)+":ATPK.CMD" &
		! OUR CONSOLE KEYBOARD DEVICE, EXPLICITLY. &
	\ IN.CH%=2% &
		! CHANNEL ON WHICH TO INPUT CONTROL LINES &
		! IF NOT INPUTTING FROM OUR CONSOLE KEYBOARD. &
		! MAY BE CHANGED LATER. &
	\ RESTORE &
		! TO READ COMMANDS FROM DATA. &
	\ READ COMMAND$(T0%) FOR T0%=1% TO 11% &
		! READ IN THE FOLLOWING VALID COMMANDS: &
	\ DATA	$ENABLELOGGING, $ENABLEKBLOGGING, $DISABLELOGGING, &
		$DISABLEKBLOGGING, $ALLOWNOERRORS, &
		$ALLOWWARNINGERRORS, $ALLOWFATALERRORS, $WAIT, $@, $LOGIN, &
		$DETACH &

1070	IF LEN(PRIV.REQ$) THEN &
		IF NOT FNPRIV%(PRIV.REQ$) THEN &
			PRINT CR.LF$;"?";PRIV.REQ$;" privilege required" &
	\		GOTO 32767 &
		! IF PRIVILEGE REQUIRED TO USE ATPK, &
		!    IF USER DOESN'T HAVE THAT PRIV, &
		!      DISPLAY ERROR TEXT AND ABORT &

1090	IF (TTINTF% AND 63%)=8% THEN &
		IF NOT FNPRIV%("DEVICE") AND NOT PRELIN.FLG% THEN &
			ERR.MSG$="?ATPK can't run at a PK" &
	\		NO.REPROMPT%=-1% &
	\		GOTO 19200 &
		! IF WE'RE BEING RUN ON A PK &
		!   USER MUST HAVE DEVICE PRIVILEGE &
		!	OR THE "ALLOW-PK" FLAG IS SET (BY PRELIN) &

1100	! &
	! &
	! &
	!	G E T    S T A R T U P    C O M M A N D &
	! &
	! &
	! &
	IF ENTRY.TYP%=0% THEN &
		OPEN KB$ AS FILE 4% &
	\	PRINT IF CCPOS(0%) &
	\	PRINT "*"; &
	\	INPUT LINE #4%, START$ &
	\	CLOSE 4% &
	\	START$=CVT$$(START$,256%+32%+4%) &
	\	GOTO 1100 UNLESS LEN(START$) &
		! RUN ENTRY, SO GET USER'S STARTUP COMMAND LINE. &
	&

1500	! &
	! &
	! &
	!	P R O C E S S    S T A R T U P    C O M M A N D &
	! &
	!		    O P E N    F I L E S &
	! &
	! &
	! &
	T0%=INSTR(1%,START$,"=") &
	\ T1%=INSTR(1%,START$,"/CHA") &
	\ T0%=0% IF T1% AND T1%<T0% &
	\ LOGFILE$="" &
	\ LOGFILE$=LEFT(START$,T0%-1%) IF T0% &
	\ START$=RIGHT(START$,T0%+1%) IF T0% &
		! BREAK LOG FILESPEC OFF REST OF COMMAND. &
		! THE FIRST "=" WE SEE COULD BE SEPARATING THE &
		! LOG FILE FROM THE REST OF THE COMMAND, OR IT &
		! COULD BE PART OF THE /CHA SWITCH. &
		! &
	\ T0% = INSTR(1%,START$,"/") &
	\ T0% = LEN(START$)+1% UNLESS T0% &
	\ SWITCHES$=RIGHT(START$,T0%) &
	\ FILESPEC$=LEFT(START$,T0%-1%) &

1510	GOTO 1520 UNLESS LEN(SWITCHES$) &
		! CONTINUE IF NO MORE SWITCHES TO PARSE &
	\ T0%=INSTR(2%,SWITCHES$,"/") &
	\ T0%=LEN(SWITCHES$)+1% UNLESS T0% &
	\ SW$,SW.AND.ARG$=LEFT(SWITCHES$,T0%-1%) &
	\ SWITCHES$=RIGHT(SWITCHES$,T0%) &
		! STRIP NEXT SWITCH OFF SWITCH FIELD. &
		! SWITCHES$ ALWAYS STARTS WITH "/" UNLESS NULL. &
	\ T0%=INSTR(2%,SW$,":") &
	\ ARG$=NL$ &
	\ ARG$=RIGHT(SW$,T0%+1%) IF T0% &
	\ SW$=LEFT(SW$,T0%-1%) IF T0% &
		! STRIP ARGUMENT OF SWITCH IF PRESENT. &
	\ T0%=INSTR(1%,"////RTS/DET/DEL/LOG/CHA/TIM/PRI/RUN",LEFT(SW$,4%))/4% &
	\ T0%=0% IF LEN(SW$)<4% &
		! DETERMINE SWITCH TYPE (ERROR IF < 3 CHARS) &
	\ IF T0% = 0% THEN &
		FILESPEC$=FILESPEC$+SW.AND.ARG$ &
	  ELSE	ON T0%+1% GOSUB	4000,		! INVALID SWITCH &
				4010,		! /RTS SWITCH &
				4020,		! /DET SWITCH &
				4030,		! /DEL SWITCH &
				4040,		! /LOG SWITCH &
				4050,		! /CHAIN SWITCH &
				4070,		! /TIME SWITCH &
				4080,		! /PRIORITY SWITCH &
				4090		! /RUNBURST SWITCH &
		! IF UNRECOGNIZED, ASSUME FSS UNDERSTANDS IT &
		! OTHERWISE GO PROCESS BASED ON TYPE &

1515	GOTO 1510 &
		! LOOP BACK IF MORE SWITCHES TO PROCESS. &

1520	EXT$="CMD" &
	\ GOSUB 12000 &
		! FSS THE FILE-SPEC &
	\ OPEN FILESPEC$ FOR INPUT AS FILE 2% &
		! OPEN CONTROL FILE. &
	\ START$=FILESPEC$ &
		! REMEMBER THE CONTROL FILE NAME. &
	\ DUMMY$=SYS(PRIV.ON$) &
		! REGAIN TEMPORARY PRIVILEGES. &
	\ KB.IN%=PEEK(IOB%+4%)=KB.DDB% &
		! FLAG IF WE JUST OPENED OUR KB FOR INPUT. &
		! COMMAND LINE INPUT FROM KEYBOARD WILL BE IMMEDIATE. &
	\ DUMMY$=SYS(PRIV.OFF$) &
		! DROP TEMPORARY PRIVILEGES. &
	\ LOGFILE$=START$ IF KB.IN% AND LEN(LOGFILE$)=0% &
		! IF NO LOG SPECIFIED AND KB OPENED, USE STRING OPENED &
		! (TO INSURE SAME MODE). &
	\ LOGFILE$=KB$ UNLESS LEN(LOGFILE$) &
		! IF NO LOG SPECIFIED AND INPUT NOT FROM KB. &
	\ FILESPEC$=LOGFILE$ &
	\ EXT$="LOG" &
	\ GOSUB 12000 &
	\ OPEN FILESPEC$ FOR OUTPUT AS FILE 3% &
		! OPEN LOG FILE. &
	\ NO.REPROMPT%=-1% &
		! IF A FATAL ERROR OCCURRS PAST THIS POINT, &
		! WE WILL NOT GO BACK TO THE "*" PROMPT. &
	\ T0%=0% &
		! WE WILL TRY TO OPEN PK0: FIRST IN NEXT LINE. &

1530	! &
	! &
	! &
	!	O P E N    P K    A N D    S T A R T    U P    J O B &
	! &
	! &
	! &
	DUMMY$=SYS(PRIV.ON$) &
		! REGAIN TEMPORARY PRIVILEGES. &
	\ OPEN "_PK:" AS FILE 1%, RECORDSIZE 134%, MODE 16% &
		! OPEN (OR TRY TO OPEN) THE PK TO BE USED. &
	\ DUMMY$=SYS(CHR.6$+CHR$(-7%)) &
		! ENABLE CTRL/C TRAP SO WE CAN CLOSE UP SHOP ON CTRL/C. &
	\ FIELD #1%, 2% AS F2$ &
	\ FIELD #1%, 3% AS F3$ &
	\ FIELD #1%, 4% AS F4$ &
		! TO BE USED IN LOOKING FOR ECHOED DELIMITERS. &
	\ CHANGE SYS(CHR.6$+CHR$(-8%)+CHR$(1%)) TO M% &
		! GET DDB OF PK OPENED ON CHANNEL 1. &
	\ PK.KB%=M%(11%)/2% &
		! # OF KB ASSOCIATED WITH PK. &
	\ PK.KB.DDB%=PEEK(DEVTBL%+M%(11%)) &
		! DDB OF KB ASSOCIATED WITH PK. &
	\ KB.CH%=2% IF KB.IN% &
		! USE KEYBOARD ON CH 2 IF THAT'S WHAT WE ARE INPUTTING FROM. &
	\ KB.OUT%=(PEEK(IOB%+6%)=KB.DDB%) &
		! FLAG IF WE JUST OPENED OUR KB FOR OUTPUT. &
		! IF WE DIDN'T, WE ALSO REPORT DIALOGUE TO OUR KB. &
	\ KB.CH%=3% IF KB.OUT% AND NOT KB.IN% &
		! IF KB: OPENED FOR LOG BUT NOT FOR INPUT, &
		! DO THE OUTPUT THERE. &
	\ DETACHED%=(CVT$%(SYS(CHR.6$+CHR$(9%))) AND 1%)<>0% &
		! SEE IF WE'RE DETACHED. &
	\ T1%=133% &
		! PK WIDTH (+1) UNLESS WE ARE PRINTING TO TERMINAL. &
	\ T1%=ASCII(MID(SYS(CHR$(6%)+CHR$(-8%)+CHR$(0%)),20%,1%)) &
	      IF KB.OUT% OR NOT (DETACHED% OR DET.SW%) &
		! IF PRINTING TO TERMINAL, PK WIDTH WILL BE TERMINAL WIDTH. &
		! &
	\ DUMMY$=SYS(CHR.6$+CHR$(16%)+CHR$(0%)+CHR$(PK.KB%)+CHR$(T1%) &
	     +CHR$(128%)+CHR$(255%)+CHR$(128%)+CHR$(0%)+CHR$(0%) &
	     +CHR$(0%)+CHR$(255%)+CVT%$(0%)+CVT%$(0%)+CHR$(0%)+CHR$(128%) &
	     +CHR$(0%)+CVT%$(0%)+CHR$(128%)+CHR$(0%)+CHR$(255%)+CVT%$(-1%)) &
		! &
		! FOR KB ASSOCIATED WITH PK, SET WIDTH AS DETERMINED ABOVE, &
		! TAB, FORM, LC OUTPUT, LC INPUT, UPARROW, NO ESC SEQ, &
		! ESC, CTRL/R & CTRL/T, RESUME ANY. &
		! &
	\ DUMMY$=SYS(PRIV.OFF$) &
	\ GOSUB 11200 &
		! &
		! GO SET FULL DUPLEX OR LOCAL ECHO DEPENDING ON WHETHER WE &
		! ARE INPUTTING FROM OUR CONSOLE KEYBOARD. &
		! &
	\ T0$=NL$ &
	\ T0%=FNTO.LOG%(CR.LF$+"!*** Processing started on "+ &
		DATE$(0%)+" at "+TIME$(0%)+" ***"+CR.LF$,0%) &
			IF STAMP.SW% &
		! DATE/TIME STAMP WHEN WE STARTED, IF /TIM WAS SPECIFIED. &
	\ GOSUB 2900 &
		! SPAWN LOGIN TO ATTACH TO THE PK. &

2000	! &
	! &
	! &
	!	M A I N    R O U T I N E &
	! &
	! &
	! &
	IF KB.IN%=0% THEN &
	      INPUT LINE #IN.CH%, CTRL.LINE$ &
	ELSE  WAIT 3% &
	\     GET #IN.CH% &
	\     FIELD #IN.CH%, RECOUNT AS T0$ &
	\     CTRL.LINE$=T0$+NL$ &
	\     FIELD #IN.CH%, RECOUNT-1% AS T1$, 1% AS T2$ &
	\     LAST.CHR$=T2$+NL$ &
	\     ECHO%=ASCII(T2$)+1000% &
	\     T0$=T1$+"$" IF ECHO%=1027% &
	\     T0%=FNTO.LOG%(CVT$$(FNQUOTE$(T0$),1%),-1%) &
		! GET LINE TO BE PUT TO PK. &
		! IF WE ARE INPUTTING FROM OUR KB:, LOG IT &
		!	AS IT WILL NOT BE ECHOED THROUGH THE PK. &
		!	IF "<ESC>" WAS TYPED, LOG A DOLLAR SIGN. &
		! WE WILL EXPECT THAT THE LINE DELIMITER WILL BE ECHOED. &
		! LOG THE NEW LINE, IF APPROPRIATE. &

2010	CTRL.LINE$=FNQUOTE$(CTRL.LINE$) &
		! PROCESS QUOTE CHARACTERS. &
	\ IF ASCII(CTRL.LINE$)=33% THEN &
	      GOSUB 10200 &
	\     T0%=FNTO.LOG%(CVT$$(CTRL.LINE$,1%),0%) UNLESS KB.IN% &
	\     GOTO 2000 &
		! IF CTRL LINE WAS A COMMENT, MAKE SURE ALL DIALOGUE &
		! IS LOGGED, AND THEN LOG THE COMMENT. &
		! GO BACK FOR ANOTHER CTRL LINE. &

2020	CTRL.LINE$=FNCTRL$(CTRL.LINE$) &
		! PROCESS CONTROL CHARACTERS. &
	\ IF ASCII(CTRL.LINE$)=36% THEN &
		! LOOKS LIKE A COMMAND. &
	      T0$=CVT$$(CTRL.LINE$,255%) &
	\     T0%=0% &
	\     T0%=T1% &
		  IF LEFT(COMMAND$(T1%),LEN(T0$))=LEFT(T0$,LEN(COMMAND$(T1%))) &
		  FOR T1%=1% TO 11% &
	\     IF T0% AND LEN(T0$)>2% THEN &
		  ON T0% GOSUB	2200, 2250, 2300, 2350, 2400, 2500, 2600, &
				2700, 2800, 2900, 3000 &
			! 2200 - $LOG ON COMMAND. &
			! 2250 - $LOG KB ON COMMAND. &
			! 2300 - $LOG OFF COMMAND. &
			! 2350 - $LOG KB OFF COMMAND. &
			! 2400 - $ALLOW NO ERRORS COMMAND. &
			! 2500 - $ALLOW WARNING ERRORS COMMAND. &
			! 2600 - $ALLOW FATAL ERRORS COMMAND. &
			! 2700 - $WAIT COMMAND. &
			! 2800 - $@ COMMAND. &
			! 2900 - $LOGIN COMMAND. &
			! 3000 - $DETACH COMMAND. &
	\	  GOTO 2000 &
			! IF IT WAS A RECOGNIZED COMMAND, EXECUTE IT &
			! AND GO BACK FOR ANOTHER CONTROL LINE. &

2030	GOSUB 10000 &
		! SEND LINE TO PK AND WAIT FOR NEXT INPUT STALL. &
	\ IF PK.STATUS%=0% AND WAIT.COMM% THEN &
	      CLOSE 4% &
	\     WAIT.COMM%=0% &
	\     IN.CH%=2% &
	\     IN.CH%=5% IF IN.AT% &
	\     GOSUB 11200 &
		! IF WE'RE THROUGH WITH THE $WAIT COMMAND THEN &
		! FLAG SO AND RETURN TO WHERE WE WERE COMMING FROM. &

2040	IF PK.STATUS%>=0% THEN &
		PK.RETRY%=0% &
	\	GOTO 2000 &

2050	IF PK.STATUS%=-1% THEN &
		ERR.MSG$="?Job attached to ATPK" &
	\	GOTO 19200 &
		! IF PK STATUS IS LEGITIMATE, GET NEXT CONTROL LINE. &
		! IF USER JOB IS ATTACHED TO ATPK, ABORT &

2060	SLEEP 1% &
	\ GOTO 2080 IF PK.RETRY%=-1% &
	\ PK.RETRY%=PK.RETRY%+1% &
	\ IF PK.RETRY%<5% THEN GOSUB 10100 &
	\	GOTO 2040 &
		! WAIT A SEC, TALLY A RETRY, IF UNDER THE LIMIT, THEN &
		! GO GET STATUS OF PK AND TRY AGAIN (IN CASE A JOB GOT &
		! DETACHED OR SOMETHING BUT COMES BACK AGAIN). &

2070	PK.RETRY%=-1% &
	\ GOTO 2000 &
		! FLAG THAT THE PK IS LOGGED OUT, AND TRY FOR ONE MORE &
		! COMMAND IN CASE IT IS A $LOGIN. &

2080	ERR.MSG$="?Job logged out" &
	\ GOTO 19200 &
		! IF USER JOB HAS LOGGED OUT AT THE PK, ABORT. &

2100	SLEEP 3% OR NOT 32767% &
		! WAIT FOR RESPONSE FROM KB OR FROM PK. &
	\ SLEEP 30% OR NOT 32767% UNLESS PK.STATUS%=5% &
		! SLEEP LONGER UNLESS JOB IS LOGGED OUT. &
	\ GOSUB 10010 &
		! GET ANY OUTPUT THAT MIGHT BE PENDING FROM PK. &
	\ GOTO 2040 &
		! GO TRY ANOTHER GET FROM THE KB WITH SHORT WAIT. &
		! KEYBOARD WAIT EXHAUSTED ERROR COMES HERE. &
	&

2200	! &
	! &
	! &
	!	C O N T R O L    F I L E    C O M M A N D    R O U T I N E S &
	! &
	! &
	! &
	! &
	!	$ENABLE LOGGING &
	! &
	LOG.OFF%=0% &
	\ LOG.KB.OFF%=0% &
	\ RETURN &
		! ENABLE LOG REPORTING. &

2250	! &
	!	$ENABLE KB LOGGING &
	! &
	LOG.KB.OFF%=0% &
	\ RETURN &
		! ENABLE KB LOGGING ONLY. &

2300	! &
	!	$DISABLE LOGGING &
	! &
	LOG.OFF%=-1% &
	\ LOG.KB.OFF%=-1% &
	\ RETURN &
		! DISABLE LOG REPORTING. &

2350	! &
	!	$DISABLE KB LOGGING &
	! &
	LOG.KB.OFF%=-1% &
	\ RETURN &
		! DISABLE KB LOGGING ONLY. &

2400	! &
	!	$ALLOW NO ERRORS COMMAND &
	! &
	ERROR.CHECK%,WARNING.CHECK%=-1% &
	\ RETURN &
		! CHECK FOR BOTH FATAL AND WARNING ERRORS. &

2500	! &
	!	$ALLOW WARNING ERRORS COMMAND &
	! &
	ERROR.CHECK%=-1% &
	\ WARNING.CHECK%=0% &
	\ RETURN &
		! CHECK FOR FATAL ERRORS ONLY. &

2600	! &
	!	$ALLOW FATAL ERRORS COMMAND &
	! &
	ERROR.CHECK%,WARNING.CHECK%=0% &
	\ RETURN &
		! DO NO ERROR CHECKING. &

2700	! &
	!	$WAIT COMMAND &
	! &
	OPEN KB$ AS FILE 4% &
	\ IN.CH%=4% &
	\ WAIT.COMM%=-1% &
	\ GOSUB 11200 &
	\ RETURN UNLESS DETACHED% AND (NOT KB.OUT%) &
	\ PRINT #4%, IF CCPOS(4%) &
	\ PRINT #4%, "Input Required>"; &
	\ RETURN &
		! REMEMBER WHERE WE WERE INPUTTING FROM. &
		! FLAG THAT WE'RE NOW INPUTTING FROM KB UNTIL MONITOR WAIT. &
		! USE CHANNEL 0 FOR INPUT UNLESS KB ALREADY OPEN ELSEWHERE. &
		! PROMPT FOR INPUT IF WE ARE NOT LOGGING TO KB:. &

2800	! &
	!	$@ COMMAND &
	! &
	IF IN.AT% THEN &
		! WE ARE ALREADY IN AN INDIRECT.  THIS IS ILLEGAL. &
	      ERR.MSG$="?Nesting of indirect command files not allowed" &
	\     GOTO 19200 &
		! GO REPORT THE ERROR. &

2810	FILESPEC$=RIGHT(T0$,INSTR(1%,T0$,"@")+1%) &
	\ EXT$="CMD" &
	\ GOSUB 12000 &
	\ OPEN FILESPEC$ FOR INPUT AS FILE 5% &
		! GET FILESPEC FOR INDIRECT COMMAND FILE AND OPEN IT. &
	\ IN.AT%=-1% &
	\ IN.CH%=5% &
		! FLAG TO INPUT CONTROL LINES FROM CHANNEL 5. &
	\ GOSUB 11200 &
		! GO SET UP FOR INPUT FROM CONSOLE KB: IF NEEDED. &
	\ RETURN &

2900	! &
	!	$LOGIN COMMAND &
	! &
	T0%=INSTR(1%,T0$,"(") &
	\ T1%=INSTR(1%,T0$,"KB") &
	\ T0%=T1% IF T1% AND (T1%<T0% OR T0%=0%) &
	\ T1%=PPN% &
	\ T3%=PK.KB% &
	!\ T0%=0% &
		! OPTIONAL PATCH.  BY SETTING T0%=0%, THE $LOGIN &
		! COMMAND WILL IGNORE ANY ARGUMENTS. &
	\ IF T0% THEN &
		CHANGE SYS(CHR.6$+CHR$(-23%)+RIGHT(T0$,T0%)) TO M% &
	\	T1%=M%(5%)+SWAP%(M%(6%)) IF M%(29%) AND 128% &
	\	IF M%(23%)=75% AND M%(24%)=66% THEN &
			IF M%(26%)=255% THEN &
				T3%=M%(25%) &
			ELSE	T3%=KB% &
		! IF A PPN WAS SPECIFIED FOR LOGIN, USE IT. &
		! IF KBn: WAS SPECIFIED, USE KEYBOARD n. &
		! IF KB: WAS SPECIFIED, THEN USE KEYBOARD WE STARTED FROM. &

2910	PK.FLG% = (T3%=PK.KB%) &
	\ IF PK.FLG% THEN &
		PUT #1%, RECORD 21% &
	\	FOR RETRY% = 1% TO 10% &
	\		GOSUB 10100 &
	\		GOTO 2920 IF PK.STATUS% = -2% &
	\		SLEEP 1% &
	\	NEXT RETRY% &
		! IF WE ARE CREATING NEW JOB ON PK, &
		!	KILL JOB THERE &
		!	WAIT FOR PK JOB TO GO AWAY &

2920	IF PK.FLG% THEN &
		IF PRELIN.FLG% THEN &
			Z$ = SYS(PRIV.ON$) &
	\		Z$ = SYS(CHR.6$+CHR$(10%)+STRING$(20%,0%)+ &
				"KB"+CHR$(PK.KB%)+CHR$(-1%)) &
	\		ASNPK.FLG% = -1% &
	\		Z$ = SYS(PRIV.OFF$) &
		! IF CREATING NEW JOB AT PK, &
		!	IF ENTERED BY PRELIN, &
		!		GAIN TEMP PRIVS &
		!		ASSIGN PK'S KB TO OUR JOB &
		!		(ALLOWS SPAWN WITHOUT PRIV TO RESTRICTED PK) &
		!		SET PK-ASSIGNED FLAG &
		!		DROP TEMP PRIVS &

2930	DUMMY$=SYS(	CHR.6$+CHR$(24%)+		!SPAWN sys call &
			CHR$(2%+			!mark as batch job &
			     16%+			!enter KBM &
			     64%+			!spawn logged-in &
			     ((T1%<>PPN%) AND 32%)+	!spawn under PPN &
			    (LOG.SW% AND 128%))+	!override nologins &
			CHR$(128%+T3%)+			!KB no. &
			CVT%$(0%)+			!reserved &
			CVT%$(SWAP%(RTS1%))+		!KBM name part 1 &
			CVT%$(SWAP%(RTS2%))+		!KBM name part 2 &
			CVT%$(0%)+			!reserved &
			CVT%$(SWAP%(T1%))+		!PPN &
			CHR$(PRIORITY% AND PK.FLG%)+	!priority &
			CHR$(RUNBURST% AND PK.FLG%))	!runburst &
	\ ASNPK.FLG% = 0% &
	\ RETURN UNLESS PK.FLG% &
		! SPAWN THE JOB &
		! ONLY CONTINUE IF WE MUST WAIT FOR LOGIN AT THE PK. &
		! FLAG START OF TIME TO WAIT FOR LOGIN. &
	\ T1%=0% &
	\ GOSUB 3000 IF DET.SW% AND NOT DETACHED% &
		! DETACH IF WE HAVEN'T ALREADY. &
		! THIS SHOULD ONLY HAPPEN IF WE ARE JUST STARTING UP. &

2940	GOSUB 10100 &
		! CHECK PK STATUS. &
	\ IF PK.STATUS%=0% OR PK.STATUS%=28% THEN GOSUB 10010 &
		! LOGIN HAS SUCCESSFULLY STARTED UP AND ATTACHED TO PK. &
		! NOW GO GET THE "READY" MESSAGE OR OTHER RTS PROMPT &
		! FROM LOGIN EXITING. &
	  ELSE	SLEEP 1% &
		! WAIT FOR JOB TO APPEAR ON THE PK &
	\	T1%=T1%+1% &
		! FLAG THAT WE'VE WAITED MORE FOR THE JOB &
	\	IF T1%<10% THEN &
			GOTO 2940 &
		  ! WAIT FOR 10 INTERVALS BEFORE WE GIVE UP. &
		ELSE	ERR.MSG$="?Job startup failed" &
	\	  	GOTO 19200 &
				! JOB HAS FAILED TO START UP &

2960	RETURN &
		! NEW JOB SPAWNED AND LOGGED IN. &

3000	! &
	!	$DETACH COMMAND &
	! &
	DETACHED%=(CVT$%(SYS(CHR.6$+CHR$(9%))) AND 1%)<>0% &
	\ RETURN IF DETACHED% &
	\ PRINT &
	\ PRINT "Detaching ..." &
	\ PRINT &
	\ DUMMY$=SYS(CHR.6$+CHR$(7%)) &
	\ DETACHED%=-1% &
	\ RETURN &
		! DETACH UNLESS WE ARE ALREADY DETACHED. &

4000	! &
	! &
	! &
	!	S T A R T U P    S W I T C H    R O U T I N E S &
	! &
	! &
	! &
	! &
	!	INVALID SWITCH &
	! &
	ERR.MSG$="?Invalid switch - "+SW.AND.ARG$ &
	\ T0%=0% &
	\ RETURN &
		! PRINT ERROR MESSAGE FOR INVALID SWITCH. &
		! MAKE SURE THE SWITCH IS FLAGGED AS INVALID. &

4010	! &
	!	/RTS SWITCH &
	! &
	CHANGE SYS(CHR.6$+CHR$(-10%)+ARG$) TO M% &
		! FILENAME STRING SCAN ON RTS NAME. &
	\ RTS1%=M%(7%)+SWAP%(M%(8%)) &
	\ RTS2%=M%(9%)+SWAP%(M%(10%)) &
		! SPECIFIED RTS NAME IN RADIX-50. &
	\ RETURN &
		! /RTS SWITCH PROCESSED. &

4020	! &
	!	/DET SWITCH &
	! &
	DET.SW%=-1% &
		! FLAG THAT THE /DET SWITCH WAS SPECIFIED. &
	\ RETURN &
		! /DET SWITCH PROCESSED. &

4030	! &
	!	/DEL SWITCH &
	! &
	DELETE.CTL%=-1% &
	\ RETURN &
		! /DEL SWITCH PROCESSED. &

4040	! &
	!	/LOG SWITCH &
	! &
	LOG.SW%=-1% &
		! SET FLAG TO RAISE LOGINS IF NECESSARY &
		! WHEN WE SPAWN LOGIN. &
	\ RETURN &
		! /LOG SWITCH PROCESSED. &

4050	! &
	!	/CHAIN SWITCH &
	! &
	T1%=INSTR(1%,ARG$+";",";") &
	\ T2%=INSTR(1%,ARG$,"=") &
	\ T1%=T2% IF T2% AND T2%<T1% &
	\ CHAIN.PROG$=LEFT(ARG$,T1%-1%) &
	\ DUMMY$=SYS(CHR.6$+CHR$(-10%)+CHAIN.PROG$) &
	\ T2%=INSTR(T1%,ARG$+"=","=") &
	\ CHAIN.LINE%=VAL(MID(ARG$,T1%+1%,T2%-T1%-1%)) &
	\ IF T2%<=LEN(ARG$) THEN &
		ARG$=ARG$+SWITCHES$ &
	\	SW.AND.ARG$=SW.AND.ARG$+SWITCHES$ &
	\	T1$=MID(ARG$,T2%+1%,1%) &
	\	T1%=INSTR(T2%+2%,ARG$,T1$) &
	\	DUMMY$=SYS(CHR$(8%)+MID(ARG$,T2%+2%,T1%-T2%-2%)) &
	\	SWITCHES$=RIGHT(ARG$,T1%+1%) &
	\	IF T1%=0% OR ASCII(SWITCHES$)<>47% AND LEN(SWITCHES$) THEN &
			GOTO 4000 &
		! DECODE THE SWITCH: &
		! /CHA[IN]: <prog> [;<line>] [=\<core common>\] &
		! THE "BACKSLASHES" AROUND <core common> MAY BE &
		! ANY CHARACTER NOT CONTAINED IN <core common>. &
		! LOAD CORE COMMON IF REQUESTED TO. &

4060	CHAIN.FLG%=-1% &
	\ RETURN &
		! /CHAIN SWITCH PROCESSED. &

4070	! &
	!	/TIME SWITCH (TIME STAMP) &
	! &
	STAMP.SW%=-1% &
		! FLAG THAT THE /TIME SWITCH WAS SPECIFIED. &
	\ RETURN &
		! /TIME SWITCH PROCESSED. &
	&

4080	! &
	!	/PRIORITY SWITCH &
	! &
	GOTO 4000 UNLESS FNPRIV%("TUNE") &
	\ PRIORITY% = VAL(ARG$) &
	\ GOTO 4000 IF PRIORITY% < -120% OR PRIORITY% > +120% &
	\ PRIORITY% = PRIORITY% AND NOT 7% &
	\ PRIORITY% = 255% IF PRIORITY% = 0% &
	\ RETURN &
		! ERROR UNLESS USER HAS TUNE PRIVILEGE &
		! GET PRIORITY ARGUMENT VALUE &
		! ERROR IF NOT IN RANGE -120 TO +120 &
		! ROUND VALUE DOWN TO MULTIPLE OF 8 &
	&

4090	! &
	!	/RUNBURST SWITCH &
	! &
	GOTO 4000 UNLESS FNPRIV%("TUNE") &
	\ RUNBURST% = VAL(ARG$) &
	\ GOTO 4000 IF RUNBURST% < 1% OR RUNBURST% > 127% &
	\ RETURN &
		! ERROR UNLESS USER HAS TUNE PRIVILEGE &
		! SAVE RUNBURST ARGUMENT VALUE &
		! ERROR IF NOT IN RANGE 1-127 &
	&

10000	! &
	! &
	! &
	!	P K    H A N D L I N G    R O U T I N E &
	! &
	! &
	! &
	PUT.COUNT%=LEN(CTRL.LINE$) &
		! LENGTH OF LINE TO PUT TO PK. &
	\ PUT.COUNT%=PUT.COUNT%-1% IF RIGHT(CTRL.LINE$,PUT.COUNT%-1%)=CR.LF$ &
		! IF LINE ENDS IN <CR>-<LF>, STRIP OFF <LF>. &
	\ PUT.COUNT%=PUT.COUNT%-2% IF RIGHT(CTRL.LINE$,PUT.COUNT%-2%)=LF.CR.0$ &
		! IF LINE ENDS IN <LF>-<CR>-<0>, STRIP OFF <CR>-<0>. &
	\ FIELD #1%, PUT.COUNT% AS T0$ &
		! SET UP BUFFER TO THE RIGHT LENGTH. &
	\ LSET T0$=CTRL.LINE$ &
		! MOVE LINE TO PK BUFFER. &
	\ PUT #1%, RECORD 8%, COUNT PUT.COUNT% &
		! PUT LINE TO THE PK. &

10010	GOSUB 10100 &
		! CHECK PK STATUS. &
	\ GOSUB 10200 &
		! GET AND PROCESS TEXT FROM PK. &
	\ RETURN UNLESS PK.STATUS%=3% &
		! EXIT PK HANDLER UNLESS JOB AT PK IS BUISY. &
	\ SLEEP 3% &
		! SLEEP AT LEAST 3 SEC. OR UNTIL PK STATUS CHANGES. &
	\ T0%=32000% &
	\ T0%=17% IF (KB.IN% OR KB.OUT%) &
	\ SLEEP (T0% EQV 32767%) &
		! &
		! IF WE ARE AT A KEYBOARD, MAKE SURE WE KEEP THE USER &
		! INFORMED OF THE LATEST OUTPUT CHARACTERS AT REASONABLE &
		! TIME INTERVALS. &
		! &
		! IF WE ARE JUST OFF DETACHED SOMEWHERE, GO AWAY FOR A &
		! VERY LONG TIME. &
		! &
		! NOTE THAT IF WE WERE WOKEN UP EARLY FROM THE FIRST &
		! (POSITIVE) SLEEP, THE SECOND (NEGATIVE) SLEEP WILL &
		! NEVER HAPPEN. &
		! &
		! THE REASON THE POSITIVE SLEEP IS NEEDED IS THAT IF &
		! LOGINS ARE DISABLED, THE NEGATIVE SLEEP WILL NEVER &
		! EXECUTE. &
		! &
	\ GOTO 10010 &
		! TRY AGAIN AT PK. &

10050	PK.STATUS%=-2% IF PK.STATUS%=5% &
	\ RETURN IF PK.STATUS%<0% &
		! IF ILLEGAL PK STATUS, GIVE UP. &
	\ GOSUB 10010 UNLESS ERR=63% &
		! GO WAIT FOR THE RIGHT PK STATUS. &
	\ GOTO 10000 UNLESS ERR=63% &
		! TRY AGAIN TO PUT TO PK. &
		! ERROR TRAPPED HERE WHEN PK IS IN THE WRONG STATE &
		! FOR A PUT AT LINE 10000. &
	\ ERR.MSG$=CTRL.LINE$+FNE$(47%) &
	\ GOTO 19200 &
		! IF "?FIELD overflows buffer" ERROR, WE HAD A LINE &
		! THAT WAS LONGER THAN WE COULD HANDLE. &
		! RETURN ERROR MESSAGE "?Line too long". &

10100	! &
	! &
	! &
	!	P K    S T A T U S    C H E C K &
	! &
	! &
	! &
	ON ERROR GOTO 10120 &
		! SET LOCAL ERROR TRAP. &
	\ DUMMY$=SYS(PRIV.ON$) &
		! REGAIN TEMPORARY PRIVILEGES. &
	\ T0%,PK.JOB.NO%=(PEEK(PK.KB.DDB%+2%) AND 126%)/2% &
		! JOB # * 2 AT PK'S KB. &
	\ PK.STATUS%=(JOB.NO%=PK.JOB.NO%) &
		! CLEAR THE STATUS IF WE ARE NOT CONTROLLING OURSELVES. &
		! RETURN BAD STATUS IF WE ARE CONTROLLING OURSELVES. &
	\ T0%=SWAP%(CVT$%(MID(SYS(CHR$(6%)+CHR$(26%)+CHR$(PK.JOB.NO%)+ &
		CHR$(0%)),21%,2%))) IF PK.JOB.NO% &
		! IF THERE IS A JOB AT PK'S KB, GET IT'S PPN. &
	\ DUMMY$=SYS(PRIV.OFF$) &
		! DROP TEMPORARY PRIVILEGES. &
	\ PK.STATUS%=-2% UNLESS T0% &
		! IF T0%=0% THEN EITHER THERE WAS NO JOB AT PK'S KB, &
		! OR THE JOB THERE WAS RUNNING LOGGED OUT. &
	\ GOTO 10110 IF PK.STATUS% &
		! SKIP THE TEST PUT IF ILLEGAL STATUS. &
	\ PUT #1%, RECORD 6%, COUNT 1% &
		! "TEST PUT" TO PK. &

10110	ON ERROR GOTO 19000 &
		! RESET STANDARD ERROR TRAP. &
	\ RETURN &
		! RETURN FROM PK STATUS CHECK. &

10120	PK.STATUS%=ERR &
		! RETURN THE STATUS OF THE PK. &
	\ RESUME 10110 &
		! GO RETURN THE STATUS. &
	&

10200	! &
	! &
	! &
	!	P R O C E S S     T E X T    F R O M    P K &
	! &
	! &
	! &
	ON ERROR GOTO 10220 &
		! SET LOCAL ERROR TRAP. &
	\ GET #1% &
		! DO THE GET. &
	\ ON ERROR GOTO 19000 &
		! RESET STANDARD ERROR TRAP. &
	\ FIELD #1%, RECOUNT AS PK.TEXT$ &
		! RETURN THE GET IN PK.TEXT$. &
	\ GOSUB 10230 IF ECHO% &
		! ECHO PROCESSING. &
	\ T0%=FNTO.LOG%(PK.TEXT$,0%) &
		! LOG THE TEXT. &
	\ GOSUB 10300 IF ERROR.CHECK% &
		! CHECK FOR ERROR AT PK IF CHECKING ENABLED. &
	\ GOTO 10200 &
		! SEE IF MORE TEXT PENDING. &

10210	ON ERROR GOTO 19000 &
		! RESET STANDARD ERROR TRAP. &
	\ RETURN &
		! RETURN FROM PROCESS TEXT ROUTINE. &

10220	RESUME 10210 &
		! GO RETURN FROM PROCESS TEXT ROUTINE. &

10230	T0%=0% &
	\ T0%=1% IF ASCII(PK.TEXT$)=36% AND ECHO%=1027% &
	\ T0%=2% IF F2$=CR.LF$ AND ECHO%=1010% &
	\ T0%=3% IF F3$=LF.CR.0$ AND ECHO%=1000% &
	\ T0%=4% IF F4$="^"+CHR$(ECHO%-1000%+64%)+CR.LF$ &
	\ FIELD #1%, T0% AS T0$, RECOUNT-T0% AS PK.TEXT$ IF T0% &
	\ ECHO%,PUT.COUNT%=0% &
	\ RETURN &
		! IF WE'RE ECHOING AND THE FIRST THING BACK IS &
		! <ESC> OR <CR>-<LF> OR <LF>-<CR>-<0> OR &
		! UPARROW-CHARACTER THAT WE EXPECTED, IGNORE IT. &
		! FLAG THAT WE'VE HANDLED THE ECHO. &

10300	! &
	! &
	! &
	!	C H E C K    P K    O U T P U T    F O R    E R R O R S &
	! &
	! &
	! &
	ON ERROR GOTO 19000 &
		! MAKE SURE STANDARD ERROR TRAP IS SET. &
	\ IF PUT.COUNT% THEN &
		LAST.CHR$=MID(PK.TEXT$,PUT.COUNT%,1%) &
	\	PK.TEXT$=RIGHT(PK.TEXT$,PUT.COUNT%+1%) &
	\	PUT.COUNT%=0% &
		! MAKE SURE WE DON'T PARSE ECHO LINE FOR ERRORS. &

10310	IF LEN(PK.TEXT$) THEN &
	  GOTO 10350 IF ERR.FLG% AND ASCII(PK.TEXT$)<>32% &
		! <LINE TERMINATOR> - <?> OR <%> NOT FOLLOWED BY <SP>. &
		! THIS CONFIRMS THAT WE INDEED DO HAVE AN ERROR. &
	\ ERR.FLG%=0% &
	\ T0%=INSTR(1%,PK.TEXT$,"?") &
		! FIND FIRST "?" IN TEXT, IF ANY. &
	\ T1%=INSTR(1%,PK.TEXT$,"%") &
		! FIND FIRST "%" IN TEXT, IF ANY. &
	\ T0%=T1% IF WARNING.CHECK% AND (T0%=0% OR (T1% AND T1%<T0%)) &
		! "%" WAS FIRST AND WE SHOULD CHECK FOR WARNINGS. &
	\ IF T0% THEN &
		! WE FOUND A "?" OR "%" IN THE TEXT. &
	      T0$=LAST.CHR$+LEFT(PK.TEXT$,T0%-1%) &
		! GET STRING PRECEEDING "?" OR "%". &
	\     T0$=LEFT(T0$,LEN(T0$)-1%) WHILE LEN(T0$) &
					AND ASCII(RIGHT(T0$,LEN(T0$)))=0% &
		! STRIP OFF ANY NULLS PRECEEDING "?" OR "%". &
	\     ERR.FLG%=INSTR(1%,CR.LF.FF$,RIGHT(T0$,LEN(T0$))) &
		! WAS IT PRECEEDED BY A <CR>, <LF> OR <FF>? &
	\     IF LEN(PK.TEXT$)>T0% THEN &
		      LAST.CHR$="?" &
	\	      PK.TEXT$=RIGHT(PK.TEXT$,T0%+1%) &
	\	      GOTO 10310 &
			! IF MORE TEXT TO PROCESS, GO BACK AND LOOK AT IT. &
		! THE RATIONAL HERE IS THAT "?" OR "%" APPEAR LESS OFTEN &
		! IN RANDOM TEXT THAN LINE TERMINATORS.  SO WE LOOK FURTHER &
		! FOR ERRORS (WHICH USES STRING MASHING IN BASIC-PLUS AND &
		! BASIC-PLUS-2) ONLY WHEN WE SEE A "?" OR "%". &

10320	T0$=NL$ &
	\ T0$=MID(PK.TEXT$,T0%,1%) FOR T0%=LEN(PK.TEXT$) STEP -1% &
			WHILE T0% AND ASCII(T0$)=0% &
	\ LAST.CHR$=T0$ IF ASCII(T0$) &
		! &
		! IF THERE WERE ANY NON-NULL CHARACTERS IN THIS TEXT &
		! FROM THE PK, GET THE LAST NON-NULL CHARACTER INTO &
		! LAST.CHR$ FOR FUTURE REFERENCE.  IF ONLY NULL &
		! CHARACTERS WERE FOUND, DO NOT DISTURB LAST.CHR$. &
		! &
		! NOTE THAT IF THE LAST CHARACTER IS NOT NULL (EXPECTED), &
		! ONLY ONE STRING MASHING FUNCTION (MID) IS INVOKED. &
		! &
	\ RETURN &
		! RETURN FROM ERROR CHECKING. &
		! &
		! UNALLOWABLE ERROR IS DEFINED AS THE FOLLOWING SEQUENCE: &
		!  1. <CR>, <LF> OR <FF> &
		!  2. ANY NUMBER OF NULLS. &
		!  3. "?" OR "%", DEPENDING ON TYPE OF ERRORS CHECKED FOR. &
		!  4. ANY CHARACTER EXCEPT A SPACE. &
		! &
		! NOTE THAT CARE IS TAKEN NOT TO PARSE THE ECHO RECEIVED &
		! BACK FROM THE PK FOR THIS SEQUENCE. &

10350	ERR.MSG$="?Error in job" &
		! SET UP ERROR MESSAGE. &
	\ GOTO 19200 &
		! GO REPORT IT AND ABORT JOB. &
	&

11200	! &
	! &
	! &
	!	S E T    U P    F O R     K B :    I N P U T &
	! &
	! &
	! &
	M%(T0%)=0% FOR T0%=1% TO 30% &
	\ M%(0%)=30% &
	\ M%(1%)=6% &
	\ M%(2%)=16% &
	\ M%(4%)=PK.KB% &
	\ T0$=SYS(PRIV.ON$) &
	\ KB.IN%=(PEEK(IOB%+IN.CH%*2%)=KB.DDB%) &
	\ M%(10%)=(128% OR KB.IN%) &
	\ CHANGE M% TO T0$ &
	\ T0$=SYS(T0$) &
	\ T0$=SYS(PRIV.OFF$) &
	\ IN.CH%=KB.CH% IF KB.IN% AND KB.CH% &
	\ RETURN &
		! SET KB.IN% TO FLAG WHETHER WE ARE INPUTTING FROM OUR &
		!	CONSOLE KEYBOARD. &
		! SET LOCAL ECHO AT THE PK IF WE ARE, FULL DUPLEX IF &
		! WE ARE NOT INPUTTING FROM OUR CONSOLE KEYBOARD. &

12000	! &
	! &
	! &
	!	C H E C K    F I L E S P E C    E X T E N S I O N &
	! &
	! &
	! &
	CHANGE SYS(CHR.6$+CHR$(-10%)+FILESPEC$) TO M% &
	\ T0%=INSTR(1%,FILESPEC$,":") &
	\ T1%=INSTR(1%,FILESPEC$,"/") &
	\ T1%=LEN(FILESPEC$)+1% UNLESS T1% &
	\ T0%=0% IF T1%<T0% &
	\ T2%=INSTR(T0%,FILESPEC$,"<") &
	\ T1%=T2% IF T2% AND T2%<T1% &
		! DO FILENAME STRING SCAN ON FILESPEC$. &
		! FIND OUT WHERE [P,PN]FILNAM.EXT STARTS - EITHER &
		! AT BEGINNING OR AFTER ":". &
		! FIND OUT WHERE [P,PN]FILNAM.EXT ENDS - EITHER &
		! AT END OF STRING OR BEFORE "<" OR BEFORE "/". &
	\ T0$="ATPK" &
	\ T1$=EXT$ &
	\ T2$=NL$ &
	\ T0$=RAD$(M%(7%)+SWAP%(M%(8%))) &
		+RAD$(M%(9%)+SWAP%(M%(10%))) IF (M%(29%) AND 1%) &
	\ T1$=RAD$(M%(11%)+SWAP%(M%(12%))) IF (M%(29%) AND 8%) &
	\ T2$="["+NUM1$(M%(6%))+","+NUM1$(M%(5%))+"]" IF (M%(29%) AND 128%) &
		! SET UP THE DEFAULT FILNAM, .EXT AND [P,PN]. &
		! GET THE SPECIFIED ONES IF THEY WERE SPECIFIED. &
	\ FILESPEC$=CVT$$(LEFT(FILESPEC$,T0%)+T2$+T0$+"."+T1$ &
		+RIGHT(FILESPEC$,T1%),2%) &
	\ RETURN &
		! REBUILD THE COMPLETE FILE SPECIFICATION. &
		! DEFAULT EXTENSION ADDED IF NEEDED. &
	&

15000	! &
	! &
	! &
	!	R E T U R N    E R R O R    M E S S A G E  -  F N E $ &
	! &
	! &
	! &
	DEF* FNE$(T0%)=CVT$$(RIGHT(SYS(CHR$(6%)+CHR$(9%)+CHR$(T0%)),3%),4%) &
		! DO SYS CALL TO LOOK UP ERROR MESSAGE. &
		! TRIM OFF TRAILING NULLS. &

16000	! &
	! &
	! &
	!	F N T O . L O G %  ( T 0 $ , T 0 % )  -  L O G    T E X T &
	! &
	! &
	! &
	DEF* FNTO.LOG%(T0$,T0%) &
	\ IF (NOT LOG.OFF%) THEN &
	    PRINT #3%, T0$; IF (NOT KB.OUT%) &
			    AND NO.REPROMPT% AND ERL<>16000% &
			! &
			! REPORT PK DIALOGUE IN LOG IF WE SHOULD, &
			! AND IF THE LOG IS NOT THE CONSOLE KEYBOARD. &
			! &
	\   IF (NOT T0%) THEN &
		IF (DETACHED% AND NOT KB.OUT%) THEN &
			PRINT #4%, T0$; IF WAIT.COMM% &
		ELSE	PRINT #KB.CH%, T0$; IF (NOT LOG.KB.OFF%) &
			! &
			! REPORT TO THE TERMINAL IF WE SHOULD. &

16010	FNEND &
		! END OF FNTO.LOG%(T0$) &
		! T0% IS -1% -> DON'T REPORT IT TO THE KEYBOARD. &
		!	  0% -> O.K. TO REPORT IT TO THE KEYBOARD. &

16100	! &
	! &
	! &
	!	F N P R I V $ ( priv_name$ ) &
	! &
	! &
	! Function to see if job currently has specified privilege &
	! If privileged then return -1%, else return 0% &
	! &
	! &
	DEF FNPRIV%(PRIV$) = &
		(ASCII(MID(SYS(CHR$(6%)+CHR$(32%)+CHR$(1%)+ &
		STRING$(3%,0%)+PRIV$),3%,1%)) = 0%) &

17000	! &
	! &
	! &
	!	F N D E L . T M P % ( T 0 % ) &
	! &
	!	K I L L    X X X X n n . T M P    F I L E S &
	! &
	! &
	! &
	DEF* FNDEL.TMP%(T0%) &
	\ CHANGE SYS(CHR.6$+CHR$(-10%)+"????" &
		+RIGHT(NUM1$(100%+T0%),2%)+".TMP") TO M% &
	\ M%(0%)=30% &
	\ M%(1%)=6% &
	\ M%(2%)=17% &
	\ M%(3%),M%(4%)=0% &
	\ CHANGE M% TO T0$ &
		! SET UP STRING TO GET A DIRECTORY &
		! MATCH FOR A XXXXnn.TMP FILE. &

17010	CHANGE SYS(T0$) TO M% &
	\ KILL RAD$(M%(7%)+SWAP%(M%(8%)))+RAD$(M%(9%)+SWAP%(M%(10%)))+".TMP" &
	\ GOTO 17010 &
		! GET NEXT MATCH FOR XXXXnn.TMP, KILL IT, &
		! AND TRY FOR ANOTHER MATCH. &

17020	FNEND &
		! ERRORS FROM PREVIOUS LINE ARE TRAPPED TO RETURN HERE. &
		! END OF FNDEL.TMP%(T0%). &

18000	! &
	! &
	! &
	!	F N Q U O T E $ ( C T R L . L I N E $ ) &
	! &
	!	P A R S E    F O R    Q U O T E    C H A R S &
	! &
	! &
	! &
	DEF* FNQUOTE$(CTRL.LINE$) &
	\ FNQUOTE$=CTRL.LINE$ &
	\ T0%=0% &
		! INITIALIZE TO PARSE FOR QUOTE CHARACTER. &

18010	T0%=INSTR(T0%,CTRL.LINE$,"_") &
		! FIND NEXT QUOTE CHARACTER IN LINE. &
	\ IF T0% THEN &
		! WE FOUND ONE. &
	    FNQUOTE$,CTRL.LINE$=LEFT(CTRL.LINE$,T0%-1%) &
		    +CHR$(ASCII(MID(CTRL.LINE$,T0%+1%,1%)) OR 128%) &
		    +RIGHT(CTRL.LINE$,T0%+2%) &
		! TAKE OUT QUOTE CHARACTER AND SET PARITY BIT &
		! ON NEXT CHARACTER. &
	\   GOTO 18010 &
		! SEE IF WE HAVE ANOTHER QUOTE CHARACTER. &

18020	FNEND &
		! END OF FNQUOTE$(CTRL.LINE$) &

18100	! &
	! &
	! &
	!	F N C T R L $ ( C T R L . L I N E $ ) &
	! &
	!	P A R S E    F O R    C T R L    C H A R S &
	! &
	! &
	! &
	DEF* FNCTRL$(CTRL.LINE$) &
	\ T0%=0% &
		! INITIALIZE FOR SEARCH FOR CTRL CHARS. &
	\ T0$=CVT$$(CTRL.LINE$,4%) &
		! STRIP LINE TERMINATOR. &
	\ IF ASCII(T0$)=94% AND LEN(T0$)=2% THEN &
		CTRL.LINE$=T0$ &
	\	ECHO%=1000%+(ASCII(RIGHT(T0$,2%)) AND -97%) IF KB.IN% &
		! IF LINE CONTAINS ONLY UPARROW FOLLOWED BY SINGLE CHARACTER, &
		! DISCARD THE LINE TERMINATOR. &
		! IF WE SHOULD LOOK FOR THE ECHO OF THE SINGLE CONTROL &
		! CHARACTER, FIGURE OUT WHAT IT IS. &

18120	T0%=INSTR(T0%,CTRL.LINE$,"^") &
		! FIND NEXT (UNQUOTED) UPARROW IN LINE. &
	\ IF T0% THEN &
		! WE FOUND ONE. &
	    CTRL.LINE$=LEFT(CTRL.LINE$,T0%-1%) &
		    +CHR$(ASCII(MID(CTRL.LINE$,T0%+1%,1%)) AND -97%) &
		    +RIGHT(CTRL.LINE$,T0%+2%) &
		! TAKE OUT UNQUOTED UPARROW AND UNSET BITS &
		! ON NEXT CHARACTER MAKING IT A CONTROL CHARACTER. &
	\   ECHO%=1000%+ASCII(CTRL.LINE$) IF KB.IN% AND LEN(CTRL.LINE$)=1% &
	\   GOTO 18120 &
		! SEE IF WE HAVE ANOTHER UPARROW. &

18130	FNCTRL$=CVT$$(CTRL.LINE$,1%) &
	\ FNEND &
		! END OF FNCTRL$(CTRL.LINE$) &

19000	! &
	! &
	! &
	!	E R R O R    H A N D L I N G    R O U T I N E &
	! &
	! &
	! &
	DUMMY$=SYS(CHR$(6%)+CHR$(-7%)) IF ERR=28% &
		! REENABLE CTRL/C TRAP AS SOON AS POSSIBLE. &
	\ DUMMY$=SYS(PRIV.OFF$) IF LEN(PRIV.OFF$) &
		! DROP TEMPORARY PRIVILEGES. &
	\ RESUME 19010 &
		! RESUME FROM THE ERROR. &

19010	IF ERR=11% AND ERL=2000% AND IN.AT% AND KB.IN%=0% THEN &
		! EOF FROM INDIRECT COMMAND FILE. &
	      IN.CH%=2% &
	\     IN.AT%=0% &
	\     CLOSE 5% &
	\     GOSUB 11200 &
	\     GOTO 2000 &
		! FLAG THAT WE ARE THROUGH PROCESSING THE INDIRECT COMMAND &
		! FILE.  CLOSE IT AND RESUME PROCESSING FROM THE PRIMARY &
		! COMMAND FILE. &

19020	IF ERR=11% AND ERL=2000% AND KB.IN% THEN &
	      CTRL.LINE$=CHR$(26%) &
	\     T0%=FNTO.LOG%("^Z"+CR.LF$,-1%) &
	\     ECHO%=1026% &
	\     GOTO 2010 &
		! CTRL/Z FROM KEYBOARD TO INPUT FOR CONTROL LINE. &
		! PASS THE CTRL/Z ON TO THE PK JOB. &

19040	GOTO 19200 IF ERR=11% OR ERR=28% &
		! USER ^Z OR ^C OR EOF ON CONTROL FILE - EXIT. &
	\ GOTO 2100 IF ERL=2000% AND ERR=15% AND KB.IN% &
		! WAIT TIMEOUT ON GET FROM KB. &
	\ GOTO 2920 IF ERL=2910% &
		! NO JOB TO KILL AT PK. &
	\ GOTO 4000 IF ERL=4010% OR ERL=4050% OR ERL=4080% OR ERL=4090% &
		! ERROR ON DECODING SWITCH. &
	\ GOTO 10050 IF ERL=10000% &
		! ERROR ON PUT TO PK - PK IS IN WRONG STATE. &
	\ GOTO 17020 IF ERL=17010% &
		! THROUGH KILLING ALL XXXXnn.TMP FILES. &
	\ GOTO 32750 IF ERL=32700% &
		! CAN'T KILL CONTROL FILE. &
	\ GOTO 32760 IF ERL=32750% &
		! CAN'T CHAIN TO PROGRAM. &
	\ GOTO 32767 IF ERL=32760% &
		! CAN'T KILL OURSELVES. &

19050	ERR.MSG$=FNE$(ERR)+" - "+FILESPEC$ &
		IF ERL=1520% OR ERL=2810% OR ERL=12000% &
			! ERROR ON INPUT OR OUTPUT FILE OPEN &
			! OR FILENAME STRING SCAN. &
	\ ERR.MSG$="?Unable to login ["+NUM1$(SWAP%(T1%) AND 255%)+ &
		   ","+NUM1$(T1% AND 255%)+"] on KB"+NUM1$(T3%)+ &
		   ": - "+FNE$(ERR) &
		IF ERL=2930% &
			! ERROR SPAWNING JOB ON KB &
	\ ERR.MSG$="?Unable to detach - "+FNE$(ERR) &
		IF ERL=3000% &
			! ERROR TRYING TO DETACH &
	\ ERR.MSG$="?No PK's available - please try again later" &
		IF ERL=1530% AND ERR=6% &
			! ALL PK'S TRIED AND NONE FREE. &
	\ ERR.MSG$="?No room for ATPK now - please try again later" &
		IF ERL=2930% AND ERR=4% &
			! "NO ROOM FOR USER" ON SPAWNING LOGIN. &
	\ ERR.MSG$="?Illegal argument for $LOGIN command - "+T0$ &
		IF ERL=2900% &
			! FSS ERROR ON PARSING $LOGIN COMMAND. &
	\ ERR.MSG$="?Keyboard unavailable in $WAIT command" &
		IF ERL=2700% AND ERR=8% &
			! "DEVICE NOT AVAILABLE" IN $WAIT OPEN. &
	\ GOTO 19200 IF LEN(ERR.MSG$) &
			! ABORT WITH FATAL ERROR MESSAGE. &

19100	PRINT IF CCPOS(0%) &
	\ PRINT "??Program failure in ATPK "; IDENT.STG$ &
	\ PRINT FNE$(ERR);" at line";ERL &
	\ GOTO 32767 &
		! THAT WAS A BAD ONE. &

19200	T2%=(ERL=2000% AND ERR=11%) &
		! FLAG TO SEE IF THIS IS A SUCCESSFUL EXIT. &
	\ ERR.MSG$=ERR.MSG$+CR.LF$+"?Command file aborted" IF NO.REPROMPT% &
	\ LOG.OFF%,LOG.KB.OFF%=0% &
	\ T0%=FNTO.LOG%(CR.LF$+ERR.MSG$+CR.LF$,0%) UNLESS T2% &
	\ T0%=FNTO.LOG%(CR.LF$+"!*** Processing ended on "+ &
		DATE$(0%)+" at "+TIME$(0%)+" ***"+CR.LF$,0%) &
			IF STAMP.SW% &
		! DATE/TIME STAMP WHEN WE STOPPED, IF /TIM WAS SPECIFIED. &
	\ ERR.MSG$=NL$ &
		! LOG THE ERROR MESSAGE IF THERE WAS ONE. &
		! RESET THE ERROR MESSAGE. &
	\ T0%=FNDEL.TMP%(PK.JOB.NO%) IF PK.JOB.NO% &
		! IF THERE WAS A PK JOB, KILL ITS XXXXnn.TMP FILES. &
	\ IF ASNPK.FLG% THEN &
		Z$ = SYS(PRIV.ON$) &
	\	Z$ = SYS(CHR.6$+CHR$(11%)+STRING$(20%,0%)+ &
			"KB"+CHR$(PK.KB%)+CHR$(-1%)) &
	\	Z$ = SYS(PRIV.OFF$) &
		! IF PK KEYBOARD WAS ASSIGNED (FOR PRELIN), &
		! RAISE TEMP PRIVS &
		! DEASSIGN THE PK KEYBOARD &
		! DROP TEMP PRIVS &

19300	CLOSE 1%, 2%, 3%, 4%, 5% &
		! CLOSE THE PK AND ALL FILES (KILLING ANY JOB AT PK). &
	\ GOTO 1060% UNLESS ENTRY.TYP% OR NO.REPROMPT% OR ERL=1100% &
		! RETURN TO "*" IF RUN ENTRY AND WE STILL SHOULD. &
	\ GOTO 32700 IF T2% &
	\ GOTO 32760 &
		! GO TO APPROPRIATE EXIT: &
		! IF SUCCESSFUL FINISH, GO TO SUCCESSFUL EXIT ROUTINE. &
		! OTHERWISE, JUST GO EXIT, KILLING OURSELVES IF DETACHED. &

30000	! &
	! &
	! &
	!	C C L    E N T R Y    P O I N T &
	! &
	! &
	! &
	T0$=SYS(CHR$(7%)) &
		! GET CORE COMMON. &
	\ T0%=INSTR(1%,T0$," ") &
	\ T1%=INSTR(1%,T0$,"/") &
	\ T0%=T1% IF T1% AND (T1%<T0% OR T0%=0%) &
	\ START$=RIGHT(T0$,T0%+1%) &
		! STRIP COMMAND OF CCL. &
	\ ENTRY.TYP%=2% IF T0% &
		! FAKE A RUN ENTRY IF NO ARGUMENTS PRESENT IN CCL. &
	\ GOTO 1000 &

30010	! &
	! &
	! 	ALTERNATE ENTRY POINT FOR DCL PRELIN PROGRAM &
	! &
	! CORE COMMON WILL CONTAIN A COMMAND LINE. &
	! ALLOWS USER WITHOUT DEVICE PRIVILEGE TO: &
	!	1) RUN ATPK FROM A JOB RUNNING ON A PK, &
	!	2) SPAWN A JOB ON A RESTRICTED PK. &
	&
	PRELIN.FLG%=-1% &
	\ GOTO 30000 &
		! SET PRELIN FLAG &
		! SKIP TO STANDARD CCL ENTRY &

32700	! &
	! &
	! &
	!	E X I T &
	! &
	! &
	! &
	KILL START$ IF DELETE.CTL% &

32750	CHAIN CHAIN.PROG$ LINE CHAIN.LINE% &
		IF CHAIN.FLG% &
		! ON A SUCCESSFUL EXIT: &
		! 1) KILL THE CONTROL FILE IF FLAGGED TO. &
		! 2) CHAIN IF FLAGGED TO. &

32760	DUMMY$=SYS(PRIV.ON$) &
	\ IF (PEEK(PEEK(IOB%)+2%) AND 255%)<>JOB.NO%*2% THEN &
		DUMMY$=SYS(PRIV.OFF$) &
	\	T0%=FNDEL.TMP%(JOB.NO%) &
	\	DUMMY$=SYS(PRIV.ON$) &
	\	DUMMY$=SYS(CHR.6$+CHR$(8%)+CHR$(JOB.NO%) &
			+STRING$(24%,0%)+CHR$(255%)) &
		! KILL OURSELVES IF WE ARE DETACHED. &

32767	END &

