$ TITLE ('==> PASSIF -- FUNCMOD -- PERFORM MAJOR FUNCTIONS <==')

FUNC$MOD:
DO;

/************************************************************************/
/*																		*/
/*     'Copyright 1981,1983 Intel Corporation'.  All rights reserved.	*/
/*      No part of this program or publication may be reproduced,		*/
/*      transmitted, transcribed, stored in a retrievel system, or		*/
/*      translated into any language or computer language, in any		*/
/*      form or by any means, electronic, mechanical, magnetic,			*/
/*      optical, chemical, manual or otherwise, without the prior		*/
/*      written permission of Intel Corporation, 3065 Bowers			*/
/*      Avenue, Santa Clara, California, 95051, Attn: Software			*/
/*      License Administration.											*/
/*																		*/
/************************************************************************/

$ NOLIST

/*         LIT.INC          */

DECLARE					AS LITERALLY 'LITERALLY',
	AMPERSAND			AS '''&''',
	BELL				AS '07H',
	BLANK				AS '020H',
	BOOLEAN				AS 'BYTE',
	CHK					AS 'CALL CHECK$STATUS',
	CLEAR$CHAR			AS '01CH',
	CO$ECHO$FILE		AS '0F00H',
	COLON				AS ''':''',
	COMMA				AS '02CH',
	CONSOLE$INPUT		AS '1',
	CONSOLE$OUTPUT		AS '0',
	CONTROL$Z			AS '01AH',
	CR					AS '0DH',
	DOT					AS '''.''',
	END$DO				AS 'END',
	END$DO$CASE			AS 'END',
	END$DO$WHILE		AS 'END',
	END$IF				AS '   ',
	END$ITERATED$DO		AS 'END',
	END$LOOP			AS ' ',
	EOF$CHAR			AS 'CONTROL$Z',
	ESC					AS '01BH',
	FALSE				AS '0H',
	FF					AS '0CH',
	FOREVER				AS 'WHILE 1',
	FORMAT$ATTRIBUTE	AS '3',
	HASH$MARK			AS '''#''',
	INIT$STG			AS '(*) BYTE INITIAL',
	LEAD$IN$CHAR		AS '07EH',
	LF					AS '0AH',
	NO$LINE$EDIT		AS '0',
	NULL				AS '0FFFFH',
	OPEN$FOR$READ		AS '1',
	OPEN$FOR$WRITE		AS '2',
	OPEN$FOR$UPDATE		AS '3',
	PUB$STRING			AS '(*) BYTE PUBLIC DATA',
	RESET				AS '0',
	RETURN$MARKER$POS	AS '0',
	SEEK$2$EOF			AS '4',
	SEEK$BACKWARD$BY$N	AS '1',
	SEEK$FORWARD$BY$N	AS '3',
	SEEK$2$N			AS '2',
	SEMICOLON			AS ''';''',
	SET					AS '1',
	STRING				AS '(*) BYTE DATA',
	TAB					AS '09H',
	TRANSFER$CONTROL	AS '1',
	TRUE				AS '0FFH',
	UNDERBAR			AS '''_''',
	USER$CI				AS '0',
	WRITE$PROTECT$ATTRIBUTE 		AS '2',
	ZERO$BIAS			AS '0';

/*        ENDLIT.INC        */

/* ISIS.INC */

OPEN: PROCEDURE(AFTN,FILENAME,ACCESS$MODE,LINE$EDIT$AFTN,ERROR) EXTERNAL;
	DECLARE (AFTN,FILENAME,ACCESS$MODE,LINE$EDIT$AFTN,ERROR) ADDRESS; END;
MEMCK: PROCEDURE ADDRESS EXTERNAL; END;
READ: PROCEDURE (AFTN,BUFFER,COUNT,ACTUAL,STATUS) EXTERNAL;
	DECLARE (AFTN,BUFFER,COUNT,ACTUAL,STATUS) ADDRESS; END;
CLOSE: PROCEDURE (AFTN,STATUS) EXTERNAL; DECLARE (AFTN,STATUS) ADDRESS;	END;
DELETE:	PROCEDURE (FILE$NAME$PTR,STATUS) EXTERNAL;
	DECLARE (FILE$NAME$PTR,STATUS) ADDRESS;	END;
