Google
 

Trailing-Edge - PDP-10 Archives - tops20tools_v6_9-jan-86_dumper - tools/recog3/hlpinit.b32
There are 2 other files named hlpinit.b32 in the archive. Click here to see a list.
MODULE LIB$RECOG_INIT (
		IDENT = 'V00A03'
		 ) =
BEGIN

!
!			  COPYRIGHT (C) 1982 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 WHICH IS NOT SUPPLIED BY DIGITAL.
!
!++
! FACILITY:	HELP
!
! ABSTRACT:
!
!	This module contains the main routines in the
!	HELP/RECOGNITION facility that are user callable.
!
! ENVIRONMENT:
!
! AUTHOR: Stanley Rabinowitz,	CREATION DATE:	26-Dec-1981
!
! MODIFIED BY: Stanley Rabinowitz
!
! Edit History:
!
! V00A03	 9-Oct-1982	Stan	Allow reprompting on a null line.
!					Put error part of field in atom buffer
!					and reset the pointer.
! V00A02	 3-Oct-1982	Stan	Error status in CAB$L_STS
!					Allocate command line buffer if
!					none specified by user.
! V00A01	24-Sep-1982	Stan	Allow input from non-terminal.
!--
!
! TABLE OF CONTENTS:
!

FORWARD ROUTINE

	LIB$PARSE_LINE,		! Parse a line
	LIB$PARSE_FIELD,	! Parse a field
	GET_LINE,		! Get a line of input
	GET_CHARACTERISTICS,	! Get characteristics of SYS$INPUT
	LIB$$HLP$CATCHER,	! Condition handler to catch HLP$_HELP
	PARSE_PAB,		! Parses from just one PAB
	HLP$REPARSE_HANDLER,	! Condition handler to catch HLP$_REPARSE
	LIB$$HLP$CHAR,
	LIB$$HLP$UCHAR,
	LIB$$HLP$BACKUP		: NOVALUE;

!
! INCLUDE FILES:
!

REQUIRE 'HLP.R32';

!
! MACROS:
!

!
! EQUATED SYMBOLS:
!

!
! OWN STORAGE:
!

!
! EXTERNAL REFERENCES:
!

EXTERNAL ROUTINE

	LIB$OUT_RECOG,
	LIB$$NUMBER_RECOG,
	LIB$$TOKEN_RECOG,
	LIB$$END_HELP,
	LIB$$NO_RECOG,
	LIB$$GENERAL_HELP,
	LIB$$FILENAME_RECOG,
	LIB$$KEYWORD_HELP,
	LIB$$KEYWORD_RECOG,
	LIB$$HANDLE_KEYWORD,
	LIB$$HANDLE_NUMBER,
	LIB$$HANDLE_GUIDEWORD,
	LIB$$HANDLE_TOKEN,
	LIB$$HANDLE_TEXT,
	LIB$$HANDLE_STRING,
	LIB$$HANDLE_QUOTED_STRING,
	LIB$$HANDLE_PARAM,
	LIB$$HANDLE_FILENAME,
	LIB$$HANDLE_NAME,
	LIB$$HANDLE_CHARACTER;
GLOBAL ROUTINE LIB$PARSE_LINE(P_CAB,P_RTN,RTN_PARAM) =

BEGIN

!++
!
! FUNCTIONAL DESCRIPTION:
!
!	Initializes the system so that it is ready to
!	begin parsing a command line.
!	Perform an initial read.
!
! FORMAL PARAMETERS:
!
!	P_CAB		Address of associated CAB.
!
!	P_RTN		Address of user's parsing routine.
!
!	RTN_PARAM	Longword parameter to be passed to user's
!			parsing routine.
!
! IMPLICIT INPUTS:
!
!	The following fields in the CAB must be previously set up
!	by the user.  (This can be done with the $CAB macro.)
!
!	CAB$W_BLN	set by $CAB
!	CAB$B_BID	set by $CAB
!	CAB$V_RAI	set by user
!	CAB$V_WAK	set by user
!	CAB$V_REP	set by user
!	CAB$V_COM	set by user
!	CAB$V_CON	set by user
!	CAB$W_CHN	set by user
!	CAB$A_GET	set by user
!	CAB$A_PMT	set by user
!	CAB$A_CMD	set by user
!	CAB$A_ATM	set by user
!
! IMPLICIT OUTPUTS:
!
!	CAB$A_PAB	cleared
!	CAB$W_RES	cleared
!	CAB$W_CLN	cleared
!	CAB$W_PTR
!	CAB$W_FLD_PTR	cleared
!	CAB$W_PMT_PTR	cleared
!	CAB$W_STS	cleared
!	CAB$W_STV	cleared
!
! ROUTINE VALUE:
!
!	SS$_NORMAL	everything is okay
!
!	HLP$_CAB	CAB is invalid (BID field incorrect)
!
! SIDE EFFECTS:
!
!	NONE
!
!--
LOCAL

	TERMIN,		! Fix
	STATUS;

BIND

	CAB	= .P_CAB		: $CAB_DECL;

BIND ROUTINE

	RTN	= .P_RTN;

EXTERNAL LITERAL

	HLP$_REPARSE,
	HLP$_CAB_BID,
	HLP$_CAB_BLN;

ENABLE	HLP$REPARSE_HANDLER;
!+
!	Check that this is really a CAB.
!	If not, or if it is too small, return
!	the error status HLP$_CAB and do not try to write in the CAB.
!-

IF .CAB[CAB$B_BID] NEQ  CAB$K_BID
  THEN	RETURN	HLP$_CAB_BID;

