Google
 

Trailing-Edge - PDP-10 Archives - DEC_CMS-20_V1.0_SRC - cms/sources/diff.bli
There are no other files named diff.bli in the archive.
MODULE DIFF	(
		IDENT = '1',
		%IF
		    %BLISS(BLISS32)
		%THEN
		    LANGUAGE(BLISS32),
		    ADDRESSING_MODE(EXTERNAL=LONG_RELATIVE,
				    NONEXTERNAL=LONG_RELATIVE)
		%ELSE
		    LANGUAGE(BLISS36)
		%FI
		) =
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:	CMS Library Processor
!
! ABSTRACT:
!
!	Compare a master file with a variation file and generate a differences
!	listing file showing all of the corrections or changes implied by the
!	variation.
!
! ENVIRONMENT:	VAX/VMS, DS-20
!
! AUTHOR: D. Knight , CREATION DATE: 29-Oct-79
!
!--
!
! TABLE OF CONTENTS
!

FORWARD ROUTINE
	DIFF,				!Basic comparison algorithm
	CKPAGE,				!test for page mark
	GET_CUR_MST,			!Check line for legal processing
	GET_LIN,			!Get variation line
	OUTDIFF: NOVALUE,		!Output the differences found to the output file
	SETUP,				!Initialize the world
	TERMINATE : NOVALUE,		!Terminate the world
	WRT_LINE : NOVALUE;		!Output line of text

!
! INCLUDE FILES:
!

%if
    %bliss(bliss32)
%then
    library 'sys$library:starlet';
%else
    require 'jsys:';
%fi

LIBRARY 'XPORT:';			!XPORT I/O macros

REQUIRE 'SCONFG:';			!configuration options

REQUIRE 'BLISSX:';

REQUIRE 'COMUSR:';

REQUIRE 'HOSUSR:';

!
! MACROS:
!

MACRO
	IOB$H_RESULTANT=$SUB_FIELD(IOB$T_RESULTANT,STR$H_LENGTH) %,
	IOB$A_RESULTANT=$SUB_FIELD(IOB$T_RESULTANT,STR$A_POINTER) %,
	STG(L,M) = OUTSTG(CH$PTR(UPLIT(L)),%CHARCOUNT(L),M) %;

!
! EQUATED SYMBOLS:
!

LITERAL
	T_LINE_SIZE=250;		!Temporary header line buffer size

!
! OWN STORAGE:
!

OWN
	AFLAG,				!Append qualifier flag
	D_NAM_LEN,			!Difference (output) file name length
	D_NAM_PTR,			!Difference (output) file name pointer
	DIFF_FLG,				!Difference seen flag
	FIELD_WIDTH,			!(page width)/2 (always a multiple of 8)
	I_OPN,				!Input file open flag
	KEEP,				!GET_CUR_MST keep flag
	M_NAM_LEN,			!Master file name length
	M_NAM_PTR,			!Master file name pointer
	M_OPN,				!Master file open flag
	O_OPN,				!Output file open flag
	M_PAGE,				!master page number
	PAGE_SIZE,			!Page width (always a multiple of 8)
	PARL_L,				!Parallel flag
        QUIET_Q,                        !Inhibit terminal messages
	V_NAM_LEN,			!Variation file name length
	V_NAM_PTR,			!Variation file name pointer
	V_PAGE;				!Variation file page number

!
! EXTERNAL REFERENCES:
!

external literal
	s_different,			!Files are different
	s_doanoopen,			!Can't open default file for append
	s_erropen,			!File open error
        s_errfile,			!File has wrong attributes
	s_identical,			!Files are identical
	s_maxwiddif,			!Maximum width
	s_minwiddif,			!Minimum width
	s_nouodel,			!Can't delete unneeded output file
	s_noopen,			!Not able to be opened
	s_oanoopen,			!Can't open file for append
	s_onoopen;			!Can't open output file



EXTERNAL
	INPUT_IOB : $XPO_IOB(),		!Variation file IOB
	LIN_MST,			!Current line in master buffer
	LIN_VAR,			!Current line in variation buffer
	L_LN_MST,			!Last line in master buffer
	L_LN_VAR,			!Last line in variation buffer
	MASTER_IOB : $XPO_IOB(),	!Master file IOB
	MST_EOF,			!EOF seen on master file
	MST_PTR : REF VECTOR,		!Master buffer pointer
	OUTPUT_IOB : $XPO_IOB(),	!Output file IOB
	VAR_EOF,			!EOF seen on variation file
	VAR_PTR : REF VECTOR;		!Variation buffer pointer

EXTERNAL ROUTINE
	ASCDEC,				!Ascii to Decimal
	BUG,				!Error
	CMPINI,				!Set up comparison algorithm
	CMPTXT,				!Compare two texts
	COMAND,				!Parse command
	DATTIM,				!Get current date and time
	DECASC,				!Decimal to ASCII
	DECASZ,				!Decimal to ASCII filled
	ERS,				!User error
	ERSIOB,				
	ERSXPO,
	exits,				!exit silently
        filtyp,				!file attributes?
	GETACT,				!Get user's name
	GET_LXM,			!Get text lexeme
	GET_STG_CT,			!Get size of string
	LOCALF,				!Report error for network spec.
	OUTINI,				!Initialize text output
	OUTNUM,				!Output numeric field
	OUTNMZ,				!Output filled numeric field
	OUTSTG,				!Output text string
	PACK,				!Repack buffer
	sysmsg;
	
GLOBAL ROUTINE DIFF =