SPATH: PROCEDURE (FILE,BUFFER,STATUS) EXTERNAL;
	DECLARE (FILE,BUFFER,STATUS) ADDRESS; END;

/* END ISIS.INC */

/************************************************************/
/*															*/
/*              LITERALLY'S UNIQUE TO PASSIF                */
/*															*/
/************************************************************/

DECLARE
    MINIMUM$ACCEPTABLE$BANNER$LENGTH 	AS 		'73',
    MAXIMUM$TOKEN$LENGTH				AS		'70',
    BI									AS		'CALL BOMB$IF(STATUS)',

	FIRST$FIELD							AS		'0',
    SECOND$FIELD						AS		'28',
    THIRD$FIELD							AS		'54',

    RIGHT$PARENTHESIS 					AS		''')''',
    LEFT$PARENTHESIS 					AS		'''(''',
    RI									AS		'CALL REPORT$IF',
    UNRECOGNIZED$SWITCH$ERR 			AS		'201',
    QUESTION$MARK						AS		'''?''';


/*	MAIN.INC  */

DECLARE
    ACTUAL ADDRESS EXTERNAL,
    AFTN ADDRESS EXTERNAL,
    COMMAND$TAIL$BUF (250) BYTE EXTERNAL,
    DELIMITER BYTE EXTERNAL,
    FIRST$BLOCK$BUF (128) BYTE EXTERNAL,
    FIRST$OUTPUT$MSG$PTR ADDRESS EXTERNAL,
    FIRST$TIME$THROUGH BOOLEAN EXTERNAL,
    INITIALIZED$BANNER (74) BYTE EXTERNAL,
    PRINT$HASH$MARK BOOLEAN EXTERNAL,
    REPORT$FILE$ACTUAL ADDRESS EXTERNAL,
    REPORT$FILE$AFTN ADDRESS EXTERNAL,
    REPORT$FILE$STRING$BEGINNING ADDRESS EXTERNAL,
    SECOND$OUTPUT$MSG$PTR ADDRESS EXTERNAL,
    STATUS ADDRESS EXTERNAL,
    TMP$FILE$AFTN ADDRESS EXTERNAL,
    TMP$FILE$NAME (15) BYTE EXTERNAL,
    TOK$BUF (80) BYTE EXTERNAL;

DECLARE
    KEY$WORDS (10) STRUCTURE
    (LENGTH							BYTE,
    KEY$WORD	(13)				BYTE,
    PROC$PTR						ADDRESS) EXTERNAL;

MOVE: PROCEDURE (COUNT,SOURCE$PTR,DEST$PTR) EXTERNAL;
	DECLARE (COUNT,SOURCE$PTR,DEST$PTR) ADDRESS; END;

/*	END MAIN.INC  */

/*	ERRMSG.INC  */

DECLARE
	BAD$SYNTAX$MSG 				(29)	BYTE	EXTERNAL,
	BOMB$IF$MSG$1    			(12)	BYTE	EXTERNAL,
	BOMB$IF$MSG$2				(40)	BYTE	EXTERNAL,
    DURING$ASSERTION$CHECK$MSG	(50)	BYTE	EXTERNAL,
    PASSIF$INVOKED$BY$MSG       (19)    BYTE    EXTERNAL,
    ZERO$LENGTH$FILE$MSG 		(40)	BYTE	EXTERNAL;

/*	END ERRMSG.INC  */

/*	CLI.INC  */

DECLARE BUF$COUNT BYTE EXTERNAL;

UDI$STRING$MATCH: PROCEDURE (STG$1$PTR,STG$2$PTR) BOOLEAN EXTERNAL;
    DECLARE (STG$1$PTR,STG$2$PTR) ADDRESS; END;
ENFORCE$DELIMITER: PROCEDURE(ENFORCEE) EXTERNAL; DECLARE ENFORCEE BYTE;	END;
NEXT$ARG: PROCEDURE EXTERNAL; END;

/*	END CLI.INC  */

/*	REPORT.INC  */

REPORT$ISIS$ERROR: PROCEDURE (STATUS) EXTERNAL;	DECLARE STATUS ADDRESS;	END;
REPORT$ASSERTION$SUCCESS: PROCEDURE EXTERNAL; END;
REPORT$ASSERTION$FAILURE: PROCEDURE EXTERNAL; END;
REPORT$ASSERTION$FAILURE$W$ISIS$ERR: PROCEDURE (STATUS) EXTERNAL;
	DECLARE STATUS ADDRESS; END;