IF .CAB[CAB$W_BLN] LSSU	CAB$K_BLN
  THEN	RETURN	HLP$_CAB_BLN;

!+
!	Initialize various CAB fields to 0.
!-

CAB[CAB$A_PAB]=0;	! No initial PAB yet
CAB[CAB$W_CLN]=0;	! No command line seen yet
CAB[CAB$L_STS]=0;	! No error status yet
!CAB[CAB$L_STV]=0;	! No error status value

!+
!	If no command line buffer was specified by the user,
!	then allocate a dynamic buffer for him.
!-

IF .CAB[CAB$A_CMD] EQL 0
  THEN	BEGIN
	EXTERNAL ROUTINE

		LIB$GET_VM,
		LIB$SGET1_DD;

	!+
	! Get 8 bytes of memory for a dynamic string descriptor.
	!-

	STATUS=LIB$GET_VM(%REF(8),CAB[CAB$A_CMD]);
	IF NOT .STATUS THEN SIGNAL(.STATUS);

	!+
	! Zero this descriptor.
	!-

	CH$FILL(0,8,.CAB[CAB$A_CMD]);

	!+
	! Allocate a dynamic string descriptor for 256 bytes.
	!-

	STATUS=LIB$SGET1_DD(%REF(256),.CAB[CAB$A_CMD]);
	IF NOT .STATUS THEN SIGNAL(.STATUS);

	!+
	! Note that the virtual memory does not get deallocated
	! because it can be used again if the user makes a subsequent
	! call to these routines with the same CAB.
	!-

	END;

	BEGIN
	BIND	CMD = .CAB[CAB$A_CMD]	: BLOCK[,BYTE];
	CAB[CAB$W_CSZ]=.CMD[DSC$W_LENGTH];
	CAB[CAB$A_CBF]=.CMD[DSC$A_POINTER]
	END;

!+
!	If no channel has been supplied, then we will
!	assign a channel to SYS$INPUT.
!	This code relies on the fact that 0 is an illegal
!	channel number.
!-

IF .CAB[CAB$W_CHN] EQL 0
  THEN	BEGIN

	STATUS=$ASSIGN(	DEVNAM	= %ASCID 'SYS$INPUT',
			CHAN	= CAB[CAB$W_CHN]);
	IF NOT .STATUS
	  THEN	BEGIN
		CAB[CAB$L_STS] = .STATUS;
		RETURN .STATUS
		END;

	! How does this channel get deassigned?

	!+
	! Now clear out the DEVCLASS byte.
	! We rely on the fact that there is no
	! device class of 0 and thereby use the CAB$B_DEVCLASS
	! field to indicate that we have not yet ascertained
	! the input device's class, type, and characteristics.
	! Should the user want to reuse this CAB and shove in a
	! new channel number, he will also have to 0 out this field.
	! (We need to provide a macro to do that.)
	!-

	CAB[CAB$B_DEVCLASS]=0

	END;

!+
!	If we have not previously ascertained the characteristics
!	of the input device, do so now.
!	The field CAB$B_DEVCLASS is non-0 if we had previously
!	ascertained the device class, type, and characteristics.
!-

IF .CAB[CAB$B_DEVCLASS] EQL 0
  THEN	BEGIN
	STATUS=GET_CHARACTERISTICS(CAB);
	IF NOT .STATUS THEN RETURN .STATUS
	END;

!+
!	Now start by doing an initial read from the input device.
!-

!+
!	Invoke the user's parsing routine
!	with 3 arguments:
!
!	1.	The address of the CAB.
!		This is useful to him in case he is
!		using the same parse routine to handle
!		multiple CABs.
!
!	2.	His specified parameter.  This could
!		point to his full argument list, if desired.
!
!	3.	A flag specifying whether this is a parse or a reparse.
!		0 (FALSE)	means parse
!		1 (TRUE)	means reparse
!-

STATUS	= SS$_NORMAL;
CAB[CAB$V_HLP] = TRUE;	! KLUDGE so that we prompt first time through

!CAB[CAB$W_ERR_PTR]=0;
CAB[CAB$W_PTR]=0;	! Start at beginning of line
			! This changes to implement CTRL/H.