!++
! FUNCTIONAL DESCRIPTION:
!
!	Compare two files, one being the "master", the other
!	being the "variation".  Generate a differences listing file from the
!	differences.
!
!	The algorithm is relatively simple.  First, look for two lines that
!	match in the two files.  Once they are found, make sure that they
!	comprise a unique match, namely, there are no other lines which
!	could also match similarly (if they are not unique, assume that no
!	match exists).  Once a unique match is found, scan the text backwards
!	and find as many matching lines as possible (this gets rid of redundant
!	non-unique matches).  This establishes the boundaries of matched lines
!	which then can be passed to OUTDIFF to place in the output file.
!
!	The purpose of this procedure is to prevent false matches which may
!	occur because of multiple blank lines or multiple lines of "boilerplate"
!	which so often occur and cause unwarranted matches.
!
! FORMAL PARAMETERS:
!
!	NONE.
!
! IMPLICIT INPUTS:
!
!	NONE.
!
! IMPLICIT OUTPUTS:
!
!	NONE.
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	Standard GETELM returns as described in SCONFG.
!
! SIDE EFFECTS:
!
!	NONE.
!
!--

    BEGIN

    LOCAL
	CMD,
	FIL_PTR,
	FIL_LEN,
	PAR : REF PARAMETER_BLOCK,
	QUAL : REF QUALIFIER_BLOCK,
	RETVAL,				!Return value temp
	SUB_CMD,
	USR_REM : REF DESC_BLOCK;

    !Parse the command line
    IF
	NOT COMAND(CMD,SUB_CMD,QUAL,PAR,USR_REM)
    THEN
	RETURN K_SILENT_ERROR;

    !Set no parallel for now
    PARL_L=FALSE;

    !Set no append for now
    AFLAG = FALSE;

    !Don't inhibit terminal messages for now
    QUIET_Q = FALSE;

    !Initialize field_width; default for page_size and field_width is in SETUP.
    FIELD_WIDTH = 0;

    !Initialize output file "descriptors"
    D_NAM_LEN = 0;
    D_NAM_PTR = K_NULL;


    !Set up qualifiers, if any
    WHILE
	.QUAL NEQ K_NULL
    DO
	BEGIN
	
	SELECTONE  .QUAL[QUA_CODE]  OF
            SET
	    [K_APPEND_QUAL]  :    AFLAG = TRUE;
            [K_PARALLEL_QUAL]:    PARL_L = TRUE ;
	    [K_OUTPUT_QUAL]  :    BEGIN
				      D_NAM_LEN = .QUAL[QUA_VALUE_LEN];
				      D_NAM_PTR = .QUAL[QUA_VALUE_PTR];
				  END;
	    [K_WIDTH_QUAL]:  BEGIN
				  LOCAL P;     !for ascdec to change

				  P = .QUAL[QUA_VALUE_PTR];
				  PAGE_SIZE = ASCDEC(P, .QUAL[QUA_VALUE_LEN]);
				  IF  .PAGE_SIZE LSS 48
				  THEN
				      BEGIN
				      sysmsg(s_minwiddif,
					LIT('Minimum LINE_WIDTH is 48'),0);
				      PAGE_SIZE=48;
				      FIELD_WIDTH=24;
				      END
				  ELSE
				  IF  .PAGE_SIZE GTR 128
				  THEN		!changed max to 128 for docu-
						!ment consistency...multiples
				      BEGIN	!of 8
				      sysmsg(s_maxwiddif,
					LIT('Maximum LINE_WIDTH is 128'),0);
				      PAGE_SIZE=128;
				      FIELD_WIDTH=64;
				      END
				  ELSE
				      BEGIN     !Compute to nearest mult of 8.
				      PAGE_SIZE=.PAGE_SIZE-(.PAGE_SIZE MOD 8);
				      FIELD_WIDTH=.PAGE_SIZE/2;
				      FIELD_WIDTH=.FIELD_WIDTH-(.FIELD_WIDTH MOD 8);
				      END;
				  END;
	    [K_NOAPPEND_QUAL]  :    AFLAG = FALSE;
            [K_NOPARALLEL_QUAL]:    PARL_L = FALSE;
	    [K_NOOUTPUT_QUAL]  :    BEGIN
				      D_NAM_LEN = 0;
				      D_NAM_PTR = K_NULL;
				  END;
            TES;

	QUAL=.QUAL[QUA_A_NEXT]
	END;


    !Get master file name
    M_NAM_PTR=.PAR[PAR_TEXT_PTR];
    M_NAM_LEN=.PAR[PAR_TEXT_LEN];

    !Advance to variation name
    PAR=.PAR[PAR_A_NEXT];

    !Variation file name
    V_NAM_PTR=.PAR[PAR_TEXT_PTR];
    V_NAM_LEN=.PAR[PAR_TEXT_LEN];

    !Start up the world
    IF
	NOT SETUP(.M_NAM_LEN,.M_NAM_PTR,.V_NAM_LEN,.V_NAM_PTR,.D_NAM_LEN,
                     .D_NAM_PTR)
    THEN
	BEGIN
	TERMINATE();
	RETURN K_SILENT_ERROR
	END;

    !Generate the updated master file
    !This always starts at the beginning
    RETVAL=CMPTXT(L_LN_MST,L_LN_VAR);

    ! Convert RETVAL to a system status code, and inform the user.
    IF .RETVAL EQL G_OK
    THEN
	BEGIN	! Success.
	IF
	    .DIFF_FLG 
	THEN
	    BEGIN	! Different.
	    IF  NOT .QUIET_Q  THEN  sysmsg(s_different,
					LIT('Files are different'),0) ;
	    RETVAL = s_different;
	    END		! Different.
	ELSE
	    BEGIN	! Identical.
	    IF  NOT .QUIET_Q  THEN  sysmsg(s_identical,
					LIT('Files are identical'),0) ;
	    RETVAL = s_identical;
	    END		! Identical.
	END	! Success.
    ELSE
	RETVAL = K_SILENT_ERROR ;	! Message has been output by CMPTXT.

    !All is complete, exit quietly
    TERMINATE();

    exits(.RETVAL)

    END;				!End of DIFF
ROUTINE CKPAGE (LEN,PTR,SEQ) =

!++
! FUNCTIONAL DESCRIPTION:
!
!	Check a line to see if it is a legal page mark
!
! FORMAL PARAMETERS:
!
!	LEN - length of line
!	PTR - pointer to line
!	SEQ - TRUE if sequence information needs to be skipped
!
! IMPLICIT INPUTS:
!
!	NONE.
!
! IMPLICIT OUTPUTS:
!
!	NONE.
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	TRUE - line is page mark
!	FALSE - line is not a page mark
!
! SIDE EFFECTS:
!
!	NONE.
!
!--

    BEGIN

    LOCAL
	L,
	P;

    IF
	.LEN EQL 0 OR
	.PTR EQL 0
    THEN
	!Not a page mark
	RETURN FALSE;

    L=.LEN;
    P=.PTR;

    IF
	.SEQ
    THEN
	!Skip over the sequence information
	DO
	    L=.L-1
	UNTIL
	    CH$RCHAR_A(P) EQL %C';';

    !See if the page mark is all that is left
    .L EQL 1 AND
    CH$RCHAR(.P) EQL FORM_FEED

    END;				!end of CKPAGE
ROUTINE GET_CUR_MST (LINE_NUM) =

!++
! FUNCTIONAL DESCRIPTION:
!
!	See if a master line is a legal text line which can be processed
!
! FORMAL PARAMETERS:
!
!	LINE_NUM - line number of line in master buffer
!
! IMPLICIT INPUTS:
!
!	NONE.
!
! IMPLICIT OUTPUTS:
!
!	NONE.
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	TRUE - line is legal line for processing
!	FALSE - line is not to be processed
!
! SIDE EFFECTS:
!
!	NONE.
!
!--

    BEGIN

    .LINE_NUM LSS .L_LN_MST

    END;				!End of GET_CUR_MST
ROUTINE GET_LIN (LGT,PTR) =