REPORT$ASSERTION$FAILURE$W$MSG:	PROCEDURE (OLD$STYLE$MSG$PTR) EXTERNAL;
	DECLARE OLD$STYLE$MSG$PTR ADDRESS; END;

/*	END REPORT.INC  */
$ LIST
$ EJECT

/************************************************************/
/*															*/
/*      PROCEDURES   PERFORMING   PASSIF   FUNCTIONS        */
/*															*/
/************************************************************/

/************************************************************/
/*															*/
/*                        NOTES                             */
/*															*/
/************************************************************/

/*

1.	The token ri is a "literal" abbreviation of "call report$if",
	which will call "report$assertion$failure$w$isis$err" if
	status does not equal 0.

*/


/************************************************************/
/*															*/
/*                   LOCAL VARIABLES                        */
/*															*/
/************************************************************/

DECLARE (THE$STRING$WAS$FOUND,THE$STRING$SHOULD$BE$THERE) BOOLEAN;


/************************************************************/
/*															*/
/*                   LOCAL PROCEDURES                       */
/*															*/
/************************************************************/

REPORT$IF:
	PROCEDURE;
	IF STATUS<>0 THEN
		CALL REPORT$ASSERTION$FAILURE$W$ISIS$ERR(STATUS);
	END$IF
	END
REPORT$IF;

it$is$spool$file: PROCEDURE(file$name$ptr,want$exist) BYTE;
	DECLARE file$name$ptr ADDRESS, want$exist BYTE;
	DECLARE test BYTE;
	DECLARE spath$info STRUCTURE (
			DEV BYTE,
			NAME(6) BYTE,
			EXT(3) BYTE,
			DEV$TYPE BYTE,
			DRIVE$TYPE BYTE);

	CALL spath(file$name$ptr,.spath$info,.status);
	IF spath$info.dev <> 29 THEN RETURN false;
	CALL open(.aftn,file$name$ptr,2,0,.status); /* must open for output */
	test = (status = 11);  /* file already exists */
	IF NOT test THEN
		DO;
			CALL close(aftn,.status);
			CALL delete(file$name$ptr,.status);
		END;
	IF (test XOR want$exist) THEN
		CALL report$assertion$failure;
	ELSE CALL report$assertion$success;
	RETURN true;
END;

$ EJECT

/************************************************************/
/*															*/
/*                     FILE$EXISTS                          */
/*															*/
/************************************************************/

FILE$EXISTS:
	PROCEDURE PUBLIC;

	CALL NEXT$ARG;
	CALL ENFORCE$DELIMITER(RIGHT$PARENTHESIS);

	IF it$is$spool$file(.tok$buf(1),true) THEN RETURN;

	CALL OPEN(.AFTN,.TOK$BUF(1),OPEN$FOR$READ,NO$LINE$EDIT,.STATUS);
    IF STATUS=13 THEN
    	CALL REPORT$ASSERTION$FAILURE;
    ELSE IF STATUS<>0 THEN
        CALL REPORT$ASSERTION$FAILURE$W$ISIS$ERR(STATUS);
    ELSE
    	DO;
			/*	See whether file is of zero length. */
			DECLARE SOME$SPACE BYTE;
            CALL READ(AFTN,.SOME$SPACE,SIZE(SOME$SPACE),.ACTUAL,.STATUS); RI;
            IF ACTUAL=0 THEN
                CALL REPORT$ASSERTION$FAILURE$W$MSG(.ZERO$LENGTH$FILE$MSG);
			ELSE
				CALL REPORT$ASSERTION$SUCCESS;
            END$IF
        ENDDO;
    END$IF

	END
FILE$EXISTS;

$ EJECT

/************************************************************/
/*															*/
/*                    FILE$ABSENT                           */
/*															*/
/************************************************************/