DO	BEGIN
	LOCAL	TEMP;
	TEMP=.CAB[CAB$V_HLP];
	CAB[CAB$W_RES]=0;	! Reset result flags (except for CAB$V_HLP)
	CAB[CAB$V_HLP]=.TEMP;
	CAB[CAB$W_FLD_PTR]=0;	! First field starts at begin of command line
	CAB[CAB$W_PMT_PTR]=0;	! First prompt field starts at begin of command line
	IF .CAB[CAB$V_GOT]
	  THEN	BEGIN
		BIND BUF = .CAB[CAB$A_CMD] : BLOCK[,BYTE];
		CH$FILL(0,8,CAB[CAB$Q_IOSB]);
		CAB[CAB$W_QIO_STS]=1;
		CAB[CAB$B_QIO_TRM]=13;
		CAB[CAB$W_CLN]=.BUF[DSC$W_LENGTH];
		IF NOT .STATUS
		  THEN	BEGIN
			CAB[CAB$L_STS] = .STATUS;
			RETURN .STATUS
			END;
		END
	  ELSE	BEGIN
		STATUS=GET_LINE(CAB);
		IF NOT .STATUS THEN RETURN .STATUS
		END;

	CAB[CAB$W_PTR] = 0;	! No characters parsed yet
	CAB[CAB$V_HLP] = .CAB[CAB$B_QIO_TRM] EQL HLP$K_HELP;
	CAB[CAB$V_TAB] = .CAB[CAB$B_QIO_TRM] EQL HLP$K_RECOGNIZE;

	!+
	!	Allow optional spaces at the beginning of the command
	!	unless prohibited by the programmer.
	!-

	IF NOT .CAB[CAB$V_NOS]
	  THEN	$OPT_SPACES;

	!+
	! If CAB$V_NUL is set and the input line is null,
	! then we go do a reparse, i.e. we get another line of input.
	! We studiously avoid doing this if CAB$V_GOT is set,
	! because that would put us in an infinite loop.
	! If the above situation is not true, then we call
	! the user's parsing routine to parse the line that
	! has been obtained.
	! Note that the third argument to the user's parsing routine
	! is now WRONG; but this is okay since we want to get
	! rid of this argument.
	!-

	IF NOT .CAB[CAB$V_GOT] AND
	  .CAB[CAB$W_PTR] EQL .CAB[CAB$W_CLN] AND
	  NOT .CAB[CAB$V_HLP] AND
	  NOT .CAB[CAB$V_TAB]
	  THEN	BEGIN
		STATUS=HLP$_REPARSE;
		CAB[CAB$V_HLP] = TRUE;	! KLUDGE so that we prompt again
		CAB[CAB$W_PTR]=0	! Start at beginning of line
		END
	  ELSE	STATUS=RTN(CAB,.RTN_PARAM,.STATUS EQL HLP$_REPARSE)
	END
UNTIL	.STATUS NEQ HLP$_REPARSE;

IF NOT .STATUS
  THEN	BEGIN
	CAB[CAB$L_STS] = .STATUS;
	RETURN .STATUS
	END;

!+
!	Everything went all right, so return SS$_NORMAL as success status.
!-

CAB[CAB$L_STS] = SS$_NORMAL;
RETURN SS$_NORMAL

END;
ROUTINE GET_CHARACTERISTICS(P_CAB) =

BEGIN

!++
!
! FUNCTIONAL DESCRIPTION:
!
!	Gets characteristcis of SYS$INPUT and puts them in the CAB.
!
! FORMAL PARAMETERS:
!
!	P_CAB		Address of CAB
!
! IMPLICIT INPUTS:
!
!	NONE
!
! IMPLICIT OUTPUTS:
!
!	CAB fields get filled in:
!
!	CAB$B_DEVCLASS		device class
!	CAB$L_DEVCHAR		device characteristics
!	CAB$B_DEVTYPE		device type
!	CAB$W_DEVBUFSIZ		device buffer size (terminal width)
!	CAB$L_DEVDEPEND		device dependent information
!	CAB$L_DEVDEPND2		extended device characteristics.
!
! ROUTINE VALUE:
!
!	status value
!
! SIDE EFFECTS:
!
!	NONE
!
!--
LOCAL

	STATUS;

BIND

	CAB	= .P_CAB		: $CAB_DECL;

OWN

	DEVDEPEND2	: BITVECTOR[32],
	DVI_LIST	: VECTOR[1*3+1]
		INITIAL(WORD(4),WORD(DVI$_DEVDEPEND2),
			LONG(DEVDEPEND2),
			LONG(0),
			LONG(0));

LOCAL

	CBUF	: BLOCK[DIB$K_LENGTH,BYTE],	! Device characteristics buffer
	CDESC	: VECTOR[2];			! Descriptor for that buffer
!+
! Set up the descriptor for the device characteristics buffer
! on the stack.
!-

CDESC[0]=DIB$K_LENGTH;
CDESC[1]=CBUF;

!+
! Issue the $GETCHN system service to ascertain the desired
! information.
!-

STATUS=$GETCHN(	CHAN	= .CAB[CAB$W_CHN],
		PRIBUF	=  CDESC);
IF NOT .STATUS
  THEN	BEGIN
	CAB[CAB$L_STS] = .STATUS;
	RETURN .STATUS
	END;

STATUS=$GETDVI(	CHAN	= .CAB[CAB$W_CHN],
		ITMLST	=  DVI_LIST);
IF NOT .STATUS THEN SIGNAL(.STATUS);

!+
! Move that information into the CAB.
! The fields set up are:
!
!	CAB$B_DEVCLASS		device class
!	CAB$L_DEVCHAR		device characteristics
!	CAB$B_DEVTYPE		device type
!	CAB$W_DEVBUFSIZ		device buffer size (terminal width)
!	CAB$L_DEVDEPEND		device dependent information
!	CAB$L_DEVDEPND2		extended device characteristics.
!-

CAB[CAB$B_DEVCLASS]	= .CBUF[DIB$B_DEVCLASS];
CAB[CAB$L_DEVCHAR]	= .CBUF[DIB$L_DEVCHAR];
CAB[CAB$B_DEVTYPE]	= .CBUF[DIB$B_DEVTYPE];
CAB[CAB$W_DEVBUFSIZ]	= .CBUF[DIB$W_DEVBUFSIZ];
CAB[CAB$L_DEVDEPEND]	= .CBUF[DIB$L_DEVDEPEND];
CAB[CAB$L_DEVDEPND2]	= .DEVDEPEND2;

!+
! If the user has not specified a WIDTH, then
! we default in the WIDTH field from the terminal's width
! (device buffer size).
!-

IF .CAB[CAB$W_WIDTH] EQL 0
  THEN	CAB[CAB$W_WIDTH]=.CAB[CAB$W_DEVBUFSIZ];

RETURN SS$_NORMAL

END;
ROUTINE GET_LINE(P_CAB) =

BEGIN