!++
! FUNCTIONAL DESCRIPTION:
!
!	Get a variation line from the input file
!
! FORMAL PARAMETERS:
!
!	LGT - address of cell where length is to be placed
!	PTR - address of cell where address of data is placed
!
! IMPLICIT INPUTS:
!
!	NONE.
!
! IMPLICIT OUTPUTS:
!
!	NONE.
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	I/O completion code
!
! SIDE EFFECTS:
!
!	NONE.
!
!--

    BEGIN

    LOCAL
	COMPLETION;

    COMPLETION=$step_get(IOB=INPUT_IOB,FAILURE=0);

    .LGT=.INPUT_IOB[IOB$H_STRING];
    .PTR=.INPUT_IOB[IOB$A_STRING];

    .COMPLETION

    END;				!End of GET_LIN
ROUTINE OUTDIFF (M_LINE,V_LINE) : NOVALUE =

!++
! FUNCTIONAL DESCRIPTION:
!
!	Output any differences between the master and variation files seen
!	so far.  This recognizes both deletions and insertions.  Comments
!	and control lines are ignored.
!
! FORMAL PARAMETERS:
!
!	M_LINE - Source line pointer
!	V_LINE - Variation line pointer
!
! IMPLICIT INPUTS:
!
!	The lines of text are stored in the master and variation buffers.
!
! IMPLICIT OUTPUTS:
!
!	NONE.
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!	PACK is called to repack the buffer and get rid of the lines that
!	have been output.
!
!--

    BEGIN

    LOCAL
	COUNT,
	L_PTR,
	L_SIZ,
	TXT_M_SEEN,
	TXT_V_SEEN;

    IF
	.M_LINE EQL -1 AND
	.V_LINE EQL -1
    THEN
	BUG(LIT('Two null OUTDIFF pointers (COMPAR)'));

    !Check for potential bug
    IF
	.M_LINE LSS .LIN_MST AND
	.M_LINE NEQ -1
    THEN
	BUG(LIT('Master line pointer out of range in OUTDIFF (COMPAR)'));

    IF
	.V_LINE LSS .LIN_VAR AND
	.V_LINE NEQ -1
    THEN
	BUG(LIT('Variation line pointer out of range in OUTDIFF (COMPAR)'));

    TXT_M_SEEN=FALSE;
    TXT_V_SEEN=FALSE;



    !Get number of lines per entry maximum
    IF
	.M_LINE NEQ -1 AND
	.V_LINE NEQ -1
    THEN
	BEGIN
	IF
	    .M_LINE-.LIN_MST GTR .V_LINE-.LIN_VAR
	THEN
	    COUNT=.M_LINE-.LIN_MST
	ELSE
	    COUNT=.V_LINE-.LIN_VAR
	END
    ELSE
    IF
	.M_LINE EQL -1
    THEN
	COUNT=.V_LINE-.LIN_VAR
    ELSE
	COUNT=.M_LINE-.LIN_MST;

    IF
	.COUNT NEQ 0
    THEN
	DIFF_FLG=TRUE;

    !Check to see which kind of listing is desired
    IF
	NOT .PARL_L
    THEN
	BEGIN

	LOCAL
	    ADJLEN,		!Adjust length
	    LFLAG,		!If m_nam_len larger then 1,else 0
	    MORE,
	    POS,
	    SAVEPOS,		!position of last dash output
	    TEMP,		!all-purpose variable
	    TEMPV,		!all-purpose variable
	    TL,			!current total output line length
	    XTRA;		!difference between lengths of file names



	!Initialize variables to enable matching the dashes at the end of any
	!pair of difference lines.  EX.
	!-  -  -  -  longname.dat  Line 32 -  -  -  -
	!-  -  -  -  temp.dat Line 0 -  -  -  -  -  -
	!					    ^

	!Find out whether the "header" lines for the two files will fit on
	! a line of size .page_size
	TEMP = .MASTER_IOB[IOB$V_SEQUENCED];
	TEMP = 30 + (IF .TEMP THEN 2*SEQ_NUM_SIZE+1 ELSE 5);
	TEMPV = .INPUT_IOB[IOB$V_SEQUENCED];
	TEMPV = 30 + (IF .TEMPV THEN 2*SEQ_NUM_SIZE+1 ELSE 5);
	IF .PAGE_SIZE GEQ .TEMP+.M_NAM_LEN
	THEN
	    TEMP = .M_NAM_LEN
	ELSE
	    TEMP = 8; 		!8 is length of "FILE (1)"
	IF .PAGE_SIZE GEQ .TEMPV+.V_NAM_LEN
	THEN
	    TEMPV = .V_NAM_LEN
	ELSE
	    TEMPV = 8;		!8 is length of "FILE (2)"

	!Now TEMP & TEMPV stand for the length of whatever will go in the
	! file name field (note that in any case, we use whatever the user
	! typed in for files instead of the resultant file specification.
	TL = 0;
	XTRA = ABS(.TEMP - .TEMPV);
	IF .TEMP GEQ .TEMPV
	THEN
	    LFLAG = 1
	ELSE
	    LFLAG = 0;

	!Run through the master text
	IF
	    .M_LINE NEQ -1
	THEN
	    BEGIN
	    INCR I FROM .LIN_MST TO .M_LINE-1 DO
		BEGIN

		LOCAL
		    M_SEQ,
		    T_PTR;

		!Read a line
		L_PTR=.MST_PTR[.I-.LIN_MST];
		L_SIZ=GET_STG_CT(L_PTR);
		T_PTR=.L_PTR;

		!Get sequence number if it exists
		IF
		    .MASTER_IOB[IOB$V_SEQUENCED]
		THEN
		    BEGIN
		    M_SEQ=ASCDEC(L_PTR,0);
		    !Skip over ";"
		    CH$RCHAR_A(L_PTR);
		    !Readjust count
		    L_SIZ=.L_SIZ-CH$DIFF(.L_PTR,.T_PTR)
		    END
		ELSE
		    M_SEQ=0;

		IF
		    NOT .TXT_M_SEEN
		THEN
		    BEGIN
		    STG('-  -  -  -  ',FALSE);
		    TL = .TL + 12;
		    IF .TEMP NEQ .M_NAM_LEN
		    THEN		!don't use real file name
			BEGIN
			STG('FILE (1)',FALSE);
			TL = .TL + 8;
			END
		    ELSE		!use real file name
			BEGIN
			OUTSTG(.M_NAM_PTR,.M_NAM_LEN,FALSE);
			TL = .TL + .M_NAM_LEN;
			END;
		    STG(' Line ',FALSE);

		    TL = .TL + 6;
		    IF
			.M_SEQ EQL 0
		    THEN
			BEGIN
			OUTNUM(.LIN_MST+1,FALSE);
			ADJLEN = DECASC(.LIN_MST+1,0);
			END
		    ELSE
			!Sequenced file
			BEGIN
			IF
			    CKPAGE(.L_SIZ,.L_PTR,FALSE)
			THEN
			    !a page mark will appear as line 0 of the next page
			    BEGIN
			    OUTNMZ(0,SEQ_NUM_SIZE,FALSE);
			    STG('/',FALSE);
			    OUTNUM(.M_PAGE+1,FALSE);
			    ADJLEN = SEQ_NUM_SIZE + 1 + DECASC(.M_PAGE+1,0);
			    END
			ELSE
			    BEGIN
			    OUTNMZ(.M_SEQ,SEQ_NUM_SIZE,FALSE);
			    STG('/',FALSE);
			    OUTNUM(.M_PAGE,FALSE);
			    ADJLEN = SEQ_NUM_SIZE + 1 + DECASC(.M_PAGE,0);
			    END
			END;

		    TL = .TL + .ADJLEN;
		    IF (.TL MOD 3) EQL 0
		    THEN		!Add one space after line number
			BEGIN
			STG(' ',FALSE);
			TL = .TL + 1;
			END;

		    IF .V_LINE NEQ -1
		    THEN
			BEGIN			!Prepare to match dashes
						! of master and variation
			IF .LFLAG
			THEN
			    MORE = 0
			ELSE
			    MORE = .XTRA;

			!Seq_num_size included here in case that master is
			! unsequenced, and variation is sequenced. This
			! could lead to numbers of form "xx.." and
			! "xx.../xx..." ,and the latter might be so long
			! in the variation file that it would go past
			! .tl+12+.more (12 is the amount of space required
			! to place four dashes after the line to match
			! the four preceding it).  When this code was written, 
			! seq_num_size was equal to 5--hence the 7.
			INCR POS FROM .TL+1 TO .TL+SEQ_NUM_SIZE+7+.MORE
			  DO
			    IF ( (.POS - 1) MOD 3 ) EQL 0
			    THEN
				BEGIN		!LAST POSITION WAS MULT. OF 3
				STG('-',FALSE);
				SAVEPOS = .POS;	!Keep position of last dash
				END
			    ELSE
				STG(' ',FALSE);

			OUTSTG(0,0,TRUE);
			END
		    ELSE
			STG('  -  -  -  -',TRUE);
		    TXT_M_SEEN=TRUE
		    END;

		STG('      1)',FALSE);
		IF
		    CKPAGE(.L_SIZ,.L_PTR,FALSE)
		THEN
		    BEGIN
		    STG('<PAGE>',TRUE);
		    M_PAGE=.M_PAGE+1
		    END
		ELSE
		    !Length of '      1)' is 8
		    WRT_LINE(.L_PTR,.L_SIZ,.PAGE_SIZE-8,TRUE)
		END
	    END;

	!Process variation text
	IF
	    .LIN_VAR NEQ .V_LINE AND
	    .V_LINE NEQ -1
	THEN
	    BEGIN
	    !Output the text proper
	    INCR I FROM .LIN_VAR TO .V_LINE-1 DO
		BEGIN

		LOCAL
		    V_SEQ,
		    T_PTR;

		L_PTR=.VAR_PTR[.I-.LIN_VAR];
		L_SIZ=GET_STG_CT(L_PTR);
		T_PTR=.L_PTR;

		!Get sequence number if it exists
		IF
		    .INPUT_IOB[IOB$V_SEQUENCED]
		THEN
		    BEGIN
		    V_SEQ=ASCDEC(L_PTR,0);
		    !Skip over ";"
		    CH$RCHAR_A(L_PTR);
		    !Readjust count
		    L_SIZ=.L_SIZ-CH$DIFF(.L_PTR,.T_PTR)
		    END
		ELSE
		    V_SEQ=0;

		IF
		    NOT .TXT_V_SEEN
		THEN
		    BEGIN
		    TXT_V_SEEN=TRUE;
		    TL = 0;
		    STG('-  -  -  -  ',FALSE);
		    TL = .TL + 12;
		    IF .TEMPV NEQ .V_NAM_LEN
		    THEN		!don't use real file name
			STG('FILE (2)',FALSE)
		    ELSE		!use real file name
			OUTSTG(.V_NAM_PTR,.V_NAM_LEN,FALSE);
		    TL = .TL + .TEMPV;
		    STG(' Line ',FALSE);
		    TL = .TL + 6;
		    IF
			.V_SEQ EQL 0
		    THEN
			BEGIN
			OUTNUM(.LIN_VAR+1,FALSE);
			ADJLEN = DECASC(.LIN_VAR+1,0);
			END
		    ELSE
			!Sequenced file
			BEGIN
			IF
			    CKPAGE(.L_SIZ,.L_PTR,FALSE)
			THEN
			    !A page mark will appear as line 0 of the next page
			    BEGIN
			    OUTNMZ(0,SEQ_NUM_SIZE,FALSE);
			    STG('/',FALSE);
			    OUTNUM(.V_PAGE+1,FALSE);
			    ADJLEN = SEQ_NUM_SIZE + 1 + DECASC(.V_PAGE+1,0);
			    END
			ELSE
			    BEGIN
			    OUTNMZ(.V_SEQ,SEQ_NUM_SIZE,FALSE);
			    STG('/',FALSE);
			    OUTNUM(.V_PAGE,FALSE);
			    ADJLEN = SEQ_NUM_SIZE + 1 + DECASC(.V_PAGE,0);
			    END
			END;

		    TL = .TL + .ADJLEN;
		    IF (.TL MOD 3) EQL 0
		    THEN			!Add 1 space after line number
			BEGIN
			STG(' ',FALSE);
			TL = .TL + 1;
			END;
	
		    IF .TXT_M_SEEN
		    THEN
			BEGIN

			IF  NOT .LFLAG
			THEN
			    MORE = 0
			ELSE
			    MORE = .XTRA;

			INCR POS FROM .TL+1 TO .SAVEPOS
			  DO
			    IF  ((.POS - 1) MOD 3) EQL 0
			    THEN		!IF MULT. OF 3,PRINT "-"
				STG('-',FALSE)
			    ELSE
				STG(' ',FALSE);
			OUTSTG(0,0,TRUE);
			END
		    ELSE
		        STG('  -  -  -  -',TRUE);
		    END;

		STG('      2)',FALSE);
		IF
		    CKPAGE(.L_SIZ,.L_PTR,FALSE)
		THEN
		    BEGIN
		    STG('<PAGE>',TRUE);
		    V_PAGE=.V_PAGE+1
		    END
		ELSE
		    !Length of '      2)' is 8
		    WRT_LINE(.L_PTR,.L_SIZ,.PAGE_SIZE-8,TRUE)
		END
	    END;

	IF
	    .TXT_M_SEEN OR
	    .TXT_V_SEEN
	THEN
	    BEGIN
	    IF .PAGE_SIZE GEQ 75 
	    THEN
		INCR I FROM 1 TO 25 DO STG('-  ',FALSE)
	    ELSE
		INCR I FROM 1 TO .PAGE_SIZE/3 DO STG('-  ',FALSE);
	    OUTSTG(0,0,TRUE);
	    OUTSTG(0,0,TRUE);
	    OUTSTG(0,0,TRUE)
	    END
	END
    ELSE
	!Parallel listing format
	BEGIN

	LOCAL
	    L_TRLR,
	    R_TRLR,
	    WRK_LINE : VECTOR[CH$ALLOCATION(15)],
	    WRK_LEN,
	    WRK_PTR;


	L_TRLR=FALSE;
	R_TRLR=FALSE;

	!Now output the header for the change sequence
	IF
	    .COUNT NEQ 0
	THEN
	    BEGIN

	    LOCAL
		M_SEQ,
		V_SEQ;

	    !Get alignment for header line
	    INCR I FROM 1 TO .FIELD_WIDTH-19 DO STG(' ',FALSE);

	    !Get line
	    WRK_PTR=CH$PTR(WRK_LINE);

	    !Generate header data on left part of listing
	    IF
		.MASTER_IOB[IOB$V_SEQUENCED] AND
		.M_LINE NEQ -1
	    THEN
		BEGIN
		L_PTR=.MST_PTR[0];
		L_SIZ=GET_STG_CT(L_PTR);
		IF
		    CKPAGE(.L_SIZ,.L_PTR,TRUE)
		THEN
		    !A page mark will appear as sequence 0 of the next page
		    BEGIN
		    INCR I FROM 1 TO SEQ_NUM_SIZE DO CH$WCHAR_A(%C'0',WRK_PTR);
		    CH$WCHAR_A(%C'/',WRK_PTR);
		    WRK_LEN=SEQ_NUM_SIZE+1+DECASC(.M_PAGE+1,.WRK_PTR)
		    END
		ELSE
		    !Pick up sequence number needed
		    BEGIN
		    M_SEQ=ASCDEC(L_PTR,0);
		    WRK_LEN=DECASZ(.M_SEQ,.WRK_PTR,SEQ_NUM_SIZE);
		    WRK_PTR=CH$PLUS(.WRK_PTR,.WRK_LEN);
		    CH$WCHAR_A(%C'/',WRK_PTR);
		    WRK_LEN=.WRK_LEN+1+DECASC(.M_PAGE,.WRK_PTR)
		    END
		END
	    ELSE
		!Unsequenced
		WRK_LEN=DECASC(.LIN_MST+1,.WRK_PTR);

	    !Output the assembled data
	    INCR I FROM 1 TO 10-.WRK_LEN DO STG('-',FALSE);
	    STG(' Line ',FALSE);
	    OUTSTG(CH$PTR(WRK_LINE),.WRK_LEN,FALSE);
	    STG(' ',FALSE);
	    STG('---',FALSE);

	    !Generate header data on the right part of the listing
	    WRK_PTR=CH$PTR(WRK_LINE);
	    IF
		.INPUT_IOB[IOB$V_SEQUENCED] AND
		.V_LINE NEQ -1
	    THEN
		BEGIN
		L_PTR=.VAR_PTR[0];
		L_SIZ=GET_STG_CT(L_PTR);
		IF
		    CKPAGE(.L_SIZ,.L_PTR,TRUE)
		THEN
		    !A page mark will appear as line 0 of the next page
		    BEGIN
		    INCR I FROM 1 TO SEQ_NUM_SIZE DO CH$WCHAR_A(%C'0',WRK_PTR);
		    CH$WCHAR_A(%C'/',WRK_PTR);
		    WRK_LEN=SEQ_NUM_SIZE+1+DECASC(.V_PAGE+1,.WRK_PTR)
		    END
		ELSE
		    !Pick up sequence number needed
		    BEGIN
		    V_SEQ=ASCDEC(L_PTR,0);
		    WRK_LEN=DECASZ(.V_SEQ,.WRK_PTR,SEQ_NUM_SIZE);
		    WRK_PTR=CH$PLUS(.WRK_PTR,.WRK_LEN);
		    CH$WCHAR_A(%C'/',WRK_PTR);
		    WRK_LEN=.WRK_LEN+1+DECASC(.V_PAGE,.WRK_PTR)
		    END
		END
	    ELSE
		!Unsequenced
		WRK_LEN=DECASC(.LIN_VAR+1,.WRK_PTR);

	    !Output the assembled data
	    STG(' Line ',FALSE);
	    OUTSTG(CH$PTR(WRK_LINE),.WRK_LEN,FALSE);
	    STG(' ',FALSE);
	    INCR I FROM 1 TO 10-.WRK_LEN DO STG('-',FALSE);
	    OUTSTG(0,0,TRUE)
	    END;

	!Now put out the two texts
	INCR I FROM 0 TO .COUNT-1 DO
	    BEGIN

	    !Left part
	    IF
		.LIN_MST+.I LSS .M_LINE AND
		.M_LINE NEQ -1
	    THEN
		BEGIN
		L_PTR=.MST_PTR[.I];
		L_SIZ=GET_STG_CT(L_PTR);
		IF
		    .MASTER_IOB[IOB$V_SEQUENCED]
		THEN
		    !Skip sequence number
		    DO
			L_SIZ=.L_SIZ-1
		    UNTIL
			CH$RCHAR_A(L_PTR) EQL %C';'
		END
	    ELSE
		BEGIN
		L_PTR=0;
		L_SIZ=0
		END;

	    IF
		.L_PTR NEQ 0 OR
		.L_TRLR
	    THEN
		BEGIN
		IF
		    CKPAGE(.L_SIZ,.L_PTR,FALSE)
		THEN
		    BEGIN
		    WRT_LINE(CH$PTR(UPLIT('<PAGE>')),6,.FIELD_WIDTH,FALSE);
		    M_PAGE=.M_PAGE+1
		    END
		ELSE
		    WRT_LINE(.L_PTR,.L_SIZ,.FIELD_WIDTH,FALSE)
		END
	    ELSE
		BEGIN
		INCR I FROM 1 TO .FIELD_WIDTH-19 DO STG(' ',FALSE);
		STG('------------------',FALSE);
		L_TRLR=TRUE
		END;

	    STG('|',FALSE);

	    !Right part
	    IF
		.LIN_VAR+.I LSS .V_LINE AND
		.V_LINE NEQ -1
	    THEN
		BEGIN
		L_PTR=.VAR_PTR[.I];
		L_SIZ=GET_STG_CT(L_PTR);
		IF
		    .INPUT_IOB[IOB$V_SEQUENCED]
		THEN
		    !Skip sequence number
		    DO
			L_SIZ=.L_SIZ-1
		    UNTIL
			CH$RCHAR_A(L_PTR) EQL %C';'
		END
	    ELSE
		BEGIN
		L_PTR=0;
		L_SIZ=0
		END;

	    IF
		.L_PTR NEQ 0 OR
		.R_TRLR
	    THEN
		BEGIN
		IF
		    CKPAGE(.L_SIZ,.L_PTR,FALSE)
		THEN
		    BEGIN
		    WRT_LINE(CH$PTR(UPLIT('<PAGE>')),6,.FIELD_WIDTH,TRUE);
		    V_PAGE=.V_PAGE+1
		    END
		ELSE
		    WRT_LINE(.L_PTR,.L_SIZ,.FIELD_WIDTH,TRUE)
		END
	    ELSE
		BEGIN
		STG('------------------',TRUE);
		R_TRLR=TRUE
		END

	    END;

	!Now output the trailer
	IF
	    .COUNT NEQ 0
	THEN
	    BEGIN

	    IF
		NOT .L_TRLR
	    THEN
		BEGIN
		INCR I FROM 1 TO .FIELD_WIDTH-19 DO STG(' ',FALSE);
		STG('------------------',FALSE)
		END;

	    IF
		NOT .R_TRLR AND
		.L_TRLR
	    THEN
		WRT_LINE(0,0,.FIELD_WIDTH,FALSE);

	    IF
		NOT .R_TRLR OR
		NOT .L_TRLR
	    THEN
		STG('-',FALSE);

	    IF
		NOT .R_TRLR
	    THEN
		STG('------------------',FALSE);

	    OUTSTG(0,0,TRUE);
	    OUTSTG(0,0,TRUE)

	    END

	END;

    !Advance the page count if a page break is seen.  Remember
    !that the two lines at the end are always the same so
    !checking only one is necessary here.
    IF
	.M_LINE LSS .L_LN_MST AND
	.M_LINE NEQ -1
    THEN
	BEGIN
	L_PTR=.MST_PTR[.M_LINE-.LIN_MST];
	L_SIZ=GET_STG_CT(L_PTR);

	IF
	    CKPAGE(.L_SIZ,.L_PTR,.MASTER_IOB[IOB$V_SEQUENCED])
	THEN
	    BEGIN
	    M_PAGE=.M_PAGE+1;
	    V_PAGE=.V_PAGE+1
	    END
	END;

    !Repack the text
    PACK(.M_LINE,.V_LINE)

    END;				!End of OUTDIFF
ROUTINE SETUP (LGT,STR,LGTV,STRV,LGTD,STRD) =

!++
! FUNCTIONAL DESCRIPTION:
!
!	Initialize the tables, get the command line, and initially fill
!	the buffers.
!
! FORMAL PARAMETERS:
!
!	LGT - length of master file name to be processed
!	STR - pointer to master file name to be processed
!	LGTV - length of variation file name to be processed
!	STRV - pointer to variation file name to be processed
!	LGTD - length of difference (output) file name to be processed
!	STRD - pointer to difference (output) file name to be processed
!	       (note that LGTD and STRD may have to be computed here
!		if they contain K_NULL)
! IMPLICIT INPUTS:
!
!	NONE.
!
! IMPLICIT OUTPUTS:
!
!	NONE.
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	TRUE - files opened OK
!	FALSE - files not opened successfully
!
! SIDE EFFECTS:
!
!	NONE.
!
!--

    BEGIN

    LOCAL
	CFIL: VECTOR[CH$ALLOCATION(EXTENDED_FILE_SPEC)],
	CFIL_PTR,
	CFIL_SIZ,
	FIL: VECTOR[CH$ALLOCATION(EXTENDED_FILE_SPEC)],
	FIL_PTR,
	FIL_SIZ,
	GENERATION,
	NAME_PTR,
	S_L_PTR,
	S_L_SIZ,
	STG_CNT,
	STS;

    OWN  ISB:  $XPO_SPEC_BLOCK;

    M_OPN=FALSE;
    I_OPN=FALSE;
    O_OPN=FALSE;

    M_PAGE=1;
    V_PAGE=1;

    ! Prevent network operations in this release.
    IF
	NOT LOCALF(.LGT,.STR)
    THEN
	RETURN FALSE ;
    IF
	NOT LOCALF(.LGTV,.STRV)
    THEN
	RETURN FALSE ;

    if
	.lgtd neq 0 and
	.strd neq k_null
    then
	begin
	if
	    not localf(.lgtd,.strd)
	then
	    return false
	end;

    STS=$CMS_OPEN(IOB=MASTER_IOB,FILE_SPEC=(.LGT,.STR),FAILURE=0);
    IF
	NOT .STS
    THEN
	BEGIN
	ERSXPO(S_NOOPEN,.STS,CAT(('Cannot open '),(.LGT,.STR)));
	END;

    M_OPN=TRUE;

    STS=$CMS_OPEN(IOB=INPUT_IOB,FILE_SPEC=(.LGTV,.STRV),FAILURE=0);
    IF
	NOT .STS
    THEN
	BEGIN
	ERSXPO(S_NOOPEN,.STS,CAT(('Cannot open '),(.LGTV,.STRV)));
	END;

    I_OPN=TRUE;

    !Now open differences file
    !If output qualifier not specified, construct default
    IF
	.LGTD EQL 0 AND
	.STRD EQL K_NULL
    THEN
	!no output qualifier given
        BEGIN

	!+
	! We must remove any device and direcotry specification so that
	! the dif file ends up in the default directory
	!-

	LOCAL
	    mst_spec_blk : $xpo_spec_block;

	sts = $xpo_parse_spec (spec_block = mst_spec_blk,
			       file_spec  = master_iob[iob$t_resultant],
			       failure    = 0);

	if not .sts
	then
	    bug(cat('Cannot parse valid file name ',master_iob[iob$t_resultant]));

	IF
	    .AFLAG 
	THEN
	    BEGIN		!Try opening for append
	    IF
		NOT (STS=$CMS_OPEN(   IOB=OUTPUT_IOB,
				      FILE_SPEC=CMPDEF,
				      default=mst_spec_blk[xpo$t_file_name],
				      OPTIONS=APPEND,
				      FAILURE=0))
	    THEN
	        ERSIOB(s_doanoopen,OUTPUT_IOB,
			CAT('Cannot open default output file for append'));
            !+
            ! Make sure the file has the correct attributes
            !-
            IF
                filtyp(output_iob) NEQ 1
            THEN
               BEGIN
               $cms_close(iob=output_iob,options=remember,failure=0);
               ers(s_errfile,cat('File ',output_iob[iob$t_resultant],
                ' is not a sequential, variable length, non-sequenced file'));
               END;

	    END
	ELSE
	    BEGIN		!Try opening for output
	    IF
		NOT (STS=$CMS_OPEN(   IOB=OUTPUT_IOB,
				      FILE_SPEC=CMPDEF,
				      default=mst_spec_blk[xpo$t_file_name],
				      OPTIONS=output,
				      FAILURE=0))
	    THEN
	        ERSIOB(s_erropen,OUTPUT_IOB,
			CAT('Cannot open default output file'))
	    END
	END
    ELSE
	!Output qualifier given
	BEGIN
	IF
	    .AFLAG
	THEN
	    BEGIN		!Try opening for append
	    IF
		NOT (STS=$CMS_OPEN(	IOB=OUTPUT_IOB,
					file_spec=(.lgtd,.strd),
					default=CMPDEF,
					OPTIONS=APPEND,
					FAILURE=0))
	    THEN
	        ERSIOB(s_doanoopen,OUTPUT_IOB,
			CAT('Cannot open output file ',(.lgtd,.strd),
			    ' for append'));
            !+
            ! Make sure the file has the correct attributes
            !-
            IF
                filtyp(output_iob) NEQ 1
            THEN
               BEGIN
               $cms_close(iob=output_iob,options=remember,failure=0);
               ers(s_errfile,cat('File ',output_iob[iob$t_resultant],
                ' is not a sequential, variable length, non-sequenced file'));
               END;
	    END
	ELSE
	    BEGIN		!Try opening for output
	    IF
		NOT (STS=$CMS_OPEN(	IOB=OUTPUT_IOB,
					file_spec=(.lgtd,.strd),
					default=CMPDEF,
					OPTIONS=output,
					FAILURE=0))
	    THEN
	        ERSIOB(s_onoopen,OUTPUT_IOB,
			CAT('Cannot open output file ',(.lgtd,.strd)))
	    END

	END;


    O_OPN=TRUE;

    !Initialize comparison algorithm and fill the buffer
    CMPINI(FALSE,0,GET_CUR_MST,OUTDIFF,GET_LIN);

    OUTINI(OUTPUT_IOB);
    !If /append, then add form-feed
    IF .AFLAG AND (.STS EQL STEP$_NORMAL)
    THEN
	OUTSTG(CH$PTR(UPLIT(%STRING(%CHAR(FORM_FEED)))),1,TRUE);


    !Supply defaults if necessary. Must be multiple of 8
    IF  .FIELD_WIDTH EQL 0
    THEN
	BEGIN
	FIELD_WIDTH=64;
	PAGE_SIZE=128;
	END;

    DIFF_FLG=FALSE;

    !Generate listing header
    STG(%string(fac_name,' File Comparison Utility'),TRUE);

    STG('Files Compared By ',FALSE);
    FIL_SIZ=GETACT(FIL);
    OUTSTG(CH$PTR(FIL),.FIL_SIZ,FALSE);
    STG(' On ',FALSE);
    FIL_SIZ=DATTIM(FIL);
    OUTSTG(CH$PTR(FIL),.FIL_SIZ,TRUE);

    BEGIN

    OWN   OMFN,			!master file name entirely outputed flag
	  OIFN;			!input file name entirely outputed flag

    ROUTINE HEADER (A_DESC,FIELD_SIZE,PART,CENTER) =
	!spaces out all the way to field_size;if .center then center else write
	BEGIN

        OWN ASB:	$XPO_SPEC_BLOCK;
	BIND DESC = .A_DESC: $STR_DESCRIPTOR();
	LOCAL DLEN,
	      DPTR;
	$XPO_PARSE_SPEC(FILE_SPEC=DESC,SPEC_BLOCK=ASB);

	DLEN = .DESC[STR$H_LENGTH];
	DPTR = .DESC[STR$A_POINTER];

	IF .DLEN GTR .FIELD_SIZE
	THEN			!String will not fit on one line
	    BEGIN
	    LOCAL PTR,
		  DIFF_FLG;
	    PTR=CH$FIND_SUB(LEN_COMMA_PTR(DESC,ASB[XPO$T_DIRECT]));
	    IF CH$FAIL(.PTR) THEN BUG(CAT('COMPARE/SETUP CAN''T FIND DIRECTORY IN ',DESC));
	    PTR=CH$PLUS(.PTR,.ASB[XPO$H_DIRECT]);


	    !Separate file name,type,and version from node,device,and directory
    	    DIFF_FLG = CH$DIFF(.PTR,.DPTR);

    	    IF .PART EQL 1
    	    THEN	!Write out node and directory
    		BEGIN
    		IF  .DIFF_FLG GTR .FIELD_SIZE
    		THEN
    		    BEGIN	!Truncate node/device/dir on left
    		    DPTR=CH$PLUS(.PTR,-.FIELD_SIZE);
		    DIFF_FLG=.FIELD_SIZE;
    		    END;
    		IF  .CENTER
    		THEN	!center node and directory
    		    BEGIN
    		    INCR I FROM 1 TO (.FIELD_SIZE-.DIFF_FLG)/2 DO STG(' ',FALSE);
    		    OUTSTG(.DPTR,.DIFF_FLG,FALSE);
    		    INCR I FROM 1 TO .FIELD_SIZE-(.FIELD_SIZE-.DIFF_FLG)/2-.DIFF_FLG
    		     DO STG(' ',FALSE);
    		    END
    		ELSE	!don't center node and directory
    		    BEGIN
    		    OUTSTG(.DPTR,.DIFF_FLG,FALSE);
    		    INCR I FROM 1 TO .FIELD_SIZE-.DIFF_FLG DO STG(' ',FALSE);
    		    END;
    		RETURN FALSE	!Return false (more output exists)
    		END
    	    ELSE	!Write out file name,type, and version
    		BEGIN
    		LOCAL DIF;

    		DIF=.DLEN-.DIFF_FLG;

    		IF  .DIF GTR .FIELD_SIZE
    		THEN		!Truncate file name/type/ver# on right
    		    DIF=.FIELD_SIZE;

		IF  .CENTER
    		THEN	!center file name
    		    BEGIN
    		    INCR I FROM 1 TO (.FIELD_SIZE-.DIF)/2 DO STG(' ',FALSE);
    		    OUTSTG(CH$PLUS(.DPTR,.DIFF_FLG),.DIF,FALSE);
    		    INCR I FROM 1 TO .FIELD_SIZE-(.FIELD_SIZE-.DIF)/2-.DIF
    		      DO STG(' ',FALSE);
    		    END
    		ELSE	!don't center file name
    		    BEGIN
    		    OUTSTG(.PTR,.DIF,FALSE);
    		    INCR I FROM 1 TO .FIELD_SIZE-.DIF DO STG(' ',FALSE);
    		    END;
     		RETURN TRUE 	!return true (no more output)
    		END
	    END
	ELSE				!file name fits on one line
	    BEGIN
	    IF  .CENTER
	    THEN
		BEGIN			!Center output
		INCR I FROM 1 TO (.FIELD_SIZE-.DLEN)/2 DO STG(' ',FALSE);
		OUTSTG(.DPTR,.DLEN,FALSE);
		INCR I FROM 1 TO .FIELD_SIZE - (.FIELD_SIZE-.DLEN)/2 - .DLEN
		  DO STG(' ',FALSE);
		END
	    ELSE
		BEGIN			!Don't center output
		OUTSTG(.DPTR,.DLEN,FALSE);
		INCR I FROM 1 TO .FIELD_SIZE - .DLEN DO STG(' ',FALSE);
		END;
	    RETURN TRUE			!Return true (no more output)
	    END

	END;			!END HEADER

    IF
	NOT .PARL_L
    THEN
	BEGIN
	STG('    (1)  ',FALSE);
	OMFN = HEADER(MASTER_IOB[IOB$T_RESULTANT],.PAGE_SIZE-9,1,FALSE);
	IF  NOT .OMFN
	THEN
	    BEGIN
	    OUTSTG(0,0,TRUE);
	    STG('                  ',FALSE);  	!18 Spaces
	    HEADER(MASTER_IOB[IOB$T_RESULTANT],.PAGE_SIZE-18,2,FALSE);
	    END;
	OUTSTG(0,0,TRUE);

	STG('    (2)  ',FALSE);
	OIFN = 	HEADER(INPUT_IOB[IOB$T_RESULTANT],.PAGE_SIZE-9,1,FALSE);
	IF  NOT .OIFN
	THEN
	    BEGIN
	    OUTSTG(0,0,TRUE);
	    STG('                  ',FALSE);	!18 Spaces
	    HEADER(INPUT_IOB[IOB$T_RESULTANT],.PAGE_SIZE-18,2,FALSE);
	    END;

	INCR I FROM 1 TO 4 DO OUTSTG(0,0,TRUE)
	END
    !Parallel format
    ELSE
	BEGIN


	OUTSTG(0,0,TRUE);

	OMFN = HEADER(MASTER_IOB[IOB$T_RESULTANT],.FIELD_WIDTH-1,1,TRUE);
	STG(' ',FALSE);
	OIFN = HEADER(INPUT_IOB[IOB$T_RESULTANT],.FIELD_WIDTH,1,TRUE);
	OUTSTG(0,0,TRUE);

	IF  NOT .OMFN
	THEN
	    BEGIN
	    HEADER(MASTER_IOB[IOB$T_RESULTANT],.FIELD_WIDTH-1,2,TRUE);
	    STG(' ',FALSE);
	    END;
	IF .OMFN  AND  NOT .OIFN
	THEN
	    BEGIN
	    INCR I FROM 1 TO .FIELD_WIDTH DO STG(' ',FALSE);
	    OIFN = HEADER(INPUT_IOB[IOB$T_RESULTANT],.FIELD_WIDTH,2,TRUE);
	    END;
	IF  NOT .OIFN
	THEN
	    HEADER(INPUT_IOB[IOB$T_RESULTANT],.FIELD_WIDTH,2,TRUE);
	OUTSTG(0,0,TRUE);

	OUTSTG(0,0,TRUE);
	END;

    END;		!end  block
    TRUE

    END;				!End of SETUP
ROUTINE TERMINATE : NOVALUE =

!++
! FUNCTIONAL DESCRIPTION:
!
!	Clean up the loose ends and go away.
!
! FORMAL PARAMETERS:
!
!	NONE.
!
! IMPLICIT INPUTS:
!
!	All of the logical unit numbers of open files.
!
! IMPLICIT OUTPUTS:
!
!	NONE.
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!	All of the files are closed.
!
!--

    BEGIN

    IF
	.I_OPN
    THEN
	$cms_close(IOB=INPUT_IOB);

    IF
	.M_OPN
    THEN
	$cms_close(IOB=MASTER_IOB);

    IF
	.O_OPN
    THEN
	begin
	if
	    not .aflag and
	    not .diff_flg
	then
	    !no output file if no differences seen
	    begin
	    local status;
	    $cms_close(iob=output_iob,options=remember);
	    status = $step_delete(iob=output_iob,failure=0);
!I don't know what these codes mean or why there're here
!	    if not .status 
!	    then 
!		if not(.status eql xpo$_bad_req or .status eql xpo$_sys_error)
!		then
!		    ersxpo(s_nouodel,.status, lit('Cannot delete output file.'));
	    end
	else
	    begin
	    stg('**** End of Differences ****',true);
	    $cms_close(IOB=OUTPUT_IOB)
	    end
	end

    END;				!End of TERMINATE
ROUTINE WRT_LINE (POINTER,LENGTH,FIELD_LENGTH,TERM) : NOVALUE =

!++
! FUNCTIONAL DESCRIPTION:
!
!	Write a text line, counting tabs and blanks correctly
!
! FORMAL PARAMETERS:
!
!	LENGTH - length of text line
!	POINTER - pointer to text line
!	FIELD_LENGTH - length of output field (either field_width or page size)
!
! IMPLICIT INPUTS:
!
!	Input and output IOBs
!
! IMPLICIT OUTPUTS:
!
!	NONE.
!
! ROUTINE VALUE:
! COMPLETION CODES:
!
!	NONE.
!
! SIDE EFFECTS:
!
!	NONE.
!
!--

    BEGIN

    LOCAL
	COL_POS,
	PRT_SIZ;

    COL_POS=0;
    PRT_SIZ=.LENGTH;

    !Count the effective length of the line
    INCR I FROM 1 TO .LENGTH DO
	BEGIN

	LOCAL
	    CHAR,
	    OLD_POS;

	CHAR=CH$RCHAR(CH$PLUS(.POINTER,.I-1));

	OLD_POS=.COL_POS;

	!Ignore non-printing characters
	IF
	    .CHAR GEQ %C' ' OR
	    .CHAR EQL %C'	'
	THEN
	    BEGIN
	    IF
		.CHAR NEQ %C'	'
	    THEN
		COL_POS=.COL_POS+1
	    ELSE
		COL_POS=.COL_POS-(.COL_POS MOD 8)+8
	    END;

	IF
	    .COL_POS GEQ .FIELD_LENGTH-1
	THEN
	    BEGIN
	    COL_POS=.OLD_POS+1;
	    PRT_SIZ=.I;
	    !mark line as being overflowed
	    CH$WCHAR(%C'+',CH$PLUS(.POINTER,.PRT_SIZ-1));
	    EXITLOOP
	    END
	ELSE
	    OLD_POS=.COL_POS

	END;

    OUTSTG(.POINTER,.PRT_SIZ,FALSE);

    IF
	NOT .TERM
    THEN
	BEGIN
	!Fill to nearest tab
	IF
	    .COL_POS+8-(.COL_POS MOD 8) LEQ .FIELD_LENGTH-1
	THEN
	    BEGIN
	    STG('	',FALSE);
	    COL_POS=.COL_POS+8-(.COL_POS MOD 8)
	    END;

	!Tab as close to end as possible
	UNTIL
	    .COL_POS+8 GTR .FIELD_LENGTH-2
	DO
	    BEGIN
	    STG('	',FALSE);
	    COL_POS=.COL_POS+8
	    END;

	!Fill in the remainder
	INCR I FROM .COL_POS+1 TO .FIELD_LENGTH-1 DO
	    STG(' ',FALSE)
	END;

    OUTSTG(0,0,.TERM)

    END;				!End of WRT_LINE
END				!End of Module DIFF
ELUDOM