-- DFDiffImpl.Mesa, last edit February 9, 1983 12:34 pm
-- Pilot 6.0/ Mesa 7.0

				
DIRECTORY
  CWF: TYPE USING [SetWriteProcedure, WF0, WF1, WF2, WFCR],
  DFSubr: TYPE USING [AllocateDFSeq, CompareDFs, DFSeq, FreeDFSeq, 
  	ParseStream, SortByFileName],
  IO: TYPE USING[Handle, PutChar, UserAbort],
  Rope: TYPE USING[ROPE, Text],
  RopeInline: TYPE USING[InlineFlatten],
  UnsafeSTP: TYPE USING [Error, Handle],
  STPSubr: TYPE USING [GeneralOpen],
  Stream: TYPE USING [Delete, Handle],
  String: TYPE USING [AppendString],
  Subr: TYPE USING [AbortMyself, Any, errorflg, FileError, MakeTTYProcs, PrintGreeting, 
  	strcpy, SubrInit, SubrStop, TTYProcs],
  Time: TYPE USING [Current],
  UECP: TYPE USING[Argv, Parse],
  UserExec: TYPE USING[AcquireResource, AskUser, CommandProc, ExecHandle, GetStreams, 
  	RegisterCommand, ReleaseResource];

DFDiffImpl: PROGRAM
IMPORTS CWF, DFSubr, IO, RopeInline, STP: UnsafeSTP, STPSubr, Stream, String, 
	Subr, Time, UECP, UserExec = {

-- MDS usage!!
stdout: IO.Handle;
-- end of mds

MAXFILES: CARDINAL = 900;


Main: UserExec.CommandProc = TRUSTED {
	ENABLE UNWIND => [] ← UserExec.ReleaseResource[$DFDiff];
	h: Subr.TTYProcs;
	in, out: IO.Handle;
	[in, out] ← UserExec.GetStreams[exec];
	[] ← UserExec.AcquireResource[$DFDiff, "DFDiff", exec];
	h ← Subr.MakeTTYProcs[in, out, exec, MyConfirm];
	DFDiffUsingProcs[h, event.commandLine];
	[] ← UserExec.ReleaseResource[$DFDiff];
	};

DFDiffUsingProcs: PROC[h: Subr.TTYProcs, commandLine: Rope.ROPE] = {
sold: STRING ← [100];
snew: STRING ← [100];
shold, shnew: Stream.Handle;
dfseqold, dfseqnew: DFSubr.DFSeq ← NIL;
starttime: LONG CARDINAL;
argv: UECP.Argv ← UECP.Parse[commandLine];
flat: Rope.Text;
	
	Cleanup: PROC = {
	DFSubr.FreeDFSeq[@dfseqold];
	DFSubr.FreeDFSeq[@dfseqnew];
	Subr.SubrStop[];
	};

{
ENABLE {
	UNWIND => Cleanup[];
	Subr.AbortMyself => {
		CWF.WF0["\nDFDiff Aborted.\n"L];
		GOTO leave;
		};
	STP.Error => {
		CWF.WF0["FTP Error. "L];
		IF error ~= NIL THEN CWF.WF1["message: %s\n"L,error];
		Subr.errorflg ← TRUE;
		GOTO leave;
		};
	};
	
stdout ← h.out;
[] ← CWF.SetWriteProcedure[MyPutChar];
starttime ← Time.Current[];
Subr.SubrInit[256];
Subr.PrintGreeting["DFDiff"L];
dfseqold ← DFSubr.AllocateDFSeq[maxEntries: MAXFILES, zoneType: shared];
dfseqnew ← DFSubr.AllocateDFSeq[maxEntries: MAXFILES, zoneType: shared];
IF argv.argc < 3 THEN GOTO usage;
flat ← RopeInline.InlineFlatten[argv[2]];
Subr.strcpy[sold, LOOPHOLE[flat]];
flat ← RopeInline.InlineFlatten[argv[3]];
Subr.strcpy[snew, LOOPHOLE[flat]];
IF NOT Subr.Any[sold, '.] THEN String.AppendString[sold, ".df"L];
IF NOT Subr.Any[snew, '.] THEN String.AppendString[snew, ".df"L];
CWF.WF1["Opening %s"L, sold];
[shold] ← STPSubr.GeneralOpen[sold, h
	! Subr.FileError => GOTO err1];
CWF.WF1["Opening %s"L, snew];
[shnew] ← STPSubr.GeneralOpen[snew, h
	! Subr.FileError => GOTO err2];
IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself;
DFSubr.ParseStream[shold, dfseqold, sold, NIL, TRUE, FALSE, FALSE, h];
IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself;
DFSubr.ParseStream[shnew, dfseqnew, snew, NIL, TRUE, FALSE, FALSE, h];
Stream.Delete[shold];
Stream.Delete[shnew];
IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself;
DFSubr.SortByFileName[dfseqold, 0, dfseqold.size-1, TRUE];
IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself;
DFSubr.SortByFileName[dfseqnew, 0, dfseqnew.size-1, TRUE];
CWF.WF2["Old file: %s, new file: %s\n"L, sold, snew];
IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself;
CompareDFSeqs[dfseqold, dfseqnew];
IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself;
DFSubr.FreeDFSeq[@dfseqold];
DFSubr.FreeDFSeq[@dfseqnew];
EXITS
err1 => CWF.WF1["Error - can't open %s.\n"L, sold];
err2 => CWF.WF1["Error - can't open %s.\n"L, snew];
usage => CWF.WF0["Error - usage: DFDiff old-df-file new-df-file\n"L];
leave => NULL;
};
starttime ← Time.Current[] - starttime;
CWF.WF1["\nTotal elapsed time for DFDiff %lr."L,@starttime];
IF Subr.errorflg THEN CWF.WF0["\tErrors logged.\n"L];
CWF.WFCR[];
Cleanup[];
};

Choice: TYPE = {added, deleted, different};

CompareDFSeqs: PROC[dfseqold, dfseqnew: DFSubr.DFSeq] = {
res: INTEGER;
iold, inew: CARDINAL;

FOR choice: Choice IN Choice DO
	SELECT choice FROM
	added => CWF.WF0["\nAdded:\n"L];
	deleted => CWF.WF0["\nDeleted:\n"L];
	different => CWF.WF0["\nDifferent:\n"L];
	ENDCASE => ERROR;
	iold ← inew ← 0;
	WHILE iold < dfseqold.size OR inew < dfseqnew.size DO
		IF iold >= dfseqold.size THEN res ← 1
		ELSE IF inew >= dfseqnew.size THEN res ← -1
		ELSE res ← DFSubr.CompareDFs[@dfseqold[iold], @dfseqnew[inew], TRUE];
		IF res < 0 THEN {
			IF choice=deleted THEN {
				CWF.WF1["   %-40s"L, dfseqold[iold].shortname];
				IF dfseqold[iold].createtime ~= 0 THEN 
					CWF.WF1[" %lt"L,
						@dfseqold[iold].createtime];
				CWF.WFCR[];
				};
			iold ← iold + 1;
			}
		ELSE IF res > 0 THEN {
			IF choice = added THEN {
				CWF.WF1["   %-40s"L, dfseqnew[inew].shortname];
				IF dfseqnew[inew].createtime ~= 0 THEN 
					CWF.WF1[" %lt"L,
						@dfseqnew[inew].createtime];
				CWF.WFCR[];
				};
			inew ← inew + 1;
			}
		ELSE {-- they are equivalent names
			IF dfseqold[iold].createtime ~=
			dfseqnew[inew].createtime AND choice = different THEN {
				CWF.WF1["   %-34s"L, dfseqnew[inew].shortname];
				IF dfseqnew[inew].createtime ~= 0 THEN
					CWF.WF1["   new = %lt"L,
						@dfseqnew[inew].createtime];
				CWF.WFCR[];
				};
			inew ← inew + 1;
			iold ← iold + 1;
			};
		ENDLOOP;
	ENDLOOP;
};

MyConfirm: PROC[in, out: IO.Handle, data: REF ANY, msg: Rope.ROPE, dch: CHAR]
	RETURNS[CHAR] = {
	value: ATOM;
	value ← UserExec.AskUser[msg: msg, exec: NARROW[data], 
		keyList: LIST[$Yes, $No]];  -- order is important
	SELECT value FROM
	$No => RETURN['n];
	$Yes => RETURN['y];
	ENDCASE => ERROR;
	};

MyPutChar: PROC[ch: CHAR] = {
	stdout.PutChar[ch];
	};
	

-- start code
UserExec.RegisterCommand["DFDiff.~", Main];
}.