!++
!
! FUNCTIONAL DESCRIPTION:
!
!	Gets a line (or part of a line) of input from the terminal,
!	or other input device (physical or logical).
!
! FORMAL PARAMETERS:
!
!	P_CAB		Address of CAB.
!
! IMPLICIT INPUTS:
!
!	items in CAB
!
! IMPLICIT OUTPUTS:
!
!	items in CAB
!
! ROUTINE VALUE:
!
!	status value
!
! SIDE EFFECTS:
!
!	NONE
!
!--
LOCAL

	TIO_STATUS,
	STATUS;

BIND

	CAB	= .P_CAB		: $CAB_DECL;

BIND ROUTINE

	GET = .CAB[CAB$A_GET];

EXTERNAL ROUTINE

	LIB$GET_INPUT	: ADDRESSING_MODE(GENERAL);

EXTERNAL LITERAL

	RMS$_EOF;
!+
! If the user has supplied a routine to get input,
! then we merely get input from that routine and then return.
!-

IF GET NEQ 0
  THEN	BEGIN
	CH$FILL(0,8,CAB[CAB$Q_IOSB]);
	STATUS=GET(.CAB[CAB$A_CMD],0,CAB[CAB$W_CLN]);
	CAB[CAB$W_QIO_STS]=1;
	CAB[CAB$B_QIO_TRM]=13;
	IF NOT .STATUS
	  THEN	CAB[CAB$L_STS] = .STATUS;
	RETURN .STATUS
	END;

!+
! If no user-supplied input routine is given, then we must get
! input from SYS$INPUT.
!-

!+
! If the input device is not a terminal,
! then we just read in a line using LIB$GET_INPUT and return.
!-

IF .CAB[CAB$B_DEVCLASS] NEQ DC$_TERM
  THEN	BEGIN
	STATUS=LIB$GET_INPUT(.CAB[CAB$A_CMD],
			     .CAB[CAB$A_PMT],
			      CAB[CAB$W_CLN]);
	IF NOT .STATUS
	  THEN	CAB[CAB$L_STS] = .STATUS;
	RETURN .STATUS
	END;

!+
! Otherwise we do our normal stuff.
!-

DO TIO_STATUS = $TIO(CAB=CAB)
UNTIL .TIO_STATUS NEQ SS$_ABORT;
IF .TIO_STATUS EQL SS$_TIMEOUT
OR .TIO_STATUS EQL RMS$_EOF
  THEN	BEGIN
	CAB[CAB$L_STS] = .TIO_STATUS;
	RETURN .TIO_STATUS
	END;

IF NOT .TIO_STATUS THEN SIGNAL(.TIO_STATUS);

RETURN SS$_NORMAL
END;
GLOBAL ROUTINE LIB$PARSE_FIELD(P_PAB) =

BEGIN

!++
!
! FUNCTIONAL DESCRIPTION:
!
!	Parse a field.
!
! FORMAL PARAMETERS:
!
!	P_PAB		Address of PAB (Parameter Access Block)
!
! IMPLICIT INPUTS:
!
!	The following fields in the PAB must have been set up:
!
!	PAB$W_BLN	set by $PAB
!	PAB$B_BID	set by $PAB
!	PAB$B_TYP	set by user	type of field
!	PAB$A_NXT	set by user
!	PAB$L_ARG	set by user
!	PAB$V_SPO	set by user
!	PAB$V_SDH	set by user
!	PAB$V_NOS	set by user
!	PAB$A_CAB	set by user
!	PAB$A_HLP	set by user
!	PAB$A_DEF	set by user
!	PAB$A_PRM	set by user
!	PAB$A_GDW	set by user
!
!	The following fields in the associated CAB must be set up:
!
!	tbs
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! ROUTINE VALUE:
!
!	SS$_NORMAL	field was parsed successfully
!
!	SS$_REPARSE	The command line must be reparsed
!
! SIDE EFFECTS:
!
!	NONE
!
!--
LOCAL

	SAVE_EOL,
	DEPTH,
	ERR_STATUS,	! First error status found
	STATUS;

BIND

	PAB	= .P_PAB		: $PAB_DECL,
	CAB	= .PAB[PAB$A_CAB]	: $CAB_DECL;

EXTERNAL LITERAL

	HLP$_HELP,
	HLP$_REPARSE,
	HLP$_MISCAB,	! Backpointer to CAB missing from PAB
	HLP$_MISPAB,	! Pointer to PAB missing in CAB.
	HLP$_PAB_BID,	! Invalid PAB (block ID bad)
	HLP$_PAB_BLN,	! Invalid PAB (length too small)
	HLP$_CAB2_BID,	! Invalid CAB in RAB
	HLP$_CAB2_BLN;	! Invalid CAB in RAB

EXTERNAL ROUTINE

	STR$COPY_R,
	STR$UPCASE;
!+
!	Check that the PAB block is valid.
!	If the address of the PAB is 0 or
!	the Block ID is not PAB$K_BID
!	or the length is too short, then return
!	an error, HLP$_PAB.
!-

IF PAB EQL 0
  THEN	RETURN HLP$_MISPAB;
IF .PAB[PAB$B_BID] NEQ  PAB$K_BID
  THEN	RETURN HLP$_PAB_BID;
IF .PAB[PAB$W_BLN] LSSU PAB$K_BLN
  THEN	RETURN HLP$_PAB_BLN;

!+
!	If the backpointer to the CAB is 0
!	or points to an invalid CAB, then
!	return the error HLP$_CAB2.
!-

IF CAB EQL 0
  THEN	RETURN HLP$_MISCAB;