FILE$ABSENT:
	PROCEDURE PUBLIC;

    CALL NEXT$ARG;
    CALL ENFORCE$DELIMITER(RIGHT$PARENTHESIS);

	IF it$is$spool$file(.tok$buf(1),false) THEN RETURN;

    CALL OPEN(.AFTN,.TOK$BUF(1),OPEN$FOR$READ,NO$LINE$EDIT,.STATUS);
    IF STATUS=13 THEN
    	CALL REPORT$ASSERTION$SUCCESS;
	ELSE IF STATUS<>0 THEN
		CALL REPORT$ASSERTION$FAILURE$W$ISIS$ERR(STATUS);
    ELSE
    	CALL REPORT$ASSERTION$FAILURE;
    END$IF

	END
FILE$ABSENT;

$ EJECT

/************************************************************/
/*															*/
/*                    FILES$MATCH                           */
/*															*/
/************************************************************/

FILES$MATCH:
	PROCEDURE PUBLIC;

	DECLARE (AFTN$1,AFTN$2) ADDRESS;
    DECLARE (ACTUAL$1,ACTUAL$2) ADDRESS;

    DECLARE BUF$SIZ ADDRESS;

    DECLARE (BUF$1$BOT,BUF$1$TOP,BUF$2$BOT,BUF$2$TOP) ADDRESS;

    DECLARE (BUF$1$PTR,BUF$2$PTR) ADDRESS;
    DECLARE  BUF$1$BYTE BASED BUF$1$PTR BYTE,
    		 BUF$2$BYTE BASED BUF$2$PTR BYTE;

    /*	Pick up the first filename. */
	CALL NEXT$ARG;
	CALL ENFORCE$DELIMITER(COMMA);

    /*	Open it. */
	CALL OPEN(.AFTN$1,.TOK$BUF(1),OPEN$FOR$READ,NO$LINE$EDIT,.STATUS); RI;

    /*	Pick up the second filename. */
    CALL NEXT$ARG;
	CALL ENFORCE$DELIMITER(RIGHT$PARENTHESIS);

    /* Open it. */
    CALL OPEN(.AFTN$2,.TOK$BUF(1),OPEN$FOR$READ,NO$LINE$EDIT,.STATUS); RI;

    /*	Allocate buffers in free memory. */
	BUF$SIZ = ((MEM$CK-.MEMORY)/256) * 128;

    BUF$1$BOT = .MEMORY;
    BUF$1$TOP = .MEMORY + BUF$SIZ - 1;

    BUF$2$BOT = BUF$1$TOP + 1;
    BUF$2$TOP = BUF$2$BOT + BUF$SIZ - 1;

	/*	Now, compare the files. */

    /*	B E G I N N I N G   O F   C O M P A R I S O N   L O O P. */

	begin$loop:
        CALL READ(AFTN$1,BUF$1$BOT,BUF$SIZ,.ACTUAL$1,.STATUS);
        CALL REPORT$ISIS$ERROR(STATUS);

        CALL READ(AFTN$2,BUF$2$BOT,BUF$SIZ,.ACTUAL$2,.STATUS);
        CALL REPORT$ISIS$ERROR(STATUS);

        /*	Perform fast check which may spot mismatched files. */
        IF ACTUAL$1 <> ACTUAL$2 THEN
            CALL REPORT$ASSERTION$FAILURE;
        END$IF

        BUF$1$PTR = BUF$1$BOT;
    	BUF$2$PTR = BUF$2$BOT;

		DO WHILE BUF$1$PTR < BUF$1$BOT+ACTUAL$1;
			IF BUF$1$BYTE <> BUF$2$BYTE THEN
				CALL REPORT$ASSERTION$FAILURE;
        	END$IF
        	BUF$1$PTR = BUF$1$PTR + 1;
            BUF$2$PTR = BUF$2$PTR + 1;
        END$DO$WHILE;

        IF ACTUAL$1=BUF$SIZ THEN
        	/*	There may be more bytes to compare. */
            GOTO begin$loop;
        ELSE
        	/*	The comparison was successful. */
            CALL REPORT$ASSERTION$SUCCESS;
        END$IF
	end$loop

    /*	E N D   O F   C O M P A R I S O N   L O O P. */

    END
FILES$MATCH;

$ EJECT

/************************************************************/
/*															*/
/*                  STRING$IS$IN$FILE                       */
/*															*/
/************************************************************/

