{**************************************************************
 ***                                                        ***
 ***                     CHECK DATABASE                     ***
 ***                                                  Mar 96***
 **************************************************************}
{Split from the fixit module mar 96, but still part of the fixit
"suite".}
{$I compdirs}

unit ChkData;

INTERFACE

IMPLEMENTATION

uses jimmys,
			jimhooks,
			indexes,
			tuiedit, dialogs, inpfname,
			objects,
			dattime,
			global,
			views,
			tasks,
			notes, lstrings,
			tuimsgs,
			alljimmy,
			app,
			{$IFDEF kdirctry} kperson, kcompany, {$ENDIF}
			{$IFDEF kletter}	kletter, {$ENDIF}
			{$IFDEF kjob}	kjob, {$ENDIF}
			{$IFDEF kDiary}	kdiary, {$ENDIF}
			files,
			minilib;

const
	fxCheck    = $00;  {ie AutoLeave}
	fxQueryFix = $01;
	fxAutoFix  = $02;
	fxSolFix   = $03;

{---- global vars used for consistent error procesing, etc across the checks ----}
var	LastError, Error, Action, LastAction : string;
		ErrorFile : text;
		NumErrors : word;

		ProBox : PProgressBox;

		CheckType : record
			Query : word; {look/query fix/auto fix}
			Output : string[12];
			Indexes : boolean;
			Hooks : boolean;
			Jimmys : boolean;
		end;

{*************************************************************************
 ***             HANDLE DATABASE ERROR MESSAGE & USER CHOICE           ***
 *************************************************************************}
{Split title into two parts - second part gets stored in error file}
function DoError(Title,ErrorMsg,OptionsLine : string; ErrDefault : integer) : word;
var mfType : longint;
		MessageBox : PMEssageBox;
		I : word;