IF .CAB[CAB$B_BID] NEQ  CAB$K_BID
  THEN	RETURN HLP$_CAB2_BID;
IF .CAB[CAB$W_BLN] LSSU CAB$K_BLN
  THEN	RETURN HLP$_CAB2_BLN;

!+
!	Put this PAB address in the CAB.
!-

CAB[CAB$A_PAB]=PAB;
CAB[CAB$L_STS]=SS$_NORMAL;

!+
!	Note that we are at the beginning of a field.
!	Note that no help has occurred yet for this field.
!-

CAB[CAB$W_FLD_PTR]=.CAB[CAB$W_PTR];
CAB[CAB$V_PHL]=FALSE;
CAB[CAB$W_ALN]=0;

!+
!	If a guideword is permitted, we handle it now
!	by building a special PAB.
!-

IF .PAB[PAB$A_GDW] NEQ 0
  THEN	BEGIN
	BIND GDW = .PAB[PAB$A_GDW] : BLOCK[,BYTE];
	IF .GDW[DSC$W_LENGTH] NEQ 0
	  THEN	BEGIN
		STATUS=LIB$$HANDLE_GUIDEWORD(PAB);
		IF .STATUS EQL HLP$_HELP
		  THEN	BEGIN
			$OUT(%ASCID '',HLP$K_END_LINE);
			SIGNAL(HLP$_REPARSE)
			END;
		IF NOT .STATUS
		  THEN	BEGIN
			CAB[CAB$L_STS]=.STATUS;
			RETURN .STATUS
			END
		END
	END;

!+
!	Do not allow recognition to automatically pass through
!	this point.
!-

IF .CAB[CAB$V_PFR]
  THEN	SIGNAL(HLP$_REPARSE);

!+
!	Go see what the field type is,
!	and then perform the appropriate action.
!	If there are multiple PABs linked together,
!	then we invoke them in succession, as necessary.
!	Move the first PAB of the chain into CAB$A_CURPAB
!	and make sure CAB$W_FLD_PTR is set up properly.
!-

CAB[CAB$A_CURPAB]=.CAB[CAB$A_PAB];
CAB[CAB$V_PHL]=FALSE;
ERR_STATUS=0;
DEPTH=0;

SAVE_EOL=.CAB[CAB$V_EOL];

WHILE 1 DO
	BEGIN

	BIND	CURPAB	= .CAB[CAB$A_CURPAB]	: $PAB_DECL;

	CAB[CAB$V_EOL]=.SAVE_EOL;

	STATUS=PARSE_PAB(CURPAB);
	CURPAB[PAB$L_ERR]=.STATUS;

	! THERE is a bug below, if in a set of choices the
	! first one produces help but the second one succeeds
	! perfectly.

	IF .STATUS
	  THEN	BEGIN
		ERR_STATUS=0;
		DEPTH=0;
		EXITLOOP
		END;

	!+
	!	If the status is HELP$_HELP, this means that
	!	help was generated.
	!-

	IF .STATUS EQL HLP$_HELP
	  THEN	BEGIN
		CAB[CAB$V_PHL]=TRUE
		END
	  ELSE	BEGIN

		!+
		!	If an error occurred, store it away in
		!	ERR_STATUS so that it can be returned to the
		!	user as the final error status.
		!	In case there are multiple PABs linked
		!	together, each PAB gets the error status
		!	for that PAB's parse, and the final error
		!	status consists of the error status from the PAB
		!	whose parse proceeded furthest along the command
		!	line.
		!-

		IF NOT .STATUS
		  THEN	BEGIN
			LOCAL	NEW_DEPTH;

			!+
			! If this error got further into the command line
			! than the previous error, then save this one
			! as the most likely candidate for the final
			! error status.
			!-

			NEW_DEPTH=.CAB[CAB$W_PTR]-.CAB[CAB$W_FLD_PTR];
			IF .NEW_DEPTH GTRU .DEPTH
			OR .ERR_STATUS EQL 0
			  THEN	BEGIN
				DEPTH=.NEW_DEPTH;
				ERR_STATUS=.STATUS
				END
			END;

!		IF .ERR_STATUS EQL 0 AND NOT .STATUS
!		  THEN	ERR_STATUS=.STATUS;
		END;

	!+
	! If there is another PAB in the chain, then invoke it.
	! Otherwise, we are done scanning the PABs, so we can exit this loop.
	!-

	IF .CURPAB[PAB$A_NXT] EQL 0
	  THEN	EXITLOOP
	  ELSE	BEGIN
		CAB[CAB$A_CURPAB]=.CURPAB[PAB$A_NXT];

		!+
		! Reset the pointer back to the beginning of this field.
		!-

		CAB[CAB$W_PTR]=.CAB[CAB$W_FLD_PTR];

		END;

	END;

!+
!	If any incremental help had occurred, then we must
!	reparse the line.
!-

IF .CAB[CAB$V_PHL]
  THEN	BEGIN
	!+
	! In case user is trapping this help, tell
	! him that it is through now.
	!-

	$OUT(%ASCID '',HLP$K_END_LINE);

	CAB[CAB$V_PHL]=FALSE;
	SIGNAL(HLP$_REPARSE)
	END;

!+
!	Copy the string matched into the atom buffer
!	(if one has been specified).
!	Copy verbatim with STR$COPY unless the UCA (Upcase
!	Atom Buffer) bit has been set in the CAB, in which
!	case copy using STR$UPCASE.
!	We copy into the atom buffer even if an error
!	occurred.  In that case, the atom buffer contains
!	the partial field matched in case the user
!	wishes to print it in his error message.
!-