STRING$IS$IN$FILE:
	PROCEDURE;
	DECLARE STRING$BUF (85) BYTE;
	DECLARE STRING$LENGTH BYTE AT (.STRING$BUF(0));
    DECLARE FIRST$STRING$CHARACTER BYTE AT (.STRING$BUF(1));

    DECLARE IN$TOKEN STRING (2,'IN');
    DECLARE FILE$TOKEN STRING(4,'FILE');

    DECLARE (BUF$BOT,BUF$TOP) ADDRESS;
    DECLARE BUF$PTR ADDRESS,
    		BUF$CHAR BASED BUF$PTR (100) BYTE;

    DECLARE BYTES$LEFT$IN$BUFFER ADDRESS;
	DECLARE BUF$SIZ ADDRESS;
	DECLARE DONT$START$SEARCH$PAST$HERE ADDRESS;

$ EJECT

    /************************************************************/
    /*															*/
    /*           PROCEDURES LOCAL TO STRING$IS$IN$FILE          */
    /*															*/
    /************************************************************/

	PERFORM$FANCY$BUFFER$REFRESH:
		PROCEDURE;

		/*	The reason for the adjective "fancy" is because,
			before any more reading is done, the text currently
			left in the buffer is moved down to the bottom of
			the buffer, and more text is appended above it.
			We don't have to worry about the text destination
			area overlapping the text source area because the
			text source and destination areas are at the top and
			bottom of the i/o buffer area, respectively;  cannot
			exceed 80 bytes each, and the i/o buffer area is huge.

            After moving the block of text, the variables controlling
            the string matching and buffering are modified so that
            the string matching algorithim begins at the bottom of
            the buffer, and searches the chunk of text which was at
            the top.*/

		DECLARE TMP$ACTUAL ADDRESS;
		DECLARE TMP$COUNT ADDRESS;

        BYTES$LEFT$IN$BUFFER = STRING$LENGTH - 1;

        CALL MOVE(/*count*/			BYTES$LEFT$IN$BUFFER           ,
        		  /*source*/		(DONT$START$SEARCH$PAST$HERE+1),
        		  /*destination*/	BUF$BOT             )          ;

		TMP$COUNT = BUF$SIZ - BYTES$LEFT$IN$BUFFER;

        CALL READ(AFTN,
                 (BUF$BOT+BYTES$LEFT$IN$BUFFER),
                  TMP$COUNT,
                 .TMP$ACTUAL,
                 .STATUS);

        CALL REPORT$ISIS$ERROR(STATUS);

		/*	Now, modify the "search-controlling" to restart the search at
			the bottom of the buffer. */
		ACTUAL  = TMP$ACTUAL + BYTES$LEFT$IN$BUFFER;

		END
    PERFORM$FANCY$BUFFER$REFRESH;

    REPORT$ASSERTION$VERDICT:
    	PROCEDURE (THE$STRING$WAS$FOUND);
        DECLARE THE$STRING$WAS$FOUND BOOLEAN;

        IF NOT (THE$STRING$WAS$FOUND xor THE$STRING$SHOULD$BE$THERE) THEN
			CALL REPORT$ASSERTION$SUCCESS;
        ELSE
        	CALL REPORT$ASSERTION$FAILURE;
        END$IF
    	END
    REPORT$ASSERTION$VERDICT;

    STRING$MATCH$W$BUF:
		PROCEDURE;

		/*	This procedure is the only way to get a positive verdict
			on string matching.  There are many ways to get a negative
			verdict.

			This procedure is attempting to match a UDI string with a
			vanilla string, so the indices of the two strings in the
			matching loop below will be off by one.

        	We can start the string comparison checking at the second
        	byte because the code which calls this procedure has already
        	checked the first byte. */

        DECLARE INDEX BYTE;

        DO INDEX=1 TO STRING$LENGTH-1;
			IF STRING$BUF(INDEX+1)<>BUF$CHAR(INDEX) THEN
				RETURN;
            END$IF
        END$DO$WHILE;

        CALL REPORT$ASSERTION$VERDICT(TRUE);
        END
    STRING$MATCH$W$BUF;

    NORMAL$FILE$READ:
		PROCEDURE;
		CALL READ(AFTN,BUF$BOT,BUF$SIZ,.ACTUAL,.STATUS); RI;
		END
    NORMAL$FILE$READ;

    THERE$PROBABLY$IS$MORE$TEXT:
        PROCEDURE BOOLEAN;
            RETURN ACTUAL=BUF$SIZ;
        END
    THERE$PROBABLY$IS$MORE$TEXT;