begin
	Error := Error + '; '+ErrorMsg;

	{Auto-correct - return default option and no error box}
	if ErrDefault<0 then begin
		 case ErrDefault of
			 -1 : DoError := cmButton1;
			 -2 : DoError := cmButton2;
			 -3 : DoError := cmButton3;
			 -4 : DoError := cmButton4;
		 end;
		 if CheckType.Query = fxCheck then DoError := cmButton1; {check only...}
		 exit;
	end;

	mfType := 0;
	if Pos('1 -',OptionsLine)>0 then mfType := mfButtons1;
	if Pos('2 -',OptionsLine)>0 then mfType := mfButtons2;
	if Pos('3 -',OptionsLine)>0 then mfType := mfButtons3; {all three buttons}
	if Pos('4 -',OptionsLine)>0 then mfType := mfButtons4; {four buttons}

	{Work out type of fix}
	DoError := cmButton1;   {default to leave it}
	case CheckType.Query of
		fxCheck    : DoError := cmButton1;  {leave it}
		fxQueryFix : if mfType=0 then begin {no options}
										DoError := 0;
										PauseMessage(Title,ErrorMsg,hcNoContext)
								 end else begin
									 {Ask for which fix}
									 New(MessageBox, init(Title, ErrorMsg+#13+#13+OptionsLine, mfType, hcNoContext));
									 {Select default button}
									 for I := 1 to ErrDefault-1 do MessageBox^.SelectNext(False);
									 WarningBleep;
									 DoError := Desktop^.ExecView(MessageBox);
									 dispose(MessageBox, done);
								 end;
		fxAutoFix  : case ErrDefault of            {do default}
									1 : DoError := cmButton1;
									2 : DoError := cmButton2;
									3 : DoError := cmButton3;
									4 : DoError := cmButton4;
								 end;
	end;
end;

{**************************************************************************
 ***                    WRITE ERROR TO FILE                             ***
 **************************************************************************}
procedure WriteErrors(Rec : longint);
var S : string;
		I : integer;
begin
	if Error<>'' then begin
		{Write to disk}
		if Action<>'' then Action := 'ACTION: '+Copy(Action,3,length(Action));
		append(ErrorFile); {open with append}

		S := '';
		{add lf's so it comes out ok on the printer}
		for I := 1 to length(Error) do begin
			if Error[I] <> #13 then
				S := S + Error[I];
		end;

		Writeln(ErrorFile, Rec,S,' '+Action);

		{Store last one for display}
		LastError := Copy(Error,3,length(Error));  {for progress box}
		LastAction := Action;

		{Set counter}
		NumErrors := NumErrors +1;

		close(errorFile); {close - flushes it out in case of crash}
		{Prepare for next}
		Error := '';
		Action := '';
	end;

end;


{*************************************************************
 ***                                                       ***
 ***               SMALL METHODS FOR FIXING                ***
 ***                                                       ***
 *************************************************************}
{fix - turn into hole}
procedure MakeIntoHole(fitype: byte; IndexItem : PIndexItem; Rec : longint);
begin
	Action := Action + '; Set to hole';
	IndexItem^.Hole := True;
	IndexItem^.Idx2Dat := -1;
	Stream(fiType)^.PutAt(Rec, IndexItem);
end;

procedure InsertNewIndexItem(ForfiType, ForixType : byte; ForJimmy : PJimmy);
var IndexItem : PIndexItem;
		InsIdxRec : longint;
begin
	if ForJimmy^.GetIndexKey(ForixType)='' then
		ForJimmy^.SetIdxPtr(ForixType, -1)
	else begin
		New(IndexItem, init);
		IndexItem^.KeyString := ForJimmy^.GetIndexKey(ForixType);
		IndexItem^.Idx2Dat := ForJimmy^.RecNo;
		IndexItem^.ixType := ForixType;
		IndexStream(Forfitype)^.Insert(ForfiType, IndexItem, InsIdxRec);
		dispose(IndexItem, done);

		ForJimmy^.SetIdxPtr(Forixtype ,InsIdxRec);
	end;

	{Store changed index pointer}
	ForJimmy^.StoreIdxPtr(Forixtype);
	Action := Action +'; New IndexItem inserted at '+N2Str(InsIdxRec);
end;


{*************************************************************
 ***                                                       ***
 ***                 CHECKING METHODS                      ***
 ***                                                       ***
 *************************************************************}

{======== CHECKS POINTER TO JIMMY ============================}
function CheckPtr2Jimmy(Title, Comment : string; var JimmyID : longint) : boolean;
var Control : word;
		S : string;
		Jimmy : PJimmy;
begin
	CheckPtr2Jimmy := False; {returns true if save required}

	FileAdmin(fiJimmys)^.LogOn;

	S := '';

	if (JimmyID<-1) or (JimmyID>=JimmyStream^.NoRecs) then
		S := ' Out of Range '+N2Str(JimmyID)
	else
		if JimmyID>-1 then begin
			Jimmy := PJImmy(JImmyStream^.GetAt(JimmyID));
			if Jimmy=nil then begin
				S := ' --> nil '+N2Str(JimmyID);
				JimmyStream^.Reset;
			end else
				dispose(Jimmy, done);
		end;

	if S<>'' then begin

		Control := DoError(Title, Comment+' '+S,'1 - Leave; 2 - Set to -1',2);

		if Control = cmButton2 then begin
			Action := Action + ' JimmyID Set to -1;';
			JimmyID := -1;
			CheckPtr2Jimmy := True; {returns true if save required}
		end;
	end;

	FileAdmin(fiJimmys)^.LogOff;
end;


{*******************************************************************
 ***                                                             ***
 ***                 CHECK INDEX                                 ***
 ***                                                             ***
 ******************************************************************}
{Looks for duplicate pointers, backpointers, out-of-order}
procedure CheckIndex(fiType : Word);
var	IndexItem, PrevIndexItem,TempIndexItem : PIndexItem;
		I : longint;
		IdxRec,TempRec : longint;
		Control : word;
		Jimmy : PJimmy;
		Default : integer;
		ETitle,S : string;
		BakPtr : longint; BakfiType : byte;
		StreamName : string;



begin
	if ProBox^.command = cmCancel then exit;

	FileAdmin(fiType)^.LogOn;
	FileAdmin(fiJimmys)^.LogOn;
	StreamName := ucase(FileAdmin(fiType)^.Name^); {shorthand reference}

	IndexItem := nil;
	PrevIndexItem := nil;
	ProBox^.ResetTime;

	append(ErrorFile);
	writeln(ErrorFile,StreamName+' (fiType '+N2Str(fiType)+') recs 0-'+N2Str(Stream(fiType)^.NoRecs-1));
	Close(ErrorFile);

	IdxRec := -1{};
	while (IdxRec<Stream(fitype)^.NoRecs-1) and (ProBox^.Command=cmOK) do begin

		IdxRec := IdxRec +1;

		if (IdxRec mod 10) = 0 then
			 ProBox^.Update('#Errs='+N2Str(NumErrors)+' Last='+LastError+#13+
											LastAction+'  | Index('+N2Str(fiType)+')',IdxRec,Stream(fitype)^.NoRecs);

		ETitle := 'Index: fi'+N2Str(fiType)+' #'+N2Str(IdxRec); {error title for doerror boxes}

		{Set previous pointer - for sort checking}
		if PrevIndexItem <> nil then dispose(PrevIndexITem, done);
		PrevIndexItem := IndexItem;

		{Get index record}
		IndexItem := PIndexItem(Stream(fitype)^.GetAt(IdxRec));

		if IndexItem = nil then begin
			Control := DoError( StreamName,ETitle+#13+
												 'Cannot retrieve Index Item',
												 '1 - Leave; 2 - Put in Blank',1);

			if Control = cmButton2 then begin
				New(IndexItem, init);
				Stream(fiType)^.PutAt(idxRec, IndexItem);
			end;
		end;

		if IndexItem<>nil then begin

			{====== Check for lock ===========}
			if IndexItem^.LockTerminal <> 0 then begin
				 Error := Error + '; Locked, Terminal=' + N2Str(IndexItem^.LockTerminal);
				 {Auto Fix - unlock}
				 IndexItem^.LockTerminal := 0;
				 Stream(fiType)^.PutAt(IdxRec, IndexItem);
				 Action := Action + '; Unlocked';
			end;

			{========== Check for sort order ==============}
			if (PrevIndexItem<>nil) and (IndexItem^.GetKey < PrevIndexItem^.GetKey) then begin
				 Error := Error + '; Out of sort order ';
				 {no fix thought of yet}
			end;


			if not IndexItem^.Hole then begin

				{========== Check Data pointer valid ===========}

				{Check negative range}
				if (IndexItem^.Idx2Dat<0) and not IndexItem^.Hole then begin
					Control := DoError( StreamName, ETitle
															+' not Hole, but Idx2Dat='+N2str(IndexItem^.Idx2Dat),
															'1 - Leave; 2 - Set to Hole',2);
					if Control = cmButton2 then MakeIntoHole(fitype, IndexItem, IdxRec);
				end;

				{Check positive range}
				if (IndexItem^.Idx2Dat>=JimmyStream^.NoRecs) then begin
					Control := DoError(StreamName, ETitle
														 +' not Hole and Idx2Dat='+N2str(IndexItem^.Idx2Dat)+' past Dat eof='+N2Str(JimmyStream^.NoRecs),
														 '1 - Leave; 2 - Set to hole',2);

					if Control = cmButton2 then MakeIntoHole(fitype, IndexItem, IdxRec);
				end;

				{========== Check Data item corresponds ==========}

				if IndexItem^.Idx2Dat > -1 then begin

					{have to load full jimmy o/wise category-dependant indexes, etc
					don't work as category not set...}
					Jimmy := PJimmy(JimmyStream^.GetAt(IndexItem^.Idx2Dat));{}

					{--- Check for nil jimmy ---}
					if Jimmy=nil then begin
						Control := DoError(StreamName, ETitle+' Idx2Dat='+N2str(IndexItem^.Idx2Dat)+' returns nil Jimmy, srtype='
																+N2Str(JimmyStream^.ErrorInfo),
															 '1 - Leave; 2 - Set to hole',2);

						if Control = cmButton2 then MakeIntoHole(fitype, IndexItem, IdxRec);
						JimmyStream^.Reset;
					end else begin

						{--- fix for old ixtype = 99 = archive,now 0}
						if IndexItem^.ixType = 99 then begin
							IndexItem^.ixType := ixArchive;
							Stream(fiType)^.PutAt(IdxRec, IndexItem);
						end;

						{--- check if jimmy deleted and not archive, or vv}
						if fiType = fiArchiveIdx then begin
							if not Jimmy^.Deleted then begin
								Control := DoError(StreamName, ETitle+#13+
														'Archive Idx points to undeleted Jimmy '+N2Str(Jimmy^.RecNo),
														'1 - Leave; 2 - delete Jimmy; 3 - set to hole',2);

								if Control = cmButton2 then begin
									Jimmy^.Deleted := True;
									PutJimmy(Jimmy);
								end;

								if Control = cmButton3 then MakeIntoHole(fiType, IndexItem, IdxRec);
							end;
						end else begin
							{not archive index - check if deleted}
							if Jimmy^.Deleted then begin
								Control := DoError(StreamName, ETitle+#13+
														'Idx points to deleted Jimmy '+N2Str(Jimmy^.RecNo),
														'1 - Leave; 2 - undelete Jimmy; 3 - set to hole',2);

								if Control = cmButton2 then begin
									Jimmy^.Deleted := False;
									PutJimmy(Jimmy);
								end;

								if Control = cmButton3 then MakeIntoHole(fiType, IndexItem, IdxRec);
							end;
						end;

						{---- Check Back pointers & Index ----}
						BakPtr := Jimmy^.GetIdxPtr(IndexItem^.ixType);
						BakFitype := Jimmy^.GetfiType(IndexItem^.ixtype);

						if BakfiType<>fiType then begin
							Control := DoError(StreamName,Etitle+#13+
												'Jimmy fitype='+N2Str(BakfiType)+'<>index fitype='+N2Str(fitype)
												+'(Dat2Idx='+N2str(BakPtr)+' Index.ixtype='+N2Str(Indexitem^.ixtype)+')',
												'1 - leave; 2 - set to hole & bakptr -1',2);

							if Control = cmBUtton2 then begin
								MakeINtoHole(fitype, IndexItem, IdxRec);
								Jimmy^.SetIdxPtr(IndexItem^.ixType, -1);
								PutJimmy(Jimmy);
							end;
						end else begin

							if Bakptr<> IdxRec then begin


								{--- Back pointer wrong ----}
								Default := 3;
								if Jimmy^.GetIdxPtr(IndexItem^.ixType)=-1 then
									S := ''
								else begin
									{Check to see if the one the data DOES point to, actually does point back to the data}
									TempIndexItem := PIndexItem(Stream(fiType)^.GetAt(BAkPtr));
									if TempIndexItem=nil then
										S := '--> nil'
									else begin
										if TempIndexItem^.Idx2Dat = IndexItem^.Idx2Dat then begin
											S := '<>Idx. That idxrec DOES point back';
											Default := 2;
										end else
											S := '<>Idx. That idxrec DOES NOT point back';
										if TempIndexItem<>nil then dispose(TempIndexItem, done);
									end;
								end;

								{check if deleted}
								if Jimmy^.Deleted then S := S + '(deleted)';

								{if deleted and not archive index, or v.v., default to making hole}
								if ((Jimmy^.Deleted) and (fitype<>fiArchiveIdx))
									or ((not Jimmy^.Deleted) and (fiType = fiArchiveIdx)) then Default := 2;


								{notify user}
								Control := DoError( StreamName+'/JIMMY',ETitle+#13+
																			'JImmy (sr'+N2Str(Jimmy^.srType)+'): Bakptr ix'+N2Str(IndexItem^.ixType)
																				+' Dat2Idx='+N2Str(Jimmy^.GetIdxPtr(IndexItem^.ixType))+' '+S,
																			'1 - Leave; 2 - Set Index to hole; 3 - Set back pointer to idxrec',Default);


								if Control = cmButton2 then MakeIntoHole(fitype, IndexItem, IdxRec);

								if Control = cmButton3 then begin
									{fix 3 - set back pointer}
									Action := Action + '; Dat2Idx Set to '+N2Str(IdxRec);
									Jimmy^.SetIdxPtr(IndexItem^.ixType, IdxRec); {set back pointer}
									PutJimmy(Jimmy);
								end;


							end; {wrong bakpointer}
						end; {bakfitype ok}

						{------- CHECK KEY STRINGS MATCH ---------}
						if IndexItem^.GetKey<>Jimmy^.GetIndexKey(IndexItem^.ixType) then begin

							S := '1 - leave; 2 - set Index key to Jimmy Key';
							Default := 2;
							if Jimmy^.GetIndexKey(IndexItem^.ixtype)='' then begin
								S := S + '; 3 - delete index item';
								Default := 3;
							end;

							Control := DoError(StreamName+'/JIMMY',ETitle+#13+
											'Index Key ('+IndexItem^.GetKey+')<> Jimmy Key ('+Jimmy^.GetIndexKey(IndexItem^.ixType)+')',
											S,Default);

							if Control = cmButton2 then begin
								IndexItem^.KeyString := Jimmy^.GetIndexKey(IndexItem^.ixType);
								Stream(fiType)^.PutAt(Idxrec, IndexItem);
							end;

							if Control = cmButton3 then begin
								Jimmy^.SetIdxPtr(IndexItem^.ixtype,-1);
								PutJimmy(Jimmy);
								MakeIntoHole(fitype, IndexItem, IdxRec);
							end;
						end;

						Dispose(Jimmy, done); Jimmy := nil;
					end; {JImmy not/is nil}

				end; {idx2dat<>-1}
			end; {index not hole}

		end; {index not nil}

		WriteErrors(IdxRec);

		if ProBox^.Command <> cmOK then break;

	end; {for loop throuhg index records}

	{tidy up after index check}
	if PrevIndexItem<>nil then dispose(PrevIndexItem);
	if IndexItem<>nil then dispose(IndexItem);

	{restart after cancelling index check}
	if ProBox^.COmmand = cmButton1 then begin
		ProBox^.Command := cmOk;
		append(ErrorFile);
		writeln(ErrorFile, 'Skipped check at ',IdxRec);
		Close(ErrorFile);
	end;

	FileAdmin(fiType)^.LogOff;
	FileAdmin(fiJimmys)^.LogOff;

end; {Check index procedure}

{**************************************************
 ***         CHECK STRING EXTENSION             ***
 **************************************************}
function CheckStringExtension(ErrCom : string; TextData : PFreeTextData) : boolean; {returns true if need to save}
	var Default, I : integer;
			Control : word;
			TextItem : PTextItem;
			PrevRec, Oldrec, L,RecNo : longint;
			TextStream : PTextStream; {shorthand access to fileptr}
begin
	CheckStringExtension := False;
	if (textData^.First = -1) and (TextData^.Text.Length = 0) then exit; {nothing to check, etc}

	RecNo := TextData^.First;                                      {First in chain}

	if recNo<-1 then begin
		Control := DoError('STRING EXTENSION',ErrCom+', FirstRec '+N2Str(recNo)+' out of range',
							'1 - Leave; 2 - Set to -1',-2);

		if Control = cmButton2 then begin
			TextData^.First := -1;
			LSSetLen(TextData^.Text, 0);
			Action := Action +'; First SE Set to -1';
			CheckStringExtension := True; {tell caller to save}
		end;

		exit;
	end;

	FileAdmin(TextData^.fiType)^.LogOn;
	TextStream := PTextStream(Stream(TextData^.fiType));

	{Read in chunks}
	L :=0; PrevRec := -1; OldRec := -1;
	while (L<TextData^.Text.Length) and (RecNo>-1) and (TextItem<>nil) do begin
		PrevRec := OldRec;
		OldRec := recNo;
		TextItem := PTextItem(TextStream^.GetNext(RecNo));

		if TextItem = nil then begin
			Control := DoError('STRING EXTENSION',
															ErrCom+', Text Item '+N2Str(OldRec)+'-> nil',
															'1 - Leave; 2 - set prev.NextID -1',1);

			if COntrol = cmButton2 then begin
				if PrevRec<>-1 then begin
					Action := Action + '; Previous set to -1';
					TextItem := PTextItem(TextStream^.GetAt(PrevRec));
					TextItem^.NextID := -1;
					TextStream^.PutAt(PrevRec, TextItem);
					dispose(TextItem, done);
					TextItem := nil;
				end else
					Action := Action + '; No previous';
			end;

		end else begin
			L := L + length(TextItem^.Data);
			dispose(TextItem, done);
		end;
	end;

	if recNo<-1 then begin
		DoError('STRING EXTENSION',
						ErrCom+', RecNo '+N2Str(recNo)+' out of range',
						'1 - Leave',1);
	end;


	{At the moment Solitaire's jobsheets have no
	datalengths set in jobsheets and so this produces loads of errors.  Also,
	text items from directory items have no terminating -1...
	To fix properly, the calling routine will need to store the jimmy/directory item/etc}
	if (RecNo>-1) and (TextItem<>nil) then begin

		{Yukety poo.  This is for Solitaire's messy system.  Some (old) jobsheet
		notes have no datalength set, so it will arrive here with recno set but
		no datalength.  Some (recent) were stored with a faulty end-of-chain
		marker, which means it needs to truncate...:}
		Default := -3; if TextData^.Text.Length = 0 then Default := -2;

		Control := DoError('STRING EXTENSION',
													ErrCom+', More in SE chain but none expected (Length='+N2Str(TextData^.Text.Length)+')',
													'1 - Leave; 2 - Reset Length; 3 - Truncate ',Default);{Force for solitaire}

		if Control = cmButton2 then begin
			while (RecNo<>-1) and (TextItem<>nil) do begin
				TextItem := PTextItem(TextStream^.GetNext(RecNo));

				if TextItem = nil then
					DoError('STRING EXTENSION',
									'Text Item '+N2Str(OldRec)+'-> nil','1 - Leave',1)
				else begin
					if RecNo = -1 then
						L := L + length(DelNulR(TextItem^.Data)) {ignore nul's at end of last one}
					else
						L := L + length(TextItem^.Data);
					dispose(TextItem, done);
				end;
			end;

			Action := Action + '; Length set to '+N2Str(L);
			LSSetLen(TextData^.Text, L);
			CheckStringExtension := True; {tell caller to save}
		end;

		if Control = cmButton3 then begin
			TextItem := PTextItem(TextStream^.GetAt(OldRec));
			TextItem^.NextID := -1;
			TextStream^.PutAt(OldRec, TextItem);
			Action := Action +'; Truncated';
			CheckStringExtension := True;
		end;

	end;{}

	FileAdmin(TextData^.fiType)^.LogOff;

	if L<TextData^.Text.Length then
		DoError('STRING EXTENSION',
							ErrCom+', SE Chain Ended but more expected','1 - Leave',1);



end;


{*****************************************************************
 ***           CHECK JIMMY'S INDEX RECORDS & BACK POINTERS     ***
 *****************************************************************}

procedure CheckJImmysIndexes(ErrTitle, ErrComment : string; Jimmy : PJimmy);
var TempIndexItem, IndexItem : PIndexItem;
		Control : word;
		fiType : byte;
		Default : integer;
		TempJimmy : PJimmy;
		ixType : byte;
		AllPtrs, EDetails : string;
		IdxRec : longint;
		S : string;


begin
	ThinkingOn(ErrTitle);

	{build a string listing all pointers}
	AllPtrs := '';
	for ixType := 0 to Jimmy^.Numixtypes do
		if Jimmy^.Getfitype(ixType)>0 then
			AllPtrs := AllPtrs + N2Str(Jimmy^.GetIdxPtr(ixtype))+'/';

	if Jimmy^.DEleted then AllPtrs := AllPtrs + ' Del';

	{==== TEST INDEX RECORD ===}
	for ixType := 0 to Jimmy^.NumixTypes do begin

		IdxRec := Jimmy^.GetIdxPtr(ixType);
		fiType := Jimmy^.GetfiType(ixtype);

		if ((fiType=0) and (IdxREc<>-1)) then
			DoError(ErrTitle,'Jimmy ID '+N2Str(Jimmy^.RecNo)+' ix'+N2Str(ixType)+
										'  fitype=0 but IdxRec='+N2Str(IdxRec),'',1);


		if fiType>0 then begin

			EDetails := 'ix'+N2Str(ixtype)+' fi'+N2Str(fiType)+' Jimmy ID'+N2Str(Jimmy^.Recno)+' IdxRec'+N2Str(IdxRec)+#13+
								+'Ptrs: '+AllPtrs;

			if IdxRec>-1 then begin
				FileAdmin(fiType)^.LogOn;

				{--- Check Range -----}
				if IdxRec>=Stream(fiType)^.NoRecs then begin

					Control := DoError(ErrTitle+' JIMMY/INDEX',
															EDetails+'IdxPtr '+N2Str(IdxRec)+' out of range',
															'1 - Leave;  2 - Insert new index item;  3 - Set Dat2Idx=-1',3);

					if Control = cmButton2 then InsertNewIndexItem(fiType, ixType, Jimmy);

					if Control = cmButton3 then begin
						Action := Action +'; Dat2Idx set to -1 ';
						Jimmy^.SetIdxPtr(ixtype, -1);
						Jimmy^.StoreIdxPtr(ixtype);
					end;
				end else begin

					IndexItem := PIndexItem(Stream(fiType)^.GetAt(IdxREc));

					{========== NIL? ======================}
					if indexItem = nil then begin

						Control := DoError(ErrTitle+' JIMMY/INDEX',
															EDetails+'Indexitem --> nil',
															'1 - Leave;  2 - Insert new index item;  3 - Set Dat2Idx=-1',3);

						if Control = cmButton2 then InsertNewIndexItem(fiType, ixType, Jimmy);

						if Control = cmButton3 then begin
							Action := Action +'; Dat2Idx set to -1 ';
							Jimmy^.SetIdxPtr(ixtype, -1);
							Jimmy^.StoreIdxPtr(ixtype);
						end;

					end else begin
						{IndexItem <> nil}
						{Archive/Deleted}
						if ixType = ixArchive then begin
							{must be a valid archive pointer}
							if not Jimmy^.Deleted then begin
								Control := DoError(ErrTitle+' JIMMY/INDEX', EDetails+#13+
																	'NOT deleted but points to Archive',
																	'1 - Leave; 2 - Hole Archive; 3 - Delete',1);

								if Control = cmButton2 then MakeIntoHole(fitype, IndexItem, IdxRec);

								if Control = cmBUtton3 then begin
									Jimmy^.Deleted := True;
									PutJimmy(Jimmy);
								end;
							end;
						end else begin
							{not archive index}
							if Jimmy^.Deleted then begin
								if IndexItem^.Idx2Dat = Jimmy^.RecNo then begin
									S := 'IndexItem points back';
									Default := 2;
								end else begin
									S := 'IndexItem does NOT point back';
									Default := 3;
								end;
								Control := DoError(ErrTitle+' JIMMY/INDEX', EDetails+#13+
																	'Deleted but points to NON-Archive '+N2Str(IdxRec)+#13+S,
																	'1 - Leave; 2 - Hole Index; 3 - Set Dat2Idx to -1; 4 - Undelete',Default);

								if Control = cmButton2 then MakeIntoHole(fitype, IndexItem, IdxRec);

								if Control = cmButton3 then begin
									Action := Action +'; Dat2Idx set to -1 ';
									Jimmy^.SetIdxPtr(ixtype, -1);
									Jimmy^.StoreIdxPtr(ixtype);
									{so we don't do any more checks}
									dispose(IndexItem, done);
									IndexItem := nil;
								end;

								if Control = cmBUtton4 then begin
									Jimmy^.Deleted := False;
									PutJimmy(Jimmy);
								end;
							end;
						end;
					end; {indexitem<>nil}

					if IndexItem<>nil then begin {do a recheck in case we set it to nil above}

						{======== HOLE =============}
						if IndexItem^.Hole then begin
							if IndexItem^.Idx2Dat = Jimmy^.RecNo then Default := 2  {points back ok so default to unhole}
								else if IndexItem^.Idx2Dat = -1 then Default := 3 {no ptr, probably deleted1 somehow so default deleting data}
											else Default := 1;  {o/w leave}

							Control := DoError( ErrTitle+' JIMMY/INDEX',EDetails+#13+
															 'IndexItem hole, Idx2Dat='+N2Str(IndexItem^.Idx2Dat),
															 '1 - Leave; 2 - Insert new IndexItem; 3 - Unhole; 4 - Set Dat2Idx = -1',Default);

							if Control = cmButton2 then InsertNewIndexItem(fiType, ixType, Jimmy);

							if Control = cmButton3 then begin
								Action := Action + '; Unholed';
								IndexItem^.Hole := False;
								Stream(fiType)^.PutAt(IdxRec, IndexItem);
							end;

							if Control = cmButton4 then begin
								Action := Action +'; Dat2Idx set to -1 ';
								Jimmy^.SetIdxPtr(ixtype, -1);
								Jimmy^.StoreIdxPtr(ixtype);
							end;
						end;

						{======= BACK POINTER ==========}
						if not (IndexItem^.Hole) and (IndexItem^.Idx2Dat <> Jimmy^.RecNo) then begin

							{back pointer not right, so check what it really points to}
							if IndexItem^.Idx2Dat > -1 then
								TempJimmy := PJimmy(JimmyStream^.GetAt(IndexItem^.Idx2Dat))
							else
								TempJimmy := nil;

							if TempJimmy = nil then begin
								S := '(--> nil)';
								Default := 3;
							end else begin
								if TempJimmy^.GetIdxPtr(IndexItem^.ixtype) = IdxRec then begin
									{other jimmy points to correct index item}
									Default := 2;
									S := '(That jimmy DOES point back)';
								end else begin
									S := '(That jimmy does NOT point back)';
									Default := 3;
								end;
								dispose(TempJimmy, done);
							end;

							Control := DoError(	ErrTitle+' DATA/INDEX',EDetails+#13+
																			+'Bakptr Idx2Dat='+N2Str(IndexItem^.Idx2Dat)+'<>DatRec '+S,
																			'1 - Leave; 2 - Create new index item; 3 - set Idx2Dat to JImmy ID; 4 - Set Dat2Idx -1',Default);

							case Control of
								cmButton2 : InsertNewIndexItem(fiType, ixType, Jimmy);

								cmButton3 : begin
									Action := Action + '; Idx2Dat set to'+N2Str(Jimmy^.RecNo);
									IndexItem^.Idx2Dat := Jimmy^.RecNo;
									Stream(fiType)^.PutAt(IdxRec, IndexItem);
								end;

								cmButton4 : begin
									Action := Action + '; Dat2Idx set to -1';
									Jimmy^.SetIdxPtr(ixtype, -1);
									Jimmy^.StoreIdxPtr(ixtype);
								end;

							end; {case control}

						end; {If not a hole and bakptr wrong}

						Dispose(IndexItem, done);

					end; {if indexitem <> nil}

				end; {if not in range}

				FileAdmin(fiType)^.LogOff;

			end else {if jimmy's dat2idx <> -1}
				{IdxRec<=-1}
				if IdxRec<-1 then begin {out of range}
					Control := DoError( ErrTitle+' JIMMY #'+N2Str(Jimmy^.RecNo),
																	 'Dat2Idx='+N2Str(IdxRec)+' out of range',
																	 '1 - Leave; 2 - Set Dat2Idx -1',-2);

					if Control = cmButton2 then begin
						Action := Action + '; Dat2Idx set to -1';
						Jimmy^.SetIdxPtr(ixtype, -1);
						Jimmy^.StoreIdxPtr(ixtype);
					end;

				end;

		end; {fitype >0}
	end; {for ixType}
	ThinkingOff;
end;

{*******************************************************************
 ***              CHECK A JIMMY'S HOOKS                          ***
 ******************************************************************}
{returns true if it jimmy needs to be saved (ie firstinchain has changed}
procedure CheckJimmysHooks(ErrTitle, ErrComment : string; OwningJimmy : PJimmy; hkType : byte);
var FirstChainID : longint;
		Default : integer;
		Control : word;
		EDetails : string;
		Jimmy : PJimmy;

		{for checking attached chains}
		ChainRec : longint;
		TempHook, Hook : PHook;
		ItemString : string;
		HookInfo : string;
		S : string;

		{for checking looping}
		CheckLoopA : array[0..99] of longint;
		CheckLoop : byte;
		ChainPos : byte;

		{For checking chain}
		PrevRec : longint;


begin
	FirstChainID := OwningJimmy^.GetFirstHookPtr(hkType);
	if FirstChainID = -1 then exit; {nothing to do}

	ThinkingOn(ErrTitle);

	for CheckLoop := 0 to 99 do CheckLoopA[CheckLoop] := -1;
	ChainPos := 0;
	PrevRec := -1;

	FileAdmin(fiHooks)^.LogOn;

	{========= CHECK FIRST IN CHAIN ===============}

	ChainRec := FirstChainID;
	Hook := PHook(HookFile^.GetAt(ChainRec));

	ErrTitle := ErrTitle + ' HOOK ERROR ';
	EDetails := 'Jimmy ID='+N2Str(OwningJimmy^.RecNo)+' hktype='+N2Str(hkType);

	if Hook = nil then begin

		Control := DoError( 'JIMMY/'+Errtitle, EDetails+#13+
														'nil FirstHookPtr='+N2Str(ChainRec),
														'1 - leave;  2 - set FirstHookPtr to -1',2);
		if Control = cmButton2 then begin
			FirstChainID := -1;
			ChainRec := -1;
		end;

	end else begin

		{Check pointer to chain is at start of chain}
		if Hook^.PrevID > -1 then begin
			TempHook := PHook(HookFile^.GetAt(Hook^.PrevID));
			if (TempHook<>nil)  then begin

				if TempHook^.NextID = ChainRec then Default := 3	else Default := 2;

				if TempHook^.OwnerID <> Hook^.OwnerID then Default := 2;

				S := 'Prev.NextID'+N2Str(TempHook^.NextID)+'  Prev.Owner='+N2Str(TempHook^.OwnerID);

				dispose(TempHook, done);

			end else begin
				S := 'Prev --> nil';
			end;

			Control := DoError( 'JIMMY/'+Errtitle, EDetails+#13+
												'FirstHookPtr='+N2Str(ChainRec)+' not FirstinChain, Prev='+N2Str(Hook^.PrevID)+' '+S,
											'1 - Leave; 2 - Prev -1; 3 - FirstHookPtr prev; 4 - FirstHookPtr -1', default);

			if Control = cmButton2 then begin
				Hook^.PrevID := -1;
				HookFile^.PutAt(ChainRec, Hook);
				Action := Action + '; Prev set to -1';
			end;

			if Control = cmButton3 then begin
				{FIX - mark firstinchain as previous}
				Action := Action + '; FirstHookPtr set to prev='+N2Str(Hook^.PrevID);
				FirstChainID := Hook^.PrevID;
			end; {3 fix}

			if Control = cmButton4 then begin
				{FIX - mark firstinchain as nil}
				Action := Action + '; FirstHookPtr set to -1';
				FirstChainID := -1;
			end;

			{Restore to first in chain - eg for above errors}
			ChainRec := FirstChainID;

		end else {check at start of chain (prev>-1)}

			if Hook^.PrevID<-1 then begin
				Control := DoError( 'JIMMY/'+Errtitle,EDetails+#13+
															'ChainRec ('+N2Str(ChainRec)+') .PrevID='+N2Str(Hook^.PrevID)+' out of range = '+N2Str(Hook^.PrevID),
															'1 - Leave; 2 - Set Prev to -1;', -2);

				if Control = cmButton2 then begin
					Hook^.PrevID := -1;
					HookFile^.PutAt(ChainRec, Hook);
					Action := Action + '; Prev set to -1';
				end;

			end;

		dispose(Hook, done);

	end;  {Hook not nil}


	{========= ALL CHAIN ========================}
	while ChainRec>-1 do begin

		Hook := PHook(HookFile^.GetAt(ChainRec));

		if Hook<>nil then begin

			{check for looping}
			CheckLoopA[ChainPos] := ChainRec;
			if ChainPos<99 then inc(ChainPos);
			for CheckLoop := 0 to 99 do
				if (CheckLoopA[CheckLoop] = Hook^.NextID) and (Hook^.NextID <> -1) then begin
					{Pointer to next already exists in chain}

					Control := DoError('JIMMY/'+Errtitle, EDetails+#13+
										'Chain loops on itself: Rec '+N2Str(ChainRec)+' .NextID->'+N2str(Hook^.NextID)+' already in chain',
										'1 - Leave; 2 - Terminate',2);

					if Control = cmButton2 then begin
						Action := Action + '; Terminated';
						Hook^.NextID := -1;
						HookFile^.PutAt(ChainRec, Hook);
					end;

					Hook^.NextID := -1; {set it to blank anyway so test routine doesn't loop}

				end;

			Hook^.RecNo := ChainRec;
			HookInfo := N2Str(Hook^.RecNo)+' N'+N2Str(Hook^.NextID)+' P'+N2Str(Hook^.PrevID)
											+' Key'+N2Str(Hook^.SortKey)+' Own'+N2Str(Hook^.OwnerID)
											+' sr'+N2Str(Hook^.srType)+' J'+N2Str(Hook^.JimmyID);

			{-- check pointer to previous ---}
			{don't bother if there is no previous - that's tested for above in firstinchain}
			if (Hook^.PrevID <> PrevRec) and (PrevRec<>-1) then begin

				Control := DoError( 'JIMMY/'+Errtitle, EDetails+#13+
															'Hook ('+HookInfo+') .PrevID<>Previous Rec='+N2Str(PrevRec),
															'1 - Leave; 2 - set Prev to Previous Rec',2);

				if Control = cmButton2 then begin
					Action := Action + '; Prev set to '+N2Str(PrevRec);
					Hook^.PrevID := PrevRec;
					HookFile^.PutAt(ChainRec, Hook);
				end;

			end;

			{--- Check pointer to owner ----}
			if Hook^.OwnerID<>OwningJimmy^.RecNo then begin

				Default := 1;
				if (Hook^.OwnerID <= -1) or (Hook^.OwnerID>JimmyStream^.NoRecs-1) then Default := -2;

				Control := DoError( 'JIMMY/'+Errtitle, EDetails+#13+
													'Hook ('+HookInfo+') Ptr2Owner<>Owning JimmyID',
													'1 - Leave; 2 - set Ptr2Owner to JimmyID; 3 - set Owners FirstHookPtr=-1',Default);

				if Control = cmButton2 then begin
					Action := Action + '; Ptr2Owner set to '+N2Str(OwningJimmy^.RecNo);
					Hook^.OwnerID := OwningJimmy^.RecNo;
					HookFile^.PutAt(ChainRec, Hook);
				end;

				if Control = cmButton3 then begin
					FirstChainID := -1;
				end;
			end; {owning pointer check}

			{------Check hooked Jimmy exists --------}
			Jimmy := PJimmy(JimmyStream^.GetAt(Hook^.JimmyID));

			if Jimmy = nil then begin
				Control := DoError('JIMMY/'+Errtitle+' HOOK/JIMMY ERROR',EDetails+#13+
									'nil Jimmy, Hook ('+HookInfo+')'
											+' '+N2Str(JimmyStream^.STatus)+'/'+N2Str(JimmyStream^.ErrorInfo),
									'1 - Leave; 2 - Delete from chain',2);  {temporary -2 to fix solitaire}

				JimmyStream^.reset;

				if Control = cmButton2 then begin
					Action := Action + '; Deleted';
					HookFile^.DeleteLink(ChainRec, FirstChainID);
					ChainRec := PrevRec; {makes prevrec below set to prevrec here, so don't get problem thinking things are different}
				end;

			end else
					Dispose(Jimmy, done);



			PrevRec := ChainRec;

			ChainRec := Hook^.NextID;

			dispose(Hook, done); Hook := nil;

		end else begin
			{Hook nil}
			Control := DoError( 'JIMMY/'+Errtitle, EDetails+#13+
														'nil Hook '+N2Str(ChainRec),
														'1 - Leave; 2 - set Prev.NextID -1',2);

			if (Control=cmButton2) then begin
				if PrevRec>-1 then begin
					Action := Action + '; Set prev.NextID -1';
					Hook := PHook(HookFile^.GetAt(PrevRec));
					Hook^.NextID := -1;
					HookFile^.PutAt(PrevRec, Hook);
				end else
					Action := Action +'couldnt set prev.NextID -1';
			end;

			ChainRec := -1; {force end of chain}

		end;

		{Ought to also check that each next points to prev back pointer, etc}

	end;

	WriteErrors(OwningJimmy^.RecNo);

	if OwningJimmy^.GetFirstHookPtr(hkType)<>FirstChainID then begin
		OwningJimmy^.SetFirstHookPtr(hkType, FirstChainID);
		OwningJimmy^.StoreFirstHookPtr(hkType);
	end;

	FileAdmin(fiHooks)^.LogOff;
	ThinkingOff;
end;

{*******************************************************************
 ***                                                             ***
 ***               CHECK INDIVIDUAL JIMMY TYPES                  ***
 ***                                                             ***
 ******************************************************************}


{************************************************
 ***        DIRECTORY ITEM                    ***
 ************************************************}
{$IFDEF kdirctry}
{============ PERSON ===================}
procedure CheckPerson(Jimmy : PJimmy);
var Person : PPerson;
begin
	Person := PPerson(Jimmy);

	if (typeof(Person^)<>typeof(TPerson)) then begin
		DoError(  'PERSON CHECK '+N2Str(Person^.RecNo),
							'Expecting Person item but srtype='+N2Str(Person^.srtype)+' instead',
							'1 - Leave',1);
		exit;
	end;

	{Following checks return true if saving required}
{$B+}{Force compiler to evaluate *all* of following functions - o/w it
			will shortcut as soon as one registers true}
	if {CheckPtr2Jimmy('PERSON','Ptr2Inv',Person^.Ptr2Inv)
		or CheckPtr2Jimmy('PERSON','Ptr2Ref',Person^.Ptr2Ref)
			or{} CheckPtr2Jimmy('PERSON','Ct/For',Person^.ContactFor) then

				PutJimmy(Person);
{$B-}

	WriteErrors(Person^.RecNo);
end;

{============ COMPANY ===================}
procedure CheckCompany(Jimmy : PJimmy);
var Company : PCompany;
begin
	Company := PCompany(Jimmy);

	if (typeof(Jimmy^)<>typeof(TCompany)) then begin
		DoError(  'COMPANY CHECK '+N2Str(Jimmy^.RecNo),
							'Expecting Company item but srtype='+N2Str(Jimmy^.srtype)+' instead',
							'1 - Leave',1);
		exit;
	end;

	{Following checks return true if saving required}
{$B+}{Force compiler to evaluate *all* of following functions - o/w it
			will shortcut as soon as one registers true}
{	if CheckPtr2Jimmy('COMPANY','Ptr2Inv',Company^.Ptr2Inv)
		or CheckPtr2Jimmy('COMPANY','Ptr2Ref',Company^.Ptr2Ref) then

				PutJimmy(Company);{}
{$B-}

	WriteErrors(Company^.RecNo);
end;

{$ENDIF}




{************************************************
 ***                LETTER                    ***
 ************************************************}
{$IFDEF kletter}
procedure CheckLetter(Jimmy : PJimmy);
var Letter : PLetter;
		ItemRec : longint;
		ES : string;
		Default, Control : word;

begin
	ES := 'JIMMY FILE ERROR (LETTER @'+N2Str(Jimmy^.RecNo)+')';

	if (typeof(Jimmy^)<>typeof(TLetter)) then begin
		DoError(  ES,
							'Expecting Letter (srtype) but typeof not matching',
							'1 - Leave',1);
		exit;
	end;

	Letter := PLetter(Jimmy);

		{---- Check if it does point to ChainOwnerID somewhere  ------}
		{Not necessarily a program bug - it's possible for a user to do this
		by changing the Input Dirs after storing I think...}
		{Also, will throw up an error if you are looking at the history
		chain of a company contact, where the invoice is to a person and
		your looking at the company chain...}
{    if (ChainOwnerID<>-1)
			and ((ChainOwnerID<>Letter^.ToWho) and (Letter^.ToWho<>-1))
			and ((ChainOwnerID<>Letter^.ByWho) and (Letter^.ByWho<>-1))
			and ((ChainOwnerID<>Letter^.ReWho) and (Letter^.ReWho<>-1)) then
				DoError(ES,'',
								'ToWho, ReWho & ByWho dont point to Dir owning chain',
								'1 - Leave',1);{}
	{ought to check address...}


{	if ChainOwnerID<>-1 then WriteErrors(ChainOwnerID); {helps to spread errors down page
	rather than along line when checking directory chains}
end;
{$ENDIF}



{*******************************************************************
 ***                                                             ***
 ***               CHECK JIMMYS                                  ***
 ***                                                             ***
 ******************************************************************}
{finds out which kind of jimmy it is, runs the check for that jimmy
and returns also the size of the record on disk.  Therefore the jimmy
can be checked from the index checker, the hooks checker and the main
jimmy data file checker...}

procedure DoCheckJimmy(var Jimmy : PJimmy; var RecSize : word); far;
var hktype : byte;
begin
	ProBox^.Update('#Errs='+N2Str(NumErrors)+' Last='+LastError+#13+
									LastAction+'  | Jimmys ',Jimmy^.RecNo,JimmyStream^.NoRecs-1);

	{=========== CHECK INDEX RECORDS & BACK POINTERS =================}
	if CheckType.Indexes then
		CheckJimmysIndexes('JIMMY INDEX CHECK','',Jimmy);

	{=================== CHECK HOOKS ======================}
	if CheckType.Hooks then
		for hkType := 1 to 9 do
			CheckJimmysHooks('JIMMY HOOK CHECK', '', Jimmy, hkType);

	{$IFDEF kdirctry}
		if typeof(Jimmy^)=typeof(TPerson) then CheckPerson(Jimmy);
		if typeof(Jimmy^)=typeof(TCompany) then CheckCompany(Jimmy);
	{$ENDIF}

	{$IFDEF kinvoice}
{		if typeof(Jimmy^)=typeof(Tinvoice) then CheckInvoice(ChainOwnerID, Jimmy);{}
	{$ENDIF}

	{$IFDEF KLetter}
		if typeof(Jimmy^)=typeof(TLetter)   then CheckLetter(Jimmy);
	{$ENDIF}

	{$IFDEF KDiary}
		if typeof(Jimmy^)=typeof(TDiaryRepeater) then with PDiaryRepeater(Jimmy)^ do begin
			{$B+}{Force compiler to evaluate *all* of following functions}
			if CheckPtr2Jimmy('DIARY REPEATER','Ptr2Jimmy',JimmyID) then
				PutJimmy(Jimmy);
			{$B-}
		end;
	{$ENDIF}

end;


procedure CheckJimmys;
begin
	FileAdmin(fiJimmys)^.LogOn;
	FileAdmin(fiHooks)^.LogOn;

	append(ErrorFile);
	writeln(ErrorFile,'JIMMY FILE recs 0-'+N2Str(JimmyStream^.NoRecs));
	close(ErrorFile);
	LastError := ''; LastAction := ''; {Reset}
	ProBox^.ResetTime;

	ProBox^.SetState(sfDisabled, True);
	ProBox^.Command := ForAllJimmys('JIMMY FILE CHECK','Checking Jimmys',DoCheckJimmy,OnNilDoStd);
	ProBox^.SetState(sfDisabled, False);

	FileAdmin(fiJimmys)^.LogOff;
	FileAdmin(fiHooks)^.LogOff;
end;


{*************************************************************
 ***           OVERALL ROUTINE - ASK USER WHAT TO CHECK    ***
 *************************************************************}

procedure CheckDBase; far;
var EditBox : PEditBox;
		R : Trect;
		Control : word;
		fiType : byte;
begin
	R.Assign(0,0,35,16);
	New(EditBox, init(R,'Check DataBase',nil));
	EditBox^.Options := EditBox^.Options or ofCentered;

	CheckType.Query := fxQueryFix;
	CheckType.Output := 'DBASECHK.$$$';
	CheckType.Indexes := True;
	CheckType.Hooks := True;
	CheckType.Jimmys := True;

	with EditBox^ do begin

		InsTitledField(15, 2,14, 3, 'Query', New(PRadioButtons, init(R, NewSItem('~C~heck only',
																																		NewSITem('~Q~uery Fix',
																																		NewSItem('~A~uto Fix', nil))))));
		InsTitledField(15, 6,12, 1, 'Output to', New(PInputFName, init(R,12,'', '', False, nil)));

		InsTitledField(15, 8, 1, 1, 'Indexes', New(PInputBoolean, init(R)));
		InsTitledField(15, 9, 1, 1, 'Hooks', New(PInputBoolean, init(R)));
		InsTitledField(15,10, 1, 1, 'Jimmys', New(PInputBoolean, init(R)));

		InsOKButton(5,13, @CheckType);
		InsCancelButton(15,13);
		EndInit;
		SetData(CheckType);
	end;

	Control := Desktop^.ExecView(EditBox);
	dispose(Editbox, done);

	if Control = cmCancel then exit;

	ProBox := NewProgressBox('CHECKING DATABASE','',mfCancelButton or mfSkipButton,hcNoContext);

	ProBox^.MoveTo(10,4);      {move out of the way}

	NumErrors := 0;
	LastError := '';
	LastAction := '';
	Error := '';  Action := ''; {No error so far}

	Assign(ErrorFile, CheckType.Output);{}
	rewrite(ErrorFile);
	writeln(ErrorFile,'--------------- DATABASE CHECK ------------------');
	writeln(ErrorFile,Today.Text(daAbbr)+'  '+TimeNow.Digit5);
	close(ErrorFile);

	if CheckType.Indexes then
		for fiType := 1 to 99 do if (fiType<>fiFoundIdx) and (fiType<>fiSpecialDirIdx) then
			if (FileAdmin(fiType)<>nil) and (ProBox^.Command=cmOK) then begin
				FileAdmin(fiType)^.LogOn;
				if Right(Stream(fiType)^.FileName,4)='.IDX' then
					CheckIndex(fiType);
				FileAdmin(fiType)^.LogOff;
			end;

	if (ProBox^.Command<>cmCancel) and CheckType.Jimmys then CheckJimmys;

	{tidy up error check}
	append(ErrorFile);
	if ProBox^.Command <> cmOK then
		writeln(ErrorFile, '...Cancelled')
	else
		writeln(ErrorFile,'...end of report');
	close(ErrorFile);

	Dispose(ProBOx, done);  {also removes from desktop}

	if (CheckType.Query = fxCheck) or (CheckType.Query = fxAutoFix) then
		if NumErrors >0 then
			ProgramWarning('Errors found'+#13+'See file'+CheckType.Output, hcNoContext)
		ELSE BEGIN
			DoneBleep;
			PauseMessage('DBASE CHECK','No Errors found', hcNoContext)
		end;
end;


begin
	RegisterTask(DesktopTasks,cmCheckDBase, @CheckDBase);

end.