IF .CAB[CAB$A_ATM] NEQ 0
  THEN	BEGIN
	BIND	BUF = .CAB[CAB$A_CBF]		: VECTOR[,BYTE],
		INI =  CAB[CAB$W_FLD_PTR]	: WORD,
		PTR =  CAB[CAB$W_PTR]		: WORD;

	CAB[CAB$W_ALN]=.PTR-.INI;
	IF .CAB[CAB$V_UCA]
	  THEN	BEGIN
		LOCAL	D : VECTOR[2];
		D[0]=.PTR-.INI;
		D[1]=BUF[.INI];
		STATUS=STR$UPCASE(.CAB[CAB$A_ATM],D)
		END
	  ELSE	STATUS=STR$COPY_R(	.CAB[CAB$A_ATM],
					%REF(.PTR-.INI),
					BUF[.INI]);
	IF NOT .STATUS AND .ERR_STATUS NEQ 0
	  THEN	ERR_STATUS=.STATUS;

	END;

!+
!	If an error had occurred, then we return the first one
!	to the user.
!	Field CAB$W_ERR_PTR is set to the current pointer
!	position so that a subsequent CTRL/H will work.
!	The pointer is then reset to the beginning of the
!	field in case the user wishes to manually reparse the field.
!-

IF .ERR_STATUS NEQ 0
  THEN	BEGIN
	CAB[CAB$L_STS]=.ERR_STATUS;
	CAB[CAB$W_ERR_PTR]=.CAB[CAB$W_PTR];
	CAB[CAB$W_PTR]=.CAB[CAB$W_FLD_PTR];
	RETURN .ERR_STATUS
	END;

$CONTINUE_RECOG;

IF NOT .PAB[PAB$V_NOS]
  THEN	$SPACES;

CAB[CAB$L_STS]=SS$_NORMAL;
RETURN SS$_NORMAL

END;
ROUTINE PARSE_PAB(P_PAB) =

BEGIN

!++
!
! FUNCTIONAL DESCRIPTION:
!
!	TBS
!
! FORMAL PARAMETERS:
!
!	P_PAB		Address of PAB.
!
! IMPLICIT INPUTS:
!
!	NONE
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! ROUTINE VALUE:
!
!	ERROR STATUS
!
! SIDE EFFECTS:
!
!	NONE
!
!--
BIND

	PAB	= .P_PAB	: $PAB_DECL;

LOCAL

	CHAR,
	STATUS;

EXTERNAL LITERAL

	HLP$_EXTRA,	! Extra characters at end of line
	HLP$_BADTYP;	! Bad type field in PAB

ENABLE

	LIB$$HLP$CATCHER;

PAB[PAB$A_SOS]=0;
PAB[PAB$L_ERR]=SS$_NORMAL;
PAB[PAB$L_STS]=0;
PAB[PAB$L_STV]=0;

SELECTONE .PAB[PAB$B_TYP] OF

	SET

	[HLP$K_KEYWORD,
	 HLP$K_QUALIFIER]:

		STATUS=LIB$$HANDLE_KEYWORD(PAB);

	[HLP$K_NUMBER]:

		STATUS=LIB$$HANDLE_NUMBER(PAB);

	[HLP$K_TOKEN]:

		STATUS=LIB$$HANDLE_TOKEN(PAB);

	[HLP$K_PARAM,HLP$K_DEVICE,HLP$K_FLOAT,HLP$K_TIME,HLP$K_UIC]:

		STATUS=LIB$$HANDLE_PARAM(PAB);

	[HLP$K_NAME,HLP$K_PROCESS,HLP$K_USERNAME,HLP$K_ACCOUNT,
	 HLP$K_NODENAME,HLP$K_LOGICAL_NAME]:

		STATUS=LIB$$HANDLE_NAME(PAB);

	[HLP$K_CHARACTER]:

		STATUS=LIB$$HANDLE_CHARACTER(PAB);

	[HLP$K_TEXT]:

		STATUS=LIB$$HANDLE_TEXT(PAB);

	[HLP$K_STRING]:

		STATUS=LIB$$HANDLE_STRING(PAB);

	[HLP$K_QUOTED_STRING]:

		STATUS=LIB$$HANDLE_QUOTED_STRING(PAB);

	[HLP$K_INPUT_FILE]:

		STATUS=LIB$$HANDLE_FILENAME(PAB);

	[HLP$K_OUTPUT_FILE]:

		STATUS=LIB$$HANDLE_FILENAME(PAB);

	[HLP$K_GENERAL_FILE]:

		STATUS=LIB$$HANDLE_FILENAME(PAB);

	[HLP$K_END]:

		BEGIN

		!+
		! Confirm that we are at the end of the line.
		!-

		CHAR=$CHAR(LIB$$END_HELP,LIB$$NO_RECOG);
		IF .CHAR EQL 0
		  THEN	STATUS=SS$_NORMAL
		  ELSE	STATUS=HLP$_EXTRA

		END;

	[OTHERWISE]:

		$PAB_ERR(HLP$_BADTYP,2,PAB,.PAB[PAB$B_TYP])
	TES;

RETURN .STATUS

END;
ROUTINE HLP$REPARSE_HANDLER(P_SIG,P_MECH,P_ENBL) =
BEGIN