$ EJECT

    /************************************************************/
    /*															*/
    /*           FIRST EXECUTABLE IN STRING$IS$IN$FILE          */
    /*															*/
    /************************************************************/

    /*	Grab the string. */
    CALL NEXT$ARG;
	CALL ENFORCE$DELIMITER(RIGHT$PARENTHESIS);

    /*	Move string to local buffer. */
    CALL MOVE(/*count*/				TOK$BUF(0)+1,
    		  /*source*/			.TOK$BUF(0),
    		  /*destination*/		.STRING$BUF);

	/*	Look for the "in". */
    CALL NEXT$ARG;
	CALL ENFORCE$DELIMITER(BLANK);
	IF NOT UDI$STRING$MATCH(.IN$TOKEN,.TOK$BUF) THEN
		CALL REPORT$ASSERTION$FAILURE$W$MSG(.BAD$SYNTAX$MSG);
    END$IF

	/*	Look for the "file". */
	CALL NEXT$ARG;
	CALL ENFORCE$DELIMITER(LEFT$PARENTHESIS);
    IF NOT UDI$STRING$MATCH(.FILE$TOKEN,.TOK$BUF) THEN
		CALL REPORT$ASSERTION$FAILURE$W$MSG(.BAD$SYNTAX$MSG);
    END$IF

	/*	Grab filename. */
	CALL NEXT$ARG;
	CALL ENFORCE$DELIMITER(RIGHT$PARENTHESIS);

    /*	Open file. */
    CALL OPEN(.AFTN,.TOK$BUF(1),OPEN$FOR$READ,NO$LINE$EDIT,.STATUS);
    CALL REPORT$ISIS$ERROR(STATUS);

    /*	Allocate buffers in free memory. */
	BUF$SIZ = ((MEM$CK-.MEMORY)/128)*128;
    BUF$BOT = .MEMORY;
    BUF$TOP = BUF$BOT + BUF$SIZ - 1;
	BUF$PTR = BUF$BOT;

$ EJECT

    /************************************************************/
    /*															*/
    /*                MAIN LOOP OF STRING$IS$IN$FILE            */
    /*															*/
    /************************************************************/

    /*	read in file. */
    CALL NORMAL$FILE$READ;

    continue$string$search:
        DONT$START$SEARCH$PAST$HERE = BUF$BOT + ACTUAL - STRING$LENGTH;

            /**************************************************/
            /*												  */
            /*	HERE FOLLOWS THE "SUB-LOOP" OF THE MAIN LOOP  */
            /*												  */
            /**************************************************/

            DO BUF$PTR = BUF$BOT TO DONT$START$SEARCH$PAST$HERE;
                IF BUF$CHAR(0) = FIRST$STRING$CHARACTER THEN
                    CALL STRING$MATCH$W$BUF;
                END$IF
            END$ITERATED$DO;

        IF THERE$PROBABLY$IS$MORE$TEXT THEN
        	DO;
        		CALL PERFORM$FANCY$BUFFER$REFRESH;
        		GOTO continue$string$search;
        	ENDDO;
        ELSE
            CALL REPORT$ASSERTION$VERDICT(FALSE);
        END$IF
	end$loop

	/*	The code should never get this far.  If text is exhausted, then
		the procedure which discovers that fact will call the procedure
		report$assertion$verdict, which will report the fact, and not
        return.  If the string matches, then, similarly, the string
        match procedure will call report$assertion$verdict, which will
        not return. */

    END
STRING$IS$IN$FILE;

$ EJECT

/************************************************************/
/*															*/
/*                  STRING$FOUND                            */
/*															*/
/************************************************************/

STRING$FOUND:
	PROCEDURE PUBLIC;
	THE$STRING$SHOULD$BE$THERE = TRUE;
    CALL STRING$IS$IN$FILE;
	END
STRING$FOUND;

/************************************************************/
/*															*/
/*                    STRING$ABSENT                         */
/*															*/
/************************************************************/

STRING$ABSENT:
	PROCEDURE PUBLIC;
	THE$STRING$SHOULD$BE$THERE = FALSE;
    CALL STRING$IS$IN$FILE;
	END
STRING$ABSENT;

END
FUNC$MOD;