Google
 

Trailing-Edge - PDP-10 Archives - LCG_Integration_Tools_Clearinghouse_T20_v7_30Apr86 - tools/recog3/hlprecog.b32
There are 2 other files named hlprecog.b32 in the archive. Click here to see a list.
MODULE LIB$RECOG (
		IDENT = 'V01A30'
		 ) =
BEGIN

!
!			  COPYRIGHT (C) 1982,1983 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:
!
!	Handles incremental help and recognition.
!
! ENVIRONMENT:
!
! AUTHOR: Stanley Rabinowitz,	CREATION DATE: 4-JUL-80
!
! MODIFIED BY: Stanley Rabinowitz
!
! V01A30	Stan	 6-Jul-1983	Allow radix 2-10,16 for numbers
!--
!LITERAL	HLP$K_KEYWORD_MAX = 150;	! ***

!
! TABLE OF CONTENTS:
!

FORWARD ROUTINE

	LIB$$KEYWORD_HELP	: NOVALUE,
	LIB$$KEYWORD_RECOG,
	LIB$$NUMBER_RECOG,
	LIB$$TOKEN_RECOG,
	LIB$$NO_RECOG,
	LIB$$END_HELP		: NOVALUE,
	LIB$$GENERAL_HELP	: NOVALUE,
	LIB$OUT_RECOG		: NOVALUE,
	LIB$$FILENAME_RECOG,
!	RECOG_DOCUMENT,
	FIRST_SCAN,
	SECOND_SCAN,
	LIB$$DIRECTORY_RECOG,
	LIB$$BREAK_RECOG	: NOVALUE;	! Prevent multi-field recognition

!
! INCLUDE FILES:
!

REQUIRE 'HLP.R32';

!
! MACROS:
!

!
! EQUATED SYMBOLS:
!

!
! OWN STORAGE:
!

!
! EXTERNAL REFERENCES:
!

OWN

	HLP$BUF_DESC	: VECTOR[2],	! Descriptor for command line buffer
	BUF		: VECTOR[200,BYTE],! Address of buffer to contain
	HANDLE;

!
! EXTERNAL REFERENCES:
!

EXTERNAL LITERAL

	HLP$_DEBUG_RECOG,	! May be signalled only if SET COMMAND/DEBUG
	HLP$_REPARSE;		! Signalled internally to cause a re-parse

EXTERNAL ROUTINE

	LIB$COLLECT_INITIALIZE,
	LIB$COLLECT_ABORT,
	LIB$COLLECT_STORE,
	LIB$COLLECT_OUTPUT;

ROUTINE	HLP$WILDMAN=1;
ROUTINE	HLP$RESTORE_STATE=1;

!ROUTINE HLP$OUTPUT(A,B,CHAN)=HLP$PUT_OUTPUT(.A,.B,.CHAN);

LITERAL

	FILNAM_SIZ	= NAM$C_MAXRSS,
	DFAULT_SIZ	= NAM$C_MAXRSS,
	EXPAND_SIZ	= NAM$C_MAXRSS,
	RESULT_SIZ	= NAM$C_MAXRSS,
	RELATE_SIZ	= NAM$C_MAXRSS;

OWN

	FILNAM_BUF	: VECTOR[FILNAM_SIZ,BYTE],
	DFAULT_BUF	: VECTOR[DFAULT_SIZ,BYTE],
	EXPAND_BUF	: VECTOR[EXPAND_SIZ,BYTE],
	RESULT_BUF	: VECTOR[RESULT_SIZ,BYTE],
	RELATE_BUF	: VECTOR[RELATE_SIZ,BYTE],
	RELATE_NAM	: $NAM(	RSA = RELATE_BUF,
				RSS = RELATE_SIZ) VOLATILE,
	RECNIZ_NAM	: $NAM(	ESA = EXPAND_BUF,
				ESS = EXPAND_SIZ,
				RSA = RESULT_BUF,
				RSS = RESULT_SIZ,
				RLF = RELATE_NAM) VOLATILE,
	RECNIZ_FAB	: $FAB(	NAM = RECNIZ_NAM) VOLATILE,
	DUMMY_RAB	: $RAB_DECL VOLATILE;

OWN

	CURKEY_LEN	: WORD,
	CURKEY_ADR,
	TOTAL_SIZE,
	MAX_SIZE,
	KEY_COUNT;

LITERAL

	MAX_WIDTH=132,	! maximum size of any one line
	INTERSPACE=2;	! space between keywords (if they fit on one line)

OWN

	QUALIFIER_MODE,	! 1 if qualifier field
	TERM_WIDTH,	! Actual width of line
	COLUMN,		! Number of characters in line buffer.
	TABPOS,		! Index number of next tabs top.
	FIELD_SIZE,	! Width of field in which to print keyword
	LINE		: VECTOR[MAX_WIDTH,BYTE];

OWN

	EXPAND_DESC	: VECTOR[2] INITIAL(EXPAND_SIZ,EXPAND_BUF),
	RESULT_DESC	: VECTOR[2] INITIAL(RESULT_SIZ,RESULT_BUF),
	EXCESS_DESC	: VECTOR[2],
	UNIQUE_DESC	: VECTOR[2];

EXTERNAL ROUTINE

	LIB$SCAN_KEYWORD_TABLE,
	LIB$ANALYZE_SDESC;
GLOBAL ROUTINE LIB$$KEYWORD_HELP(P_PAB) : NOVALUE =

BEGIN

!++
!
! FUNCTIONAL DESCRIPTION:
!
!	Called in response to the HELP key applied to a keyword parameter.
!
! FORMAL PARAMETERS:
!
!	P_PAB		Address of PAB.
!
! IMPLICIT INPUTS:
!
!	PAB
!	CAB
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! ROUTINE VALUE:
!
!	NONE
!
! SIDE EFFECTS:
!
!	NONE
!
!--
BIND

	PAB	= .P_PAB		: $PAB_DECL,
	CAB	= .PAB[PAB$A_CAB]	: $CAB_DECL,
	PTR	=  CAB[CAB$W_PTR]	: VOLATILE WORD,
	TYP	=  PAB[PAB$B_TYP]	: BYTE,
	INI	=  CAB[CAB$W_FLD_PTR]	: WORD,
	MTAB	= .PAB[PAB$L_ARG]	: VECTOR,
	BUF	= .CAB[CAB$A_CBF]	: VECTOR[,BYTE],
	HLP	= .PAB[PAB$A_HLP]	: BLOCK[,BYTE];

LOCAL

	STATUS,
	Q_NUMS,		! number of qualifiers
	NEW_STRUC,	! 1 if using new keyword table structure
	PART_SIZ,
	PRT		: VECTOR[2],
	KEYWORD_NAME;

EXTERNAL ROUTINE

	STR$UPCASE;

OWN

	UC_PRT	: BLOCK[8,BYTE]
		PRESET(	[DSC$W_LENGTH]	= 0,
			[DSC$B_CLASS]	= DSC$K_CLASS_D,
			[DSC$B_DTYPE]	= DSC$K_DTYPE_T,
			[DSC$A_POINTER]	= 0);

LITERAL

	MAX_EXPAND	= 25;	! Maximum width we allow any column
				! of qualifiers expand to

! Do we have a bug if a qualifier is wider than terminal width? ***
!+
! If this is a keyword field, then set QUALIFIER_MODE false.
! If this is a qualifier field, then set QUALIFIER_MODE true.
! We rely on the fact here that TRUE is 1 and FLASE is 0
! because we add this flag into the buffer pointer to bump
! the pointer by 1 when we are dealing with qualifiers.
!-

QUALIFIER_MODE=.TYP EQL HLP$K_QUALIFIER;

!+
!	Compute PART_SIZ, the length of the partial keyword
!	specified.  Note that this is one smaller if this is
!	a qualifier field; however it can't be smaller than 0.
!-

PART_SIZ=.PTR-.INI;
IF .QUALIFIER_MODE AND .PART_SIZ GTRU 0
  THEN	PART_SIZ=.PART_SIZ-1;

NEW_STRUC=.MTAB<31,1>;

IF .NEW_STRUC
  THEN	BEGIN
	BIND MT = MTAB : VECTOR[,BYTE];
	Q_NUMS=.MTAB[2];
	END
  ELSE	Q_NUMS=.MTAB[0]/2;

KEYWORD_NAME=(IF HLP NEQ 0 AND .HLP[DSC$W_LENGTH] NEQ 0
		 THEN HLP
		 ELSE IF .QUALIFIER_MODE
			THEN %ASCID 'qualifier'
			ELSE %ASCID 'keyword');

!+
! Scan the keyword table looking for keywords that match
! this partial keyword.  Count the number of keywords that
! match in KEY_COUNT.  While we are at it, also find the
! total size of the keywords (TOTAL_SIZE) and the largest size
! (MAX_SIZE).
!-