!++
!
! FUNCTIONAL DESCRIPTION:
!
!	Standard VMS Condition handler.
!	Its only purpose is to catch the special signal known as
!	HLP$_REPARSE which means that we should back up to our
!	last prompt.  We restore the state of the world (as best
!	as we can) to what it was just before the last prompt,
!	and then issue the prompt again.  This is accomplished
!	by doing an unwind.
!
! FORMAL PARAMETERS:
!
!	P_SIG		Address of signal vector.
!
!	P_MECH		Address of mechanism vector.
!
!	P_ENBL		Address of enable vector.
!
! IMPLICIT INPUTS:
!
!
!
! IMPLICIT OUTPUTS:
!
!
!
! ROUTINE VALUE:
!
!	NONE
!
! COMPLETION CODES:
!
!	NONE
!
! SIDE EFFECTS:
!
!	NONE
!
!--
BIND

	SIG	=	.P_SIG		: BLOCK[,BYTE],	! Signal vector
	MECH	=	.P_MECH		: BLOCK[,BYTE];	! Mechanism vector

LOCAL

	STATUS;

EXTERNAL LITERAL

	HLP$_REPARSE;

!+
!	If the condition that is signalled is not the one we are interested
!	in (HLP$_REPARSE), then we do nothing but resignal the condition
!	so that some condition handler above us will take care of the
!	condition.
!-

IF .SIG[CHF$L_SIG_NAME] NEQ HLP$_REPARSE THEN RETURN SS$_RESIGNAL;

!+
!	Unwind, not to our caller, but to us.
!	This forces the call to the user's parsing routine
!	to return HLP$_REPARSE as a value.
!-

MECH[CHF$L_MCH_SAVR0]=.SIG[CHF$L_SIG_NAME];

STATUS=SETUNWIND(MECH[CHF$L_MCH_DEPTH]);
IF NOT .STATUS THEN SIGNAL(.STATUS);

RETURN SS$_RESIGNAL

END;
GLOBAL ROUTINE LIB$$HLP$CATCHER(P_SIG,P_MECH,P_ENBL) =
BEGIN

!++
!
! FUNCTIONAL DESCRIPTION:
!
!	Standard VMS Condition handler.
!	Its only purpose is to catch the special signal known as
!	HLP$_HELP and return it as a status value.
!
! FORMAL PARAMETERS:
!
!	P_SIG		Address of signal vector.
!
!	P_MECH		Address of mechanism vector.
!
!	P_ENBL		Address of enable vector.
!
! IMPLICIT INPUTS:
!
!
!
! IMPLICIT OUTPUTS:
!
!
!
! ROUTINE VALUE:
!
!	NONE
!
! COMPLETION CODES:
!
!	NONE
!
! SIDE EFFECTS:
!
!	NONE
!
!--
BIND

	SIG	=	.P_SIG		: BLOCK[,BYTE],	! Signal vector
	MECH	=	.P_MECH		: BLOCK[,BYTE];	! Mechanism vector

LOCAL

	STATUS;

EXTERNAL LITERAL

	HLP$_HELP;

!+
!	If the condition that is signalled is not the one we are interested
!	in (HLP$_HELP), then we do nothing but resignal the condition
!	so that some condition handler above us will take care of the
!	condition.
!-

IF .SIG[CHF$L_SIG_NAME] NEQ HLP$_HELP THEN RETURN SS$_RESIGNAL;

!+
!	Unwind, to our caller.
!	This forces the call to us
!	to return HLP$_HELP as a value.
!-

MECH[CHF$L_MCH_SAVR0]=.SIG[CHF$L_SIG_NAME];

STATUS=SETUNWIND();
IF NOT .STATUS THEN SIGNAL(.STATUS);

RETURN SS$_RESIGNAL

END;
GLOBAL ROUTINE LIB$$HLP$UCHAR(P_PAB,P_HELP_RTN,P_RECOG_RTN) =

BEGIN

!++
!
! FUNCTIONAL DESCRIPTION:
!
!	Gets the next character to be parsed, first
!	converting it to upper case
!
! FORMAL PARAMETERS:
!
!	P_PAB		Address of PAB
!
! IMPLICIT INPUTS:
!
!	CAB
!
! IMPLICIT OUTPUTS:
!
!	CAB$W_PTR is advanced if we are not at the end of the line
!
! ROUTINE VALUE:
!
!	the next character or 0 if there are no more characters
!
! SIDE EFFECTS:
!
!	NONE
!
!--
BIND

	PAB	= .P_PAB	: $PAB_DECL;

LOCAL

	CHAR;

CHAR=$CHAR(.P_HELP_RTN,.P_RECOG_RTN);
IF .CHAR GEQU %C'a' AND .CHAR LEQU %C'z'
  THEN	CHAR=.CHAR-%C'a'+%C'A';

RETURN .CHAR

END;
GLOBAL ROUTINE LIB$$HLP$CHAR(P_PAB,P_HELP_RTN,P_RECOG_RTN) =

BEGIN

!++
!
! FUNCTIONAL DESCRIPTION:
!
!	Gets the next character out of the command line.
!
! FORMAL PARAMETERS:
!
!	P_PAB		Address of PAB.
!
! IMPLICIT INPUTS:
!
!	CAB
!
! IMPLICIT OUTPUTS:
!
!	CAB$W_PTR	is advanced if we are not at the end of the line
!
! ROUTINE VALUE:
!
!	the next character or 0 if we were at the end of the line
!
! SIDE EFFECTS:
!
!	NONE
!
!--
LOCAL

	STATUS;

BIND

	PAB	= .P_PAB		: $PAB_DECL,
	CAB	= .PAB[PAB$A_CAB]	: $CAB_DECL,
	BUF	= .CAB[CAB$A_CBF]	: VECTOR[,BYTE],
	DEF	= .PAB[PAB$A_DEF]	: BLOCK[,BYTE],
	PTR	=  CAB[CAB$W_PTR]	: WORD,
	CLN	=  CAB[CAB$W_CLN]	: WORD,
	INI	=  CAB[CAB$W_FLD_PTR]	: WORD;

BIND ROUTINE

	HELP_RTN	= .P_HELP_RTN,
	RECOG_RTN	= .P_RECOG_RTN;

EXTERNAL LITERAL

	HLP$_HELP,
	HLP$_REPARSE;

!+
!	Is there a character left to be parsed in the buffer?
!	If there is, then we can immediately return it.
!	If not, then what are we to do?
!	There are one of 3 possible cases:
!
!	(a)	user hit RECOGNIZE and we need to perform recognition
!
!	(b)	user hit HELP and we need to perform incremental help
!
!	(c)	we are at the end of the user's command line.
!-

SELECTONE 1 OF

    SET

    [.PTR LSSU .CLN]:

	;

    [.CAB[CAB$B_QIO_TRM] EQL HLP$K_RECOGNIZE]:

	BEGIN

	!+
	! The user typed RECOGNIZE at this point.
	!-

	CAB[CAB$V_EOL] = 1;	! ***

	!+
	! If the current field has already been recognized,
	! then we merely return a 0.
	! This will force us on to the next field.
	!-

	IF .CAB[CAB$V_REC]
	  THEN	BEGIN
		CAB[CAB$V_REC]=FALSE;
		CAB[CAB$V_PFR]=TRUE;
		RETURN 0
		END;

	!+
	! If we are at the beginning of a field,
	! and if there is a default string,
	! then we recognize this default string.
	!-

	IF .PTR EQL .INI AND
	    DEF NEQ 0	 AND
	    .DEF[DSC$W_LENGTH] NEQ 0
	  THEN	BEGIN
		LIB$OUT_RECOG(DEF,CAB);
		STATUS=TRUE
		END
	  ELSE	BEGIN

		!+
		! If there is no recognition routine, then
		! we just return a 0.
		!-

		IF RECOG_RTN EQL 0
		  THEN	RETURN 0;

		!+
		! Go call the appropriate recognition routine.
		!-

		STATUS=RECOG_RTN(PAB);

		END;

	!+
	! Set the "current field has been recognized" bit if we
	! completed this field via recognition.
	!-

	CAB[CAB$V_REC]=.STATUS;

	!+
	! If no new characters have been recognized,
	! then go get some new characters from the terminal.
	! As a temporary inefficiency, we do this by
	! signalling HLP$_REPARSE.
	!-

	IF .PTR EQL .CLN
	  THEN	BEGIN
		IF .STATUS
		  THEN	RETURN 0
		  ELSE	BEGIN
			$OUT(UPLIT(1,UPLIT(7)),HLP$K_BELL_LINE);
			SIGNAL(HLP$_REPARSE)
			END
		END;

	!+
	! If recognition failed to complete, then
	! ring the terminal bell.
	!-

!	IF NOT .CAB[CAB$V_REC]
!	  THEN	$OUT(UPLIT(1,UPLIT(7)));

	END;

    [.CAB[CAB$B_QIO_TRM] EQL HLP$K_HELP]:

	BEGIN

	CAB[CAB$V_EOL] = 1;	! ***

	IF HELP_RTN EQL 0
	  THEN	RETURN 0;

	!+
	! The user typed HELP ("?") at this point.
	! Display the "?" and some spaces the first time through,
	! or the word OR on subsequent times.
	!-

	IF .CAB[CAB$V_PHL]
	  THEN	$OUT(%ASCID 'or ',    HLP$K_OR_LINE)
	  ELSE	$OUT(%ASCID '?     ',HLP$K_QMARK_LINE);

	!+
	! Do we have any user-supplied help text for this field?
	! If so, output it now.
	!-

!	IF .PAB[PAB$V_HLP]
!	  THEN	BEGIN
!		1
!		END;

	!+
	! Now go perform normal incremental help,
	! unless the user has explicitly suppressed it.
	!-

	IF NOT .PAB[PAB$V_SDH]
	  THEN	HELP_RTN(PAB);

	!+
	! Temporarily, this will always cause a reparse.
	!-

	SIGNAL(HLP$_HELP)

	END;

    [.PTR EQL .CLN]:

	BEGIN

	!+
	! If we are at the end of the line, return a 0.
	! Set the EOL flag to stop us from backing up.
	!-

	CAB[CAB$V_EOL]=1;
	RETURN 0
	END

    TES;

!+
! Get the character and return it.
! Advance the pointer.
!-

PTR=.PTR+1;
RETURN .BUF[.PTR-1]

END;
GLOBAL ROUTINE LIB$$HLP$BACKUP(P_CAB) : NOVALUE =

BEGIN

!++
!
! FUNCTIONAL DESCRIPTION:
!
!	Backs up the command line scan by one character position.
!
! FORMAL PARAMETERS:
!
!	P_CAB		Address of CAB
!
! IMPLICIT INPUTS:
!
!	CAB
!
! IMPLICIT OUTPUTS:
!
!	CAB$W_PTR	gets decremented unless we are at the end of the line.
!
! ROUTINE VALUE:
!
!	NONE
!
! SIDE EFFECTS:
!
!	NONE
!
!--
BIND

	CAB	= .P_CAB	: $CAB_DECL;

IF NOT .CAB[CAB$V_EOL]
  THEN	CAB[CAB$W_PTR]=.CAB[CAB$W_PTR]-1

END;
END
ELUDOM