PRT[0]=.PART_SIZ;
PRT[1]=BUF[.INI]+.QUALIFIER_MODE;

!+
! Upcase the partial name found.
!-

STATUS=STR$UPCASE(UC_PRT,PRT);
IF NOT .STATUS THEN SIGNAL(.STATUS);

KEY_COUNT=0;
TOTAL_SIZE=0;
MAX_SIZE=0;

STATUS=LIB$SCAN_KEYWORD_TABLE(%REF(MTAB),UC_PRT,FIRST_SCAN,%REF(CAB));
IF NOT .STATUS THEN RETURN .STATUS;

!+
! Add in the fact that qualifiers output "/".
!-

IF .QUALIFIER_MODE
  THEN	TOTAL_SIZE=.TOTAL_SIZE+.KEY_COUNT;

CASE .KEY_COUNT FROM 0 TO 1 OF

    SET

    [0]:

	!+
	! Nothing matches.
	! Print KEYWORD, none match
	! unless there is precisely one legal keyword,
	! in which case print KEYWORD, does not match MUMBLE.
	!-

	BEGIN

	!+
	! Print the name of the entity, i.e., keyword, qualifier, guideword, etc.
	!-

	$OUT(.KEYWORD_NAME,HLP$K_ENTITY_LINE);

	!+
	! See if there is precisely one valid keyword.
	!-

	IF .Q_NUMS EQL 1
	  THEN	BEGIN
		$OUT(%ASCID ', does not match: ',HLP$K_SINGLE_LINE);
		IF .QUALIFIER_MODE
		  THEN	$OUT(%ASCID '/',HLP$K_SINGLE_LINE);
		IF .NEW_STRUC
		  THEN	BEGIN
			BIND QDESC = .MTAB[2];
			$OUT(QDESC,HLP$K_HELP)
			END
		  ELSE	$OUT(CSDESCR(.MTAB[1]),HLP$K_HELP)
		END
	  ELSE	$OUT(%ASCID ':  none match',HLP$K_NOMATCH_LINE);
	END;

    [1]:

	BEGIN
	LOCAL D : VECTOR[2];
	$OUT(.KEYWORD_NAME,HLP$K_ENTITY_LINE);
	IF .Q_NUMS EQL 1
	  THEN	$OUT(%ASCID ': ',HLP$K_SINGLE_LINE)
	  ELSE	$OUT(%ASCID ', only possibility is:  ',HLP$K_SINGLE_LINE);
	IF .QUALIFIER_MODE
	  THEN	$OUT(%ASCID '/',HLP$K_SINGLE_LINE);
	D[0]=.CURKEY_LEN;
	D[1]=.CURKEY_ADR;
	$OUT(D,HLP$K_HELP)
	END;

    [OUTRANGE]:

	BEGIN
	LOCAL	D : VECTOR[2];
	TERM_WIDTH=.CAB[CAB$W_WIDTH];
	IF .TERM_WIDTH EQL 0
	  THEN	TERM_WIDTH=80;	! Somehow, if we missed it.

	!+
	! Make sure the width isn't too large.
	!-

	TERM_WIDTH=MIN(.TERM_WIDTH,MAX_WIDTH);
	$OUT(.KEYWORD_NAME,HLP$K_ENTITY_LINE);
	$OUT(%ASCID ', one of the following:',HLP$K_PREFACE_LINE);

	!+
	! Set the internal COLUMN counter to 0 to make sure
	! that the first keyword starts at position 0.
	!-

	COLUMN=0;
	CH$FILL(%C' ',.TERM_WIDTH,LINE);

	!+
	! Field width is maximum size plus 1, but not more than MAX_EXPAND.
	!-

	FIELD_SIZE=MIN(.MAX_SIZE+.QUALIFIER_MODE+1,MAX_EXPAND);

	!+
	! If all the keywords can fit on a single line, equally spaced,
	! with INTERSPACE spaces between them, then set FIELD_SIZE to 0.
	!-

	IF .TOTAL_SIZE+INTERSPACE*(.KEY_COUNT-1) LEQ .TERM_WIDTH
	  THEN	FIELD_SIZE=0;

	STATUS=LIB$SCAN_KEYWORD_TABLE(%REF(MTAB),UC_PRT,SECOND_SCAN,%REF(CAB));
	IF NOT .STATUS THEN RETURN .STATUS;

	D[0] = .COLUMN;
	D[1] =  LINE;
	$OUT(D,HLP$K_HELP_LINE)
	END;

    TES;

END;
ROUTINE FIRST_SCAN(P_NAME_DESC,P_KIT,P_CTX) =

BEGIN

!++
!
! FUNCTIONAL DESCRIPTION:
!
!	Action routine for keyword scan to find the
!	number of keywords that match and to figure
!	out the length of the largest keyword and the total size
!	of the keywords.
!
! FORMAL PARAMETERS:
!
!	P_NAME_DESC	Address of descriptor for keyword
!
!	P_KIT		Address of keyword item table
!
!	P_CTX		Address of context longword
!			Points to CAB.
!
! IMPLICIT INPUTS:
!
!	MAX_SIZE	Gets updated if this keyword has a new maximum size
!
!	TOTAL_SIZE	Gets incremented to include the number of
!			characters in this keyword.
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! ROUTINE VALUE:
!
!	SS$_NORMAL
!
! SIDE EFFECTS:
!
!	NONE
!
!--
LOCAL

	EQUAL_SIZE,
	NEG_SIZE,
	STATUS;

BIND

	P_CAB	= .P_CTX,
	CAB	= .P_CAB	: $CAB_DECL,
	KIT	= .P_KIT	: $KIT_DECL;

IF KIT EQL 0
  THEN	BEGIN
	NEG_SIZE=0;
	EQUAL_SIZE=0
	END
  ELSE	BEGIN
	IF .CAB[CAB$W_PTR] LEQ .CAB[CAB$W_FLD_PTR]+.QUALIFIER_MODE
	  THEN	NEG_SIZE=4*.KIT[KIT$V_NEG]
	  ELSE	NEG_SIZE=0;
	EQUAL_SIZE=.KIT[KIT$V_VAL];
	END;

!+
! Do a preliminary scan over the keywords to find the largest one
! and also to see if they will all fit on one line.
!-

STATUS=LIB$ANALYZE_SDESC(.P_NAME_DESC,CURKEY_LEN,CURKEY_ADR);
IF NOT .STATUS THEN RETURN .STATUS;

KEY_COUNT=.KEY_COUNT+1;

TOTAL_SIZE = .TOTAL_SIZE+.CURKEY_LEN+.EQUAL_SIZE+.NEG_SIZE;
MAX_SIZE = MAX(.MAX_SIZE,.CURKEY_LEN+.EQUAL_SIZE+.NEG_SIZE);

RETURN SS$_NORMAL

END;
ROUTINE SECOND_SCAN(P_NAME_DESC,P_KIT,P_CTX) =

BEGIN

!++
!
! FUNCTIONAL DESCRIPTION:
!
!	Action routine for keyword scan used to print the
!	keywords that have matched.
!
! FORMAL PARAMETERS:
!
!	P_NAME_DESC	Address of descriptor for kwyword
!
!	P_KIT		Address of keyword item table
!
!	P_CTX		Address of context longword
!			In this case, that contains address of CAB.
!
! IMPLICIT INPUTS:
!
!	QUALIFIER_MODE
!
!	FIELD_SIZE
!
!	COLUMN
!
!	TABPOS
!
!	TERM_WIDTH
!
!	LINE
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! ROUTINE VALUE:
!
!	SS$_NORMAL
!
! SIDE EFFECTS:
!
!	NONE
!
!--
BIND

	KIT	= .P_KIT	: $KIT_DECL,
	P_CAB	= .P_CTX,
	CAB	= .P_CAB	: $CAB_DECL;

LOCAL

	EQUAL_SIZE,
	NEG_SIZE,
	D		: VECTOR[2],
	STATUS;

IF KIT EQL 0
  THEN	BEGIN
	NEG_SIZE=0;
	EQUAL_SIZE=0
	END
  ELSE	BEGIN
	IF .CAB[CAB$W_PTR] LEQ .CAB[CAB$W_FLD_PTR]+.QUALIFIER_MODE
	  THEN	NEG_SIZE=4*.KIT[KIT$V_NEG]
	  ELSE	NEG_SIZE=0;
	EQUAL_SIZE=.KIT[KIT$V_VAL];
	END;

STATUS=LIB$ANALYZE_SDESC(.P_NAME_DESC,CURKEY_LEN,CURKEY_ADR);
IF NOT .STATUS THEN RETURN .STATUS;

!+
! Figure out where next logical tab stop is.
! (where the next field starts).
! That is where next keyword should be put.
!-

TABPOS=	BEGIN
	IF .COLUMN EQL 0
	  THEN	0
	  ELSE	IF .FIELD_SIZE NEQ 0
		  THEN	.COLUMN+.FIELD_SIZE-
			(.COLUMN MOD .FIELD_SIZE)
		  ELSE	.COLUMN+INTERSPACE
	END;

!+
! Make sure that the next keyword will fit.
! If it would overflow the terminal width,
! then output this line and start a new line.
! Note that the column positions are labelled
! from 0 to .TERM_WIDTH-1.
!-

! Note: the next IF can be replaced by the
!	following IF should you want to ensure
!	that the final column of help keywords
!	not have any blank holes due to items
!	in THAT slot that might be too big.
!	This is entirely an esthetics problem,
!	and after trying it both ways, I chose
!	the way you see as the better.	- STAN -
!	IF .TABPOS+MAX(.CURKEY_LEN+.QUALIFIER_MODE+.EQUAL_SIZE,
!		.FIELD_SIZE) GEQ .TERM_WIDTH

IF .TABPOS+.CURKEY_LEN+.QUALIFIER_MODE+.EQUAL_SIZE+.NEG_SIZE GEQ .TERM_WIDTH
  THEN	BEGIN

	!+
	! Output the current line and end it with CRLF.
	!-

	D[0]=.COLUMN;
	D[1]=LINE;
	$OUT(D,HLP$K_HELP_LINE);
	CH$FILL(%C' ',.TERM_WIDTH,LINE);
	TABPOS=0
	END;

IF .QUALIFIER_MODE
  THEN	BEGIN
	BIND CH = LINE[.TABPOS] : BYTE;
	CH='/'
	END;

IF .NEG_SIZE NEQ 0
  THEN	CH$MOVE(4,UPLIT('[NO]'),LINE[.TABPOS]+.QUALIFIER_MODE);

CH$MOVE(.CURKEY_LEN,.CURKEY_ADR,
	LINE[.TABPOS]+.QUALIFIER_MODE+.NEG_SIZE);

!+
!	If a value is legal, then output a "=".
!-

COLUMN=.TABPOS+.CURKEY_LEN+.QUALIFIER_MODE+.NEG_SIZE;
IF .EQUAL_SIZE
  THEN	BEGIN
	BIND CH=LINE[.COLUMN] : BYTE;
	CH='=';
	COLUMN=.COLUMN+1;
	END;

RETURN SS$_NORMAL

END;
GLOBAL ROUTINE LIB$$KEYWORD_RECOG(P_PAB) =

BEGIN

!++
!
! FUNCTIONAL DESCRIPTION:
!
!	Handles recognition of keywords.
!
! FORMAL PARAMETERS:
!
!	TBS
!
! IMPLICIT INPUTS:
!
!	NONE
!
!	PAB type tells us whether this is a keyword or qualifier field.
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! ROUTINE VALUE:
!
!	TRUE		complete recognition has been accomplished
!
!	FALSE		no recognition or partial recognition
!			has been accomplished.
!
! SIDE EFFECTS:
!
!	NONE
!
!--
LOCAL

	QUALIFIER_MODE,	! 1 means qualifier field, 0 means keyword field
	NEW_STRUC,	! TRUE if new keyword table structure
	KWS,
	STATUS,
	D		: VECTOR[2],
	FULL_LEN	: WORD,
	FULL_VAL;

OWN

	SUB_COUNT;

BIND

	PAB	= .P_PAB		: $PAB_DECL,
	CAB	= .PAB[PAB$A_CAB]	: $CAB_DECL,
	PTR	=  CAB[CAB$W_PTR]	: WORD,
	TYP	=  PAB[PAB$B_TYP]	: BYTE,
	KTAB	= .PAB[PAB$L_ARG]	: VECTOR,
	BUF	= .CAB[CAB$A_CBF]	: VECTOR[,BYTE],
	INI	=  CAB[CAB$W_FLD_PTR]	: WORD;

OWN

	UC_DESC	: BLOCK[8,BYTE]
		PRESET(	[DSC$W_LENGTH]	= 0,
			[DSC$B_CLASS]	= DSC$K_CLASS_D,
			[DSC$B_DTYPE]	= DSC$K_DTYPE_T,
			[DSC$A_POINTER]	= 0),

	FULL_D		: BLOCK[8,BYTE] PRESET(

			[DSC$W_LENGTH]	= 0,
			[DSC$B_DTYPE]	= DSC$K_DTYPE_T,
			[DSC$B_CLASS]	= DSC$K_CLASS_D,
			[DSC$A_POINTER]	= 0);

EXTERNAL ROUTINE

	STR$UPCASE,
	LIB$SCAN_KEYWORD_TABLE,
	LIB$LOOKUP_KEYWORD;

ROUTINE ACTION(P_NAME_DESC,P_KWD,P_KWS) =
    BEGIN
    BIND KWS = .P_KWS;
    LOCAL LEN:WORD, ADR, STATUS;
    STATUS=LIB$ANALYZE_SDESC(.P_NAME_DESC,LEN,ADR,KWS);
    IF NOT .STATUS THEN RETURN .STATUS;
    STATUS=LIB$COLLECT_STORE(HANDLE,.LEN-.KWS,.ADR+.KWS);
    IF NOT .STATUS THEN RETURN .STATUS;
    SUB_COUNT=.SUB_COUNT+1;
    RETURN SS$_NORMAL
    END;
QUALIFIER_MODE = .TYP EQL HLP$K_QUALIFIER;

!+
! We do not permit recognizing the slash in a qualifier field.
!-

IF .PTR EQL .INI AND .QUALIFIER_MODE
  THEN	RETURN FALSE;

!+
! The keyword size is one smaller for qualifiers.
!-

KWS=.PTR-.INI-.QUALIFIER_MODE;

!+
!	Special case:
!	Before we do anything else, we see if this is an exact match.
!	In that case, we claim that full recognition has occurred.
!-

D[0]=.KWS;
D[1]=BUF[.INI]+.QUALIFIER_MODE;

!+
! Upcase the partial name.
!-

STATUS=STR$UPCASE(UC_DESC,D);
IF NOT .STATUS THEN SIGNAL(.STATUS);

STATUS=LIB$LOOKUP_KEYWORD(UC_DESC,KTAB,FULL_VAL,FULL_D,FULL_LEN);
IF .STATUS EQL SS$_NORMAL AND .FULL_LEN EQL .KWS
  THEN	RETURN TRUE;

NEW_STRUC=.KTAB<31,1>;

STATUS=LIB$COLLECT_INITIALIZE(HANDLE);
IF NOT .STATUS THEN RETURN .STATUS;
SUB_COUNT=0;

LIB$SCAN_KEYWORD_TABLE(%REF(KTAB),UC_DESC,ACTION,KWS);

RETURN	LIB$COLLECT_OUTPUT(HANDLE,LIB$OUT_RECOG,CAB)

END;
GLOBAL ROUTINE LIB$$GENERAL_HELP(P_PAB) : NOVALUE =

BEGIN

!++
!
! FUNCTIONAL DESCRIPTION:
!
!	Called in response to the HELP key applied to a keyword parameter.
!
! FORMAL PARAMETERS:
!
!	Address of CAB.
!
! IMPLICIT INPUTS:
!
!	NONE
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! ROUTINE VALUE:
!
!	NONE
!
! SIDE EFFECTS:
!
!	NONE
!
!--
BIND

	PAB	= .P_PAB		: $PAB_DECL,
	CAB	= .PAB[PAB$A_CAB]	: $CAB_DECL,
	HLP	= .PAB[PAB$A_HLP]	: BLOCK[,BYTE],
	SOS	= .PAB[PAB$A_SOS]	: BLOCK[,BYTE];

LOCAL

	NAME;

NAME	= (IF HLP NEQ 0 AND .HLP[DSC$W_LENGTH] NEQ 0
		 THEN HLP
		 ELSE IF SOS NEQ 0
			THEN SOS
			ELSE %ASCID 'number');

$OUT(.NAME,HLP$K_HELP_LINE);
1
END;
GLOBAL ROUTINE LIB$$END_HELP(P_PAB) : NOVALUE =

BEGIN

!++
!
! FUNCTIONAL DESCRIPTION:
!
!	Called in response to the HELP key applied
!	at the end of the line.
!
! FORMAL PARAMETERS:
!
!	Address of PAB.
!
! IMPLICIT INPUTS:
!
!	NONE
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! ROUTINE VALUE:
!
!	NONE
!
! SIDE EFFECTS:
!
!	NONE
!
!--
BIND

	PAB	= .P_PAB		: $PAB_DECL,
	CAB	= .PAB[PAB$A_CAB]	: $CAB_DECL,
	HLP	= .PAB[PAB$A_HLP]	: BLOCK[,BYTE];

IF HLP EQL 0 OR .HLP[DSC$W_LENGTH] EQL 0
  THEN	$OUT(%ASCID 'confirm with carriage return',HLP$K_HELP_LINE)
  ELSE	$OUT(.PAB[PAB$A_HLP],HLP$K_HELP_LINE)

END;
GLOBAL ROUTINE LIB$OUT_RECOG(P_RECOG_DESC,P_CAB) : NOVALUE =

BEGIN

!++
!
! FUNCTIONAL DESCRIPTION:
!
!	Actually outputs the recognized characters to the screen
!	and inserts them into the command line as if the user typed them.
!
! FORMAL PARAMETERS:
!
!	P_RECOG_DESC	Address of descriptor for recognized characters.
!
!	P_CAB		Address of CAB
!
! IMPLICIT INPUTS:
!
!	PTR
!
! IMPLICIT OUTPUTS:
!
!	PTR
!
! ROUTINE VALUE:
!
!	NONE
!
! SIDE EFFECTS:
!
!	NONE
!
!--
BIND

	CAB		= .P_CAB		: $CAB_DECL,
	RECOG_DESC	= .P_RECOG_DESC		: BLOCK[,BYTE],
	BUF		= .CAB[CAB$A_CBF]	: VECTOR[,BYTE],
	PTR		=  CAB[CAB$W_PTR]	: WORD,
	CLN		=  CAB[CAB$W_CLN]	: WORD;

LOCAL

	LC_FLAG,				! 1 if want to do recognition
						! in lower case.
	NEW_DESC	: VECTOR[2];

!+
!	Move the new characters into the command line.
!-

CH$MOVE(.RECOG_DESC[DSC$W_LENGTH],.RECOG_DESC[DSC$A_POINTER],BUF[.PTR]);
CLN=.CLN+.RECOG_DESC[DSC$W_LENGTH];

!+
!	Form a new descriptor for these characters, since
!	we don't want to violate the user's descriptor or data.
!-

NEW_DESC[0]=.RECOG_DESC[DSC$W_LENGTH];
NEW_DESC[1]=BUF[.PTR];

!+
!	Convert the recognized characters to lower case if the
!	"previous character" was lower case.
!	Actually we scan backwards looking for an alphabetic.
!	If this alphabetic was lower case, then we recognize
!	in lower case.  Space, tab, slash, and comma abort the scan.
!-

LC_FLAG=FALSE;
DECR I FROM .PTR-1 TO 0 DO
	SELECTONE .BUF[.I] OF
		SET
		['a' TO 'z']:	BEGIN
				LC_FLAG=TRUE;
				EXITLOOP
				END;

		['A' TO 'Z']:	EXITLOOP;

		[' ',9,'/',',']:EXITLOOP

		TES;
IF .LC_FLAG
  THEN	INCR I FROM .PTR TO .PTR+.NEW_DESC[0]-1 DO
		IF .BUF[.I] GEQU %C'A' AND .BUF[.I] LEQU %C'Z'
		  THEN	BUF[.I]=.BUF[.I]-%C'A'+%C'a';

!+
!	Output the recognized characters to the screen.
!-

$OUT(NEW_DESC,HLP$K_RECOG_LINE)

END;
GLOBAL ROUTINE LIB$$FILENAME_RECOG(P_PAB) =

BEGIN

!++
!
! FUNCTIONAL DESCRIPTION:
!
!	Handles recognition of RMS filenames.
!
! FORMAL PARAMETERS:
!
!	P_PAB		Address of PAB.
!
! IMPLICIT INPUTS:
!
!	PAB[PAB$L_ARG]		contains address of a vector of two longwords:
!
!					1.	address of descriptor
!						for default filespec
!					2.	address of related name block
!
!	PAB[PAB$A_CAB]		contains address of CAB
!
!	CAB[CAB$A_FLD_PTR]	Offset to start of partial filespecification
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! ROUTINE VALUE:
!
!	TRUE		complete recognition has been accomplished
!
!	FALSE		no recognition or partial recognition
!			has been accomplished.
!
! SIDE EFFECTS:
!
!	NONE
!
!--
LOCAL

	RESCAN,
	PNS,
	PART_SIZE,
	STATUS;

BIND

	PAB		= .P_PAB		: $PAB_DECL,
	CAB		= .PAB[PAB$A_CAB]	: $CAB_DECL,
	PTR		=  CAB[CAB$W_PTR]	: WORD,
	VEC		= .PAB[PAB$L_ARG]	: VECTOR,
	BUF		= .CAB[CAB$A_CBF]	: VECTOR[,BYTE],
	INI		=  CAB[CAB$W_FLD_PTR]	: WORD;

OWN

	PRT		: VECTOR[NAM$C_MAXRSS,BYTE];

BIND

	PNM		=  BUF[.INI]			: VECTOR[,BYTE];

OWN

	QUOTE_COUNT,	! Number of quotes in user's filespecification
	NAM_SIZE;	! Number of characters in filename that user typed

!EXTERNAL ROUTINE
!
!	HLP$FAB_CLOSE_HANDLER;
!
!ENABLE	HLP$FAB_CLOSE_HANDLER(RECNIZ_FAB,DUMMY_RAB);

!+
!	Enter a default name into the FAB if the user specified one.
!-

RECNIZ_FAB[FAB$B_DNS]=0;
IF VEC NEQ 0
  THEN	IF .VEC[0] NEQ 0
	  THEN	BEGIN
		BIND DNM_DESC = .VEC[0] : BLOCK[,BYTE];
		RECNIZ_FAB[FAB$B_DNS]=.DNM_DESC[DSC$W_LENGTH];
		RECNIZ_FAB[FAB$L_DNA]=.DNM_DESC[DSC$A_POINTER]
		END;

!+
! Compute length of specified filespec.
!-

PNS=.PTR-.INI;
PART_SIZE = .PNS;
RELATE_NAM[NAM$B_RSL] = 0;

!+
! Scan the filespec backwards from the end, looking for special characters
! such as :, ", [, ], <, and >.
! The object of this search is to find the beginning of the filename
! part of the user's filespecification.
! Also, if we discover that we are inside an access control string,
! NODE"username password"::, or a remote node filespec, NODE::"filespec",
! then we can avoid trying to perform recognition.
! If we discover that we are inside a directory specification, [DIR],
! then we perform a different kind of recognition completely (temporarily
! not implemented).
! If we discover that we are inside a version number, then we temporarily
! perform no recognition.
!-

!+
! If there are an odd number of quotes in the user's filespecification,
! then we are in the middle of an access control string or
! foreign node filespec.  In that case, we perform no recognition.
!-

QUOTE_COUNT=0;
INCR SCAN_PTR FROM 0 TO .PNS-1 DO
	IF .PNM[.SCAN_PTR] EQL %C'"' THEN QUOTE_COUNT=.QUOTE_COUNT+1;
IF .QUOTE_COUNT THEN RETURN FALSE;

RESCAN=FALSE;
NAM_SIZE=0;
DECR SCAN_PTR FROM .PNS-1 TO 0 DO
	BEGIN
	SELECTONE .PNM[.SCAN_PTR] OF
	    SET

	    [%C':',%C']',%C'>']:

		EXITLOOP;

	    [%C'[',%C'<']:

		BEGIN
		LOCAL	DIR_STATUS;
		LOCAL	U_BUF	: VECTOR[NAM$C_MAXRSS,BYTE],
			U_DESC	: VECTOR[2],
			U_LEN;	! Can't bind this to U_DESC[0] because of side effects
		U_DESC[0]=NAM$C_MAXRSS;
		U_DESC[1]=U_BUF;

		!+
		! Go perform directory recognition on this partial
		! filespecification.
		!-

		DIR_STATUS=LIB$$DIRECTORY_RECOG(PAB,U_DESC,U_LEN);
		U_DESC[0]=.U_LEN;
		IF .U_DESC[0] NEQ 0
		  THEN	LIB$OUT_RECOG(U_DESC,CAB);

		!+
		! If no or only partial recognition has occurred,
		! then that's it for us.
		!-

		IF NOT .STATUS THEN RETURN .STATUS;

		RESCAN=TRUE;
		EXITLOOP;

		END;

	    [%C';']:

			;
!		IF .SCAN_PTR NEQ .PNS-1
!		  THEN	RETURN FALSE;	! not yet in

	    TES;

	NAM_SIZE=.NAM_SIZE+1
	END;

!+
! If directory recognition occurred, we have to rescan the
! updated specification.
!-

IF .RESCAN
  THEN	BEGIN
	PNS=.PTR-.INI;
	PART_SIZE=.PNS;
	NAM_SIZE=0;
	DECR SCAN_PTR FROM .PNS-1 TO 0 DO
		BEGIN
		SELECTONE .PNM[.SCAN_PTR] OF
			SET
			[%C':',%C']',%C'>']:	EXITLOOP;
			[%C'[',%C'<']:		RETURN FALSE;
!			[%C';']:		IF .SCAN_PTR NEQ .PNS-1
!						  THEN	RETURN FALSE;
			TES;
		NAM_SIZE=.NAM_SIZE+1
		END;
	END;

!+
! At this point, we have figured out how many characters
! are in the portion of the filename specified after the "]"
! that ends the directory name.  This is in NAM_SIZE.
! We now do a preliminary parse on the partial name so far.
! We will save a lot of computation if we find this is invalid.
! We also need to know if the filespec ends with a 9-character
! filename or a 3-character file type, because if so, we have to
! suppress appending the "*" to it in the next step, because
! of an RMS mis-feature. (RMS balks at a filespec of the form
! abcdefghi*.jkl or abc.def* because there appear to be too many
! characters, even though the "*" is permitted to match the null string.)
!-

RECNIZ_FAB[FAB$W_IFI] = 0;

RECNIZ_FAB[FAB$L_FNA]=BUF[.INI];
RECNIZ_FAB[FAB$B_FNS]=.PART_SIZE;
RECNIZ_FAB[FAB$L_NAM]=RECNIZ_NAM;

RECNIZ_NAM[NAM$B_ESS]=EXPAND_SIZ;
RECNIZ_NAM[NAM$L_ESA]=EXPAND_BUF;
RECNIZ_NAM[NAM$L_RLF]=0;

STATUS=$PARSE(FAB=RECNIZ_FAB);
IF NOT .STATUS THEN RETURN .STATUS;

!+
! If the preliminary parse was successful and we found any
! wildcards in the partial spec so far, then we want to
! "recognize" the expanded name but no do any lookups (searches).
! Also, if wildcards are prohibited in this field, then we
! clearly refuse to do recognition.
!-

IF .RECNIZ_NAM[NAM$V_WILDCARD] OR .RECNIZ_NAM[NAM$V_WILD_DIR]
  THEN	BEGIN

	LOCAL	D	: VECTOR[2];

	!+
	! Do no recognition if wildcards appear but are not permitted.
	!-

	IF .PAB[PAB$V_NOWLD]
	  THEN	RETURN FALSE;

	!+
	! Recognize the remainder of the expanded name.
	!-

	D[0]=.RECNIZ_NAM[NAM$B_NAME]
		+.RECNIZ_NAM[NAM$B_TYPE]
		+.RECNIZ_NAM[NAM$B_VER]
		-.NAM_SIZE;
	D[1]=.RECNIZ_NAM[NAM$L_NAME]+.NAM_SIZE;

	LIB$OUT_RECOG(D,CAB);

	RETURN SS$_NORMAL

	END;

!+
! Copy the partial filespecification to our own buffer,
! since we may have to modify it.
! (We typically add a "*" to the end which could be bad
! if the original buffer was too small.)
!-

CH$MOVE(.PART_SIZE,BUF[.INI],PRT);

!+
! Put a "*" at the end of the filespec.
! However, we omit this step if the partial specification
! ends with a nine-character filename or a 3-character filetype
! or a non-empty version number.  This is because RMS is unhappy
! with such specifications.
!-

IF .PRT[.PNS-1] EQL %C'.'
OR .PRT[.PNS-1] EQL %C';'
OR (.RECNIZ_NAM[NAM$B_NAME] NEQ 9 AND
   NOT (.RECNIZ_NAM[NAM$V_EXP_VER] OR .RECNIZ_NAM[NAM$V_EXP_TYPE]))
OR (.RECNIZ_NAM[NAM$B_TYPE] NEQ 4 AND
    .RECNIZ_NAM[NAM$V_EXP_TYPE]   AND
    NOT .RECNIZ_NAM[NAM$V_EXP_VER])
  THEN	BEGIN
	PRT[.PNS]=%C'*';
	PART_SIZE=.PART_SIZE+1;
	END;

!+
! Have to set the default file specification string.
! During recognition, the default file specification string
! that we use is built up from the user's default file
! specification string (DNM) by further applying the defaults "*.*;0".
! We do this via a preliminary call to $PARSE, using the same FAB.
! The desired new default filespecification is left in DFAULT_BUF.
!-

!CH$FILL(%C' ',DFAULT_SIZ,DFAULT_BUF);

!+
! In case we CTRL/C'ed out of a prior operation on this FAB
! and left the FAB open, we 0 out the IFI now.
! Herb jacobs assures me that even if there is an operation
! still pending on this FAB, when RMS completes it will
! notice that the FAB is no longer valid (IFI wrong) and will
! abort the operation.
!-

RECNIZ_FAB[FAB$W_IFI] = 0;

RECNIZ_FAB[FAB$L_FNA] = .RECNIZ_FAB[FAB$L_DNA];
RECNIZ_FAB[FAB$B_FNS] = .RECNIZ_FAB[FAB$B_DNS];
RECNIZ_FAB[FAB$L_DNA] = UPLIT('*.*;0');
RECNIZ_FAB[FAB$B_DNS] = %CHARCOUNT('*.*;0');
RECNIZ_FAB[FAB$L_NAM] = RECNIZ_NAM;
RECNIZ_NAM[NAM$L_ESA] = DFAULT_BUF;
RECNIZ_NAM[NAM$B_ESS] = DFAULT_SIZ;
RECNIZ_NAM[NAM$L_RLF] = 0;

STATUS=$PARSE(FAB=RECNIZ_FAB);
IF NOT .STATUS THEN RETURN FALSE;

RECNIZ_FAB[FAB$L_DNA] = .RECNIZ_NAM[NAM$L_ESA];
RECNIZ_FAB[FAB$B_DNS] = .RECNIZ_NAM[NAM$B_ESL];

!+
! Perform a $PARSE to set up the wildcard context.
!-

RECNIZ_FAB[FAB$L_FNA] =  PRT;
RECNIZ_FAB[FAB$B_FNS] = .PART_SIZE;

RECNIZ_NAM[NAM$L_ESA] = EXPAND_BUF;
RECNIZ_NAM[NAM$B_ESS] = EXPAND_SIZ;

STATUS=$PARSE(FAB=RECNIZ_FAB);
IF NOT .STATUS THEN RETURN .STATUS;

!+
! Perform a search.
!-

STATUS=LIB$COLLECT_INITIALIZE(HANDLE);
IF NOT .STATUS THEN RETURN .STATUS;

WHILE 1 DO
	BEGIN
	STATUS=$SEARCH(FAB=RECNIZ_FAB);
	SELECTONE .STATUS OF

	    SET

	    [RMS$_NORMAL]:

		BEGIN

		!+
		! We have now found a resultant string.
		!-

		RESULT_DESC[0]=.RECNIZ_NAM[NAM$B_RSL];

		!+
		! Make sure there is a 0 at the end of this buffer.
		!-

		RESULT_BUF[.RESULT_DESC[0]]=0;

		!+
		! Let us create a descriptor for the additional string
		! after the characters that the user has typed.
		! We count off characters forward from the final "]"
		! in the resultant string.
		!-

		DECR SCAN_PTR FROM .RESULT_DESC[0]-1 TO 0 DO
			IF .RESULT_BUF[.SCAN_PTR] EQL %C']'
			OR .RESULT_BUF[.SCAN_PTR] EQL %C'>'
			  THEN	BEGIN
				EXCESS_DESC[0]=
				    .RESULT_DESC[0]-.SCAN_PTR-1-.NAM_SIZE;
				EXCESS_DESC[1]=
				     RESULT_BUF[.SCAN_PTR+1+.NAM_SIZE];
				EXITLOOP
				END;
		STATUS=LIB$COLLECT_STORE(HANDLE,.EXCESS_DESC[0],.EXCESS_DESC[1]);
		IF NOT .STATUS THEN EXITLOOP
		END;

	    [RMS$_FNF]:

		EXITLOOP;

	    [RMS$_NMF]:

		EXITLOOP;

	    [OTHERWISE]:

		BEGIN
		LIB$COLLECT_ABORT(HANDLE);
		RETURN FALSE
		END

	    TES;

	END;

RETURN LIB$COLLECT_OUTPUT(HANDLE,LIB$OUT_RECOG,CAB)

END;
ROUTINE LIB$$DIRECTORY_RECOG(P_PAB,P_UNIQUE_DESC,P_UNIQUE_LEN) =

BEGIN

!++
!
! FUNCTIONAL DESCRIPTION:
!
!	Handles recognition of RMS input directory specifications.
!
!	These can include node names and device names.
!
!	The directory specification itself begins with either
!	a "[" or a "<" and may contain subdirectories
!	and initial "-" characters.  Wildcards, "*", "%", and "..."
!	are not permitted in an input specification.
!
! FORMAL PARAMETERS:
!
!	P_PAB			Address of PAB.
!
!	P_UNIQUE_DESC		Address of a string descriptor describing
!				the buffer to contain
!				the string of recognized characters.
!
!	P_UNIQUE_LEN		Address of a longword to receive the number
!				of characters recognized (may be 0).
!				Note that this value can't be 0 if full
!				recognition has succeeded because if the
!				user's string already ended with a "]" or ">",
!				then this routine would never have been called
!				in the first place.
!
! IMPLICIT INPUTS:
!
!	NONE
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! ROUTINE VALUE:
!
!	TRUE		complete recognition has been accomplished
!
!	FALSE		no recognition or partial recognition
!			has been accomplished.
!
! SIDE EFFECTS:
!
!	NONE
!
!--
!+
! ALGORITHM:
!
!	Depending on the form of the directory portion of the partial
!	specification, we form a new directory specification including
!	wildcards (after prepending the original node name and device
!	name if any).  This new specification represents the directory
!	one level above the current one in the directory tree.
!	The derived specification is shown in the following examples:
!
!	User's partial
!	specification:		Derived specification:
!
!	[A			[000000]A*.DIR;1
!
!	[A.			[A]*.DIR;1
!
!	[A.B			[A]B*.DIR;1
!
!	[.			[]*.DIR;1
!
!	[.A			[]A*.DIR;1
!
!	[A.B.			[A.B]*.DIR;1
!
!	[A.B.C			[A.B]C*.DIR;1
!
!	[.A.			[.A]*.DIR;1
!
!	[.A.B			[.A]B*.DIR;1
!
!	[-			[-]*.DIR;1
!
!	[-.			[-]*.DIR;1
!
!	[-.A			[-]A*.DIR;1
!
!	[-.A.			[-.A]*.DIR;1
!
!	[-.A.B			[-.A]B*.DIR;1
!
!	[---.A.B.C.D		[---.A.B.C]D*.DIR;1
!
!	[A,			?
!
!	[A,B			?
!
! NOTE: We omit appending the "*" before a ".DIR;1" if the previous
!	name was already 9 characters long.
!
!-
BIND

	PAB		= .P_PAB			: $PAB_DECL,
	CAB		= .PAB[PAB$A_CAB]		: $CAB_DECL,
	PTR		=  CAB[CAB$W_PTR]		: WORD,
	INI		=  CAB[CAB$W_FLD_PTR]		: WORD,
	BUF		= .CAB[CAB$A_CBF]		: VECTOR[,BYTE],
	CLN		=  CAB[CAB$W_CLN]		: WORD,
	UNIQUE_DESC	= .P_UNIQUE_DESC		: VECTOR[2],
	UNIQUE_LEN	= .P_UNIQUE_LEN;

OWN

	DIRNIZ_FAB	: $FAB(NAM=RECNIZ_NAM) VOLATILE,
	END_NAME_LEN,	! Number of characters in trailing name of partial
			! specification.  For example, if the specification
			! were "DBA2:[ABC.EF", then END_NAME_LEN would
			! contain a 2 referring to the 2 characters "EF".
			! This value could be 0 if the partial specification
			! ended with a "-", ".", ",", "<", or "[".
	DELIM : BYTE,	! Delimiter preceding trailing name
	DELIM_PTR,	! Index within BUFFER where delimiter is located.
	BRACKET_PTR,	! Index within BUFFER where open bracket is located.
	LEFT_BRACKET  : BYTE,	! will contain either "[" or "<"
	RIGHT_BRACKET : BYTE;	! will contain matching "]" or ">".

LOCAL

	TEMP,
	STATUS;

OWN

	DERIVE_LEN,	! Number of characters in DERIVE_BUF
	DERIVE_BUF	: VECTOR[NAM$C_MAXRSS,BYTE],
	DERIVE_DESC	: VECTOR[2] INITIAL(NAM$C_MAXRSS,DERIVE_BUF),
	FIRST_FLAG,
	UNIQUE_PTR,
	NEW_COUNT,
	NEW_CHARS	: VECTOR[NAM$C_MAXRSS,BYTE],
	NEW_SIZE;

!EXTERNAL ROUTINE
!
!	HLP$FAB_CLOSE_HANDLER;
!
!ENABLE	HLP$FAB_CLOSE_HANDLER(DIRNIZ_FAB,DUMMY_RAB);
UNIQUE_LEN=0;

!+
!	Compute the number of characters in the trailing name of the
!	partial specification.  At the same time, find the character
!	that precedes this name.  The trailing name may only consist
!	of letters (A-Z, a-z), digits (0-9) and underscore (_).
!	The name length goes into END_NAME_LEN and the delimiter
!	gets stored in DELIM.  DELIM_PTR is updated to be the index
!	into BUFFER where this delimiter was stored.
!-

DELIM_PTR=-1;
DECR I FROM .CLN-1 TO .INI DO
	BEGIN
	SELECTONE .BUF[.I] OF	! This is slow - STAN -
	    SET
	    ['A' TO 'Z',
	     'a' TO 'z',
	     '0' TO '9','_']:	;
	    [OTHERWISE]:
		BEGIN
		DELIM=.BUF[.I];
		DELIM_PTR=.I;
		EXITLOOP
		END;
	    TES;
	END;

!+
! If an internal error occurs, namely no delimiter found,
! then no recognition is possible.  Don't signal this error,
! just beep the guy.  Most likely, the main parser will print
! a valid error message.
!-

IF .DELIM_PTR EQL -1 THEN RETURN FALSE;

END_NAME_LEN=.CLN-.DELIM_PTR-1;

!+
! Now continue to scan backwards looking for the initial "[" or "<".
! Whichever it is, store it in LEFT_BRACKET and store the matching
! type of right bracket in RIGHT_BRACKET.  We scan from the end instead of
! from the beginning so that we don't have to worry about ignoring
! brackets located within access control strings.
! If an internal error occurs, namely no open bracket found, then
! no recognition is possible.
! The index for where the open bracket was located in BUF is stored
! in BRACKET_PTR.  This may have the same value as DELIM_PTR if the
! delimiter was an open bracket.
!-

BRACKET_PTR=-1;

DECR I FROM .DELIM_PTR TO .INI DO
	SELECTONE .BUF[.I] OF

	    SET

	    [%C'[']:	BEGIN
			BRACKET_PTR	= .I;
			LEFT_BRACKET	= %C'[';
			RIGHT_BRACKET	= %C']';
			EXITLOOP
			END;

	    [%C'<']:	BEGIN
			BRACKET_PTR	= .I;
			LEFT_BRACKET	= %C'<';
			RIGHT_BRACKET	= %C'>';
			EXITLOOP
			END;

	    TES;

IF .BRACKET_PTR EQL -1 THEN RETURN FALSE;

!+
! If the delimiter found is not a nice one, then the user has
! typed an invalid partial directory specification or has attempted
! to use wildcards within an input filespec.
! However, it is not our duty to inform him of his error, we
! will merely beep him, and if he insists on typing carriage-return,
! then he will get a proper error message.
!-

!+
! We now go on to build the 'derived' specification for the parent
! directory from the user's partial directory specification.
! This derived specification will be built in DERIVE_BUF.
! First we copy the user's partial specification up to and including
! DELIM into DERIVE_BUF.
! Note that since DERIV_BUF is declared to be as long as the longest
! possible complete filespecification, we don't have to worry about
! checking for overflow when moving things into this buffer.
!-

DERIVE_LEN=.DELIM_PTR-.INI+1;
CH$MOVE(.DERIVE_LEN,BUF[.INI],DERIVE_BUF);

!+
! If the delimiter was ".", then replace the dot by the matching "]".
! If the delimiter was "-", then append a matching "]".
! If the delimiter was "[" or "<" then append "000000" followed
! by the matching close bracket.
!
! Set DERIVE_LEN to the number of characters now in DERIVE_BUF,
! i.e. it will be the index to the first free byte in DERIVE_BUF.
!-

SELECTONE .DELIM OF

    SET

    [%C'.']:

	BEGIN
	DERIVE_BUF[.DERIVE_LEN-1]=.RIGHT_BRACKET;
	END;

    [%C'[',%C'<']:

	BEGIN
	LITERAL	ZERO_LEN=%CHARCOUNT('000000');
	CH$MOVE(ZERO_LEN,UPLIT('000000'),DERIVE_BUF[.DERIVE_LEN]);
	DERIVE_BUF[.DERIVE_LEN+ZERO_LEN]=.RIGHT_BRACKET;
	DERIVE_LEN=.DERIVE_LEN+1+ZERO_LEN
	END;

    [%C'-']:

	BEGIN
	DERIVE_BUF[.DERIVE_LEN]=.RIGHT_BRACKET;
	DERIVE_LEN=.DERIVE_LEN+1
	END;

    [%C',']:

	RETURN FALSE;	! We don't yet handle [m,n] specifications

    [OTHERWISE]:

	RETURN FALSE

    TES;

!+
! Append the portion of the end-name that the user typed.
!-

CH$MOVE(.END_NAME_LEN,BUF[.DELIM_PTR+1],DERIVE_BUF[.DERIVE_LEN]);
DERIVE_LEN=.DERIVE_LEN+.END_NAME_LEN;

!+
! If the name already contains 9 characters, then no "*" is needed,
! otherwise, append a "*".
!-

IF .END_NAME_LEN NEQ 9
  THEN	BEGIN
	DERIVE_BUF[.DERIVE_LEN]=%C'*';
	DERIVE_LEN=.DERIVE_LEN+1
	END;

!+
! Finally, copy the string ".DIR.1" to the end of the derived-name buffer.
!-

TEMP=%CHARCOUNT('.DIR;1');
CH$MOVE(.TEMP,UPLIT('.DIR;1'),DERIVE_BUF[.DERIVE_LEN]);
DERIVE_LEN=.DERIVE_LEN+.TEMP;

DERIVE_DESC[0]=.DERIVE_LEN;

!+
! Zero out the IFI in case we had previously CTRL/C'ed out of
! this FAB leaving it invalid.
!-

DIRNIZ_FAB[FAB$W_IFI] = 0;

!+
! Perform a parse to set up for wild-card processing.
! In this case, there is no default name string or related name.
! If the parse fails, this is probably because of a syntax error
! on the user's part; however, this is no concern of ours - we
! merely refuse to do any recognition.
!-

DIRNIZ_FAB[FAB$B_DNS]=0;
RELATE_NAM[NAM$B_RSL]=0;
RECNIZ_NAM[NAM$B_RSL]=0;
DIRNIZ_FAB[FAB$L_FNA]= DERIVE_BUF;
DIRNIZ_FAB[FAB$B_FNS]=.DERIVE_LEN;

STATUS=$PARSE(FAB=DIRNIZ_FAB);
IF NOT .STATUS THEN RETURN FALSE;

!+
! If the derived filespec contains a wildcard in the directory
! portion, then no recognition is possible.
!-

IF .RECNIZ_NAM[NAM$V_WILD_DIR] THEN RETURN FALSE;

!+
! Perform a search.
!-

FIRST_FLAG=TRUE;
NEW_COUNT=0;
WHILE 1 DO
	BEGIN
	STATUS=$SEARCH(FAB=DIRNIZ_FAB);
	SELECTONE .STATUS OF

	    SET

	    [RMS$_NORMAL]:

		BEGIN
		!+
		! We have now found a resultant string.
		!-
		RESULT_DESC[0]=.RECNIZ_NAM[NAM$B_RSL];
		!+
		! Make sure there is a 0 at the end of this buffer.
		!-
		RESULT_BUF[.RESULT_DESC[0]]=0;
		!+
		! Let us create a descriptor for the additional string
		! after the characters that the user has typed.
		! We count off characters forward from the final "]"
		! in the resultant string.
		!-
		DECR SCAN_PTR FROM .RESULT_DESC[0]-1 TO 0 DO
			IF .RESULT_BUF[.SCAN_PTR] EQL .RIGHT_BRACKET
			  THEN	BEGIN
				UNIQUE_LEN=
				    .RESULT_DESC[0]-.SCAN_PTR-1-.END_NAME_LEN
				    -%CHARCOUNT('.DIR;1');
				UNIQUE_PTR=
				     RESULT_BUF[.SCAN_PTR+1+.END_NAME_LEN];
				EXITLOOP
				END;
		NEW_COUNT=.NEW_COUNT+1;
		!+
		! Calculate the number of additional characters
		! in this resultant string.
		!-
		IF .FIRST_FLAG
		  THEN	BEGIN
			NEW_SIZE=.UNIQUE_LEN;
			CH$MOVE(.NEW_SIZE,.UNIQUE_PTR,NEW_CHARS);
			FIRST_FLAG=FALSE
			END
		  ELSE	BEGIN
			BIND	XS_BUF = .UNIQUE_PTR : VECTOR[,BYTE];
			!+
			! Don't bother getting more filenames if NEW_SIZE
			! is already 0.
			!-
			IF .NEW_SIZE EQL 0 THEN EXITLOOP;
			!+
			! Find out how many characters match previous excess list.
			!-
			INCR I FROM 0 TO .NEW_SIZE-1 DO
				IF .NEW_CHARS[.I] NEQ .XS_BUF[.I]
				  THEN	BEGIN
					NEW_SIZE=.I;
					EXITLOOP
					END;
			END;
		END;

	    [RMS$_FNF]:

		EXITLOOP;

	    [RMS$_NMF]:

		EXITLOOP;

	    [OTHERWISE]:

		IF NOT .STATUS THEN RETURN FALSE

	    TES;

	END;

IF .NEW_COUNT EQL 0 THEN RETURN FALSE;

IF .NEW_COUNT EQL 1
  THEN	BEGIN
	LOCAL TEMP;
	!+
	! We have completely recognized a directory (or subdirectory) name.
	! Now we have to decide whether to beep or recognize a "]".
	! We take the name so far, add a "]*.DIR;1", and do another
	! parse and search to see if there are any more subdirectories.
	! If there are, then we beep the user (return a partial recognition
	! indication) since the user could mean the current directory or one
	! of its subdirectories.  If there are no further subdirectories,
	! then we recognize a close bracket and exit having performed
	! complete recognition on the directory component of
	! the filespecification.
	!-

	!+
	! Move the name so far into DERIVE_BUF.
	!-

	CH$MOVE(.CLN-.INI,BUF[.INI],DERIVE_BUF);
	CH$MOVE(.NEW_SIZE,NEW_CHARS,DERIVE_BUF[.CLN-.INI]);
	DERIVE_LEN=.CLN-.INI+.NEW_SIZE;
	DERIVE_BUF[.DERIVE_LEN]=.RIGHT_BRACKET;
	DERIVE_LEN=.DERIVE_LEN+1;
	TEMP=%CHARCOUNT('*.DIR;1');
	CH$MOVE(.TEMP,UPLIT('*.DIR;1'),DERIVE_BUF[.DERIVE_LEN]);
	DERIVE_LEN=.DERIVE_LEN+.TEMP;

	!+
	! Perform a PARSE to get ready for doing a SEARCH.
	!-

	DIRNIZ_FAB[FAB$L_FNA]= DERIVE_BUF;
	DIRNIZ_FAB[FAB$B_FNS]=.DERIVE_LEN;
	STATUS=$PARSE(FAB=DIRNIZ_FAB);
	IF NOT .STATUS
	  THEN	NEW_COUNT=2	! Anything but 1 stops full recognition
	  ELSE	BEGIN
		!+
		! Now do a SEARCH.
		!-
		STATUS=$SEARCH(FAB=DIRNIZ_FAB);
		IF NOT .STATUS
		  THEN	BEGIN
			NEW_CHARS[.NEW_SIZE]=.RIGHT_BRACKET;
			NEW_SIZE=.NEW_SIZE+1;
			END
		  ELSE	NEW_COUNT=2;
		END;
	END;

UNIQUE_LEN=MIN(.NEW_SIZE,.UNIQUE_DESC[0]);
CH$MOVE(.UNIQUE_LEN,NEW_CHARS,.UNIQUE_DESC[1]);

RETURN (.NEW_COUNT EQL 1)

END;
GLOBAL ROUTINE LIB$$NUMBER_RECOG(P_PAB) =

BEGIN

!++
!
! FUNCTIONAL DESCRIPTION:
!
!	Handles recognition of a number.
!	If we are at the beginning of the field, then
!	we recognize the default (if any).
!	Otherwise, we merely ascertain that the field is a
!	syntactically valid number and assume that it is complete.
!
! FORMAL PARAMETERS:
!
!	P_PAB		Address of PAB.
!
! IMPLICIT INPUTS:
!
!	USER[USER$L_RECOGNIZE_ARG]	Default value (unsigned)
!					-1 means no default.
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! ROUTINE VALUE:
!
!	TRUE		Field was syntactically valid; complete
!			recognition has therefore occurred
!
!	FALSE		We are at beginning of field and there is
!			no default.  No recognition is possible.
!					OR
!			Field was invalid; contained a non-digit.
!
! SIDE EFFECTS:
!
!	NONE
!
!--
BIND

	PAB	= .P_PAB		: $PAB_DECL,
	CAB	= .PAB[PAB$A_CAB]	: $CAB_DECL,
	INI	=  CAB[CAB$W_FLD_PTR]	: WORD,
	PTR	=  CAB[CAB$W_PTR]	: VOLATILE WORD,
	BUF	= .CAB[CAB$A_CBF]	: VECTOR[,BYTE],
	RADIX	=  PAB[PAB$L_ARG]	: LONG;

LOCAL

	RAD,
	MAX_DIG,
	STATUS;

IF .RADIX NEQ 16
  THEN	RAD=MAX(.RADIX,10);

IF .RADIX LSS 2
  THEN	RAD=10;

MAX_DIG=%C'0'+.RAD-1;

!+
! We verify that the field has been properly filled in
! with digits only.  If the field is syntactically correct,
! then we assume that the user has finished typing in his number
! and we say that complete recognition ahs taken place.
! If the field is syntactically in error, then we beep the guy.
!-

IF .RADIX EQL 16
  THEN	INCR P FROM .INI TO .PTR-1 DO
		BEGIN
		IF (.BUF[.P] GEQU %C'0' AND .BUF[.P] LEQU %C'9')
		OR (.BUF[.P] GEQU %C'A' AND .BUF[.P] LEQU %C'F')
		OR (.BUF[.P] GEQU %C'a' AND .BUF[.P] LEQU %C'f')
		  THEN	1
		  ELSE	RETURN FALSE
		END
  ELSE	INCR P FROM .INI TO .PTR-1 DO
		IF .BUF[.P] LSS %C'0' OR .BUF[.P] GTR .MAX_DIG
		  THEN	RETURN FALSE;

!+
! There must be at least one digit.
!-

RETURN (.INI NEQ .PTR)

END;
GLOBAL ROUTINE LIB$$TOKEN_RECOG(P_PAB) =

BEGIN

!++
!
! FUNCTIONAL DESCRIPTION:
!
!	Handles recognition of a token.
!	We ascertain that the field is exactly the string specified.
!
! FORMAL PARAMETERS:
!
!	P_PAB		Address of PAB.
!
! IMPLICIT INPUTS:
!
!	PAB[PAB$L_ARG]	Address of descriptor for the token.
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! ROUTINE VALUE:
!
!	TRUE		Field was syntactically valid; complete
!			recognition has therefore occurred
!
!	FALSE		We are at beginning of field and there is
!			no default.  No recognition is possible.
!					OR
!			Field was invalid; contained a non-digit.
!
! SIDE EFFECTS:
!
!	NONE
!
!--
BIND

	PAB	= .P_PAB		: $PAB_DECL,
	CAB	= .PAB[PAB$A_CAB]	: $CAB_DECL,
	INI	=  CAB[CAB$W_FLD_PTR]	: WORD,
	PTR	=  CAB[CAB$W_PTR]	: VOLATILE WORD,
	BUF	= .CAB[CAB$A_CBF]	: VECTOR[,BYTE],
	TOK	= .PAB[PAB$L_ARG]	: BLOCK[,BYTE],
	TOKLEN	=  TOK[DSC$W_LENGTH]	: WORD,
	TOKBUF	= .TOK[DSC$A_POINTER]	: VECTOR[,BYTE];

LOCAL

	D		: VECTOR[2],
	PART_SIZ,
	STATUS;

!+
!	We look at the characters already typed in by the user.
!	They must match exactly with the characters specified.
!	If not, we return FALSE, indicating that we cannot
!	perform recognition.  If they are correct, then we
!	can recognize the remainder of the token.
!-

PART_SIZ=.PTR-.INI;
STATUS=CH$EQL(.PART_SIZ,BUF[.INI],.PART_SIZ,TOKBUF,0);
IF NOT .STATUS THEN RETURN .STATUS;

!+
! We now recognize the remainder of the token.
!-

IF .TOKLEN GTRU .PART_SIZ
  THEN	BEGIN
	D[0]=.TOKLEN-.PART_SIZ;
	D[1]=TOKBUF[.PART_SIZ];
	LIB$OUT_RECOG(D,CAB)
	END;

RETURN SS$_NORMAL

END;
GLOBAL ROUTINE LIB$$NO_RECOG(P_PAB) =

BEGIN

!++
!
! FUNCTIONAL DESCRIPTION:
!
!	Handles recognition for items that don't do recognition.
!
! FORMAL PARAMETERS:
!
!	P_PAB		Address of PAB.
!
! IMPLICIT INPUTS:
!
!	NONE
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! ROUTINE VALUE:
!
!	FALSE		We are at beginning of field and there is
!			no default.  No recognition is possible.
!					OR
!			Field was invalid; contained a non-digit.
!
! SIDE EFFECTS:
!
!	NONE
!
!--
RETURN FALSE

END;
ROUTINE RECOG_DOCUMENT(DOC_FLAG) =

BEGIN

!++
!
! FUNCTIONAL DESCRIPTION:
!
!	Handles recognition of DECset document and segment names.
!
! FORMAL PARAMETERS:
!
!	DOC_FLAG		TRUE means we should try to recognize
!				a segment name, if can't, then try a
!				document name.
!				FALSE means we must be looking for
!				a segment name only.
!
! IMPLICIT INPUTS:
!
!	CMD[CMD$W_INI_PTR]	Index to start of document specification.
!
!	PTR			Index to first character after document
!				specification.
!
!	BUF			Address of buffer containing command line.
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! ROUTINE VALUE:
!
!	TRUE		complete recognition has been accomplished
!
!	FALSE		no recognition or partial recognition
!			has been accomplished.
!
! SIDE EFFECTS:
!
!	NONE
!
!--
!$SCP_INIT(STRUC=<CMD,USER,PIT,DAT>);
LOCAL PTR;

!BIND
!
!	PTR	=	PIT[PIT$W_CMD_LINE_PTR]	: VOLATILE WORD,
!	DEFAULT_DOC	= DAT[DAT$CS_DOCUMENT]	: $COUNTED_STRING;

LOCAL

	STATUS;
!+
! Go look up segment names.
!-

!+
! If there is no default document yet, then this name
! must be a document name.
!-

!IF .DEFAULT_DOC[CS$B_LENGTH] EQL 0 AND .DOC_FLAG
!  THEN	STATUS=WILD$K_NONE
!  ELSE	STATUS=HLP$WILDMAN(SDM$FT_SNAME);

SELECTONE .STATUS OF

    SET

    [WILD$K_FULL]:

	! Have performed complete recognition on a segment name.

	RETURN TRUE;

    [WILD$K_SOME]:

	! Have performed partial recognition of a segment name.

	RETURN FALSE;

    [WILD$K_NONE]:

	! No segments by this name.
	! Perhaps it was a document name instead.

	IF .DOC_FLAG
	  THEN	BEGIN

		!+
		! Go look up document names.
		!-

!		STATUS=HLP$WILDMAN(SDM$FT_DNAME);
		SELECTONE .STATUS OF

		    SET

		    [WILD$K_FULL]:

			! Have performed complete recognition of a document name.

			BEGIN
			LIB$OUT_RECOG(%ASCID ':');
			RETURN TRUE
			END;

		    [WILD$K_SOME,WILD$K_NONE]:

			! Have performed little or no recognition.

			RETURN FALSE;

		    TES;

		END

    TES;

RETURN FALSE

END;
GLOBAL ROUTINE LIB$$BREAK_RECOG : NOVALUE =

BEGIN

!++
!
! FUNCTIONAL DESCRIPTION:
!
!	prevents multiple field recognition from
!	continuing past this point.
!
! FORMAL PARAMETERS:
!
!	NONE
!
! IMPLICIT INPUTS:
!
!	BUF[.PTR]	current character.
!			If this is HLP$K_DELIM, then multiple field
!			recognition is in progress.
!
! IMPLICIT OUTPUTS:
!
!	USER[USER$L_INI_DATA_LEN]	\  Gets length and pointer to
!	USER[USER$L_INI_DATA_PTR]	/  new initial string.
!
! ROUTINE VALUE:
!
!	NONE
!
!	NOTE:	Routine may not return if multiple field recognition
!		was in progress.  If that is the case, the REPARSE
!		condtion is resignalled and the stack is unwound.
!
! SIDE EFFECTS:
!
!	NONE
!
!--
!$SCP_INIT(STRUC=<USER,PIT>);
!BIND	PTR	=	PIT[PIT$W_CMD_LINE_PTR]	: VOLATILE WORD;
LOCAL PTR;

!IF .BUF[.PTR] EQL HLP$K_DELIM
!  THEN	BEGIN
!	USER[USER$L_INI_DATA_LEN] = .PTR;
!	USER[USER$L_INI_DATA_PTR] = BUF;
!	HLP$RESTORE_STATE();
!	SIGNAL(HLP$_REPARSE)
!	END;
1

END;
END
ELUDOM