Modula-2 でプログラミング -- 文字列探索プログラム find の製作 (その3) --2025年01月05日 21:19

前回紹介したfindプログラムは、FileSearchモジュールの不備で、ファイル一覧の構造が 丸見えになっていました。FileSearchモジュールに2つ手続きを追加して不具合を解消しました。

新しいFileSearchモジュールの定義モジュールは以下の通り。

DEFINITION MODULE FileSearch;

FROM OpSys IMPORT FCB,FCBFileName;

EXPORT QUALIFIED pFileList,FileNameNode,
	GetNextFile,GetFileName,LoginDisk,MakeFileList,DumpFileList;

CONST
	DMABufferSize = 128;
	FileNameSize = 14;

TYPE
	pFileList = POINTER TO FileNameNode;
	FileNameNode = RECORD
		FileName: ARRAY [0..FileNameSize] OF CHAR;
		Next: pFileList
	END;
        DMA = ARRAY [0..DMABufferSize-1] OF CHAR;

PROCEDURE GetNextFile(FileLIst: pFileList): pFileList;
PROCEDURE GetFileName(FileList: pFileList;VAR FileName: ARRAY OF CHAR);
PROCEDURE LoginDisk(FCBBuf: FCB): CHAR;
PROCEDURE MakeFileList(FIleMatch: ARRAY OF CHAR): pFileList;
PROCEDURE DumpFileList(pList: pFileList);
END FileSearch.
手続き、次のファイルを取り出すGetNextFileとファイル名を取り出すGetFileNameを追加しました。 実現モジュールは以下の通り。
IMPLEMENTATION MODULE FileSearch;
FROM InOut IMPORT WriteHex,Write,WriteString,WriteLn;
FROM OpSys IMPORT FCB,FCBFileName,BdosFunctions,Bdos;
FROM SYSTEM IMPORT ALLOCATE,TSIZE,ADR,WORD;
FROM FileNames IMPORT StrToFCB,FCBToStr,NameState;

CONST
	EOS = 0C;

PROCEDURE GetNextFile(FileList: pFileList): pFileList;
BEGIN
	RETURN FileList^.Next
END GetNextFile;

PROCEDURE GetFileName(FileList: pFileList; VAR FileName: ARRAY OF CHAR);
VAR
	i: CARDINAL; 
BEGIN
	i := 0;
	WHILE ( FileList^.FileName[i] # EOS ) DO
		FileName[i] := FileList^.FileName[i];
		INC(i)
	END;
	FileName[i] := EOS
END GetFileName;

PROCEDURE LoginDisk(FCBBuf: FCB): CHAR;
VAR
	DiskCode: CARDINAL;
	Junk: WORD;

BEGIN
	IF FCBBuf.name.disk = 0C THEN
		Bdos(retCDsk,Junk,DiskCode);
		RETURN (CHR(ORD(DiskCode)+ORD('A')));
	ELSE
		RETURN (CHR(ORD(FCBBuf.name.disk)+ORD('A')-1));
	END;
END LoginDisk;

PROCEDURE MakeFileList(FileMatch: ARRAY OF CHAR): pFileList;
VAR
	Disk: CHAR;
	FCBBuffer: FCB;
	BdosRc: CARDINAL;
	DMABuffer: DMA;
	CPos: CARDINAL;
	NameStatus: NameState;
	pList: pFileList;
	pNewFileName: pFileList;
	FCBFile: FCBFileName;
	Formatted: BOOLEAN;
	Junk: WORD;

BEGIN
	pList := NIL;

	(* Clear FCB Buffer and set file name *)
	FCBBuffer.name.text := '';
	FCBBuffer.rest := '';
	NameStatus := StrToFCB(FileMatch,FCBBuffer.name);

	Disk := LoginDisk(FCBBuffer);

	(* set DMA Bufer *)
	Bdos(setDMA,ADR(DMABuffer),Junk);

	(* find first much file *)
	Bdos(searchFst,ADR(FCBBuffer),BdosRc);
	WHILE BdosRc # 255 DO
		ALLOCATE(pNewFileName,TSIZE(FileNameNode));
		FOR CPos := 0 TO 11 DO
			FCBFile.text[CPos]
				:= DMABuffer[CPos+BdosRc*32]
		END;
		FCBFile.disk := CHR(ORD(Disk) - ORD('A') + 1);
		FCBToStr(FCBFile,pNewFileName^.FileName,FALSE);
		pNewFileName^.Next := pList;
		pList := pNewFileName;

		(* find next file *)
		Bdos(searchNxt,Junk,BdosRc);
	END;	
	RETURN pList
END MakeFileList;

PROCEDURE WriteFileName(FileName: ARRAY OF CHAR);
BEGIN
	WriteString(FileName);
	WriteLn
END WriteFileName;

PROCEDURE DumpFileList(pList: pFileList);
BEGIN
	WHILE pList # NIL DO
		WriteFileName(pList^.FileName);
		pList := pList^.Next;
	END;
END DumpFileList;
END FileSearch.

これらの変更を反映したfindは、以下のとおり。

MODULE Find;
FROM InOut IMPORT Write,WriteCard,WriteString,WriteLn;
FROM Files IMPORT FILE,FileState,Open,Close,Read;
FROM CmdArgs IMPORT Argc,Argv;
FROM FileSearch IMPORT GetFileName,GetNextFile,
	FileNameNode,MakeFileList,pFileList;
FROM BasicFileIO IMPORT FileIOStruct,pFileIOStruct,
	OpenFile,CloseFile,EOFFile,FileIOStatus;
FROM StdFileIO IMPORT GetLine;

CONST
	EOS = 0C;
	MAXLINE = 128;
	FILENAMESIZE = 14;
	EOFMARK = 32C;

VAR
	FileName: ARRAY [0..FILENAMESIZE] OF CHAR;
	FileMatch: ARRAY [0..FILENAMESIZE] OF CHAR;
	Target: ARRAY [0..MAXLINE] OF CHAR;
	CPos,FileNumber,LineNumber,nArgc: CARDINAL;
	pList: pFileList;

PROCEDURE putLine(FileName:ARRAY OF CHAR;Line:CARDINAL;
		Buffer: ARRAY OF CHAR);
BEGIN
	WriteString(FileName);
	Write(' ');WriteCard(Line,1);Write(':');
	WriteString(Buffer);
	WriteLn
END putLine;

PROCEDURE findTarget(Target,Line:ARRAY OF CHAR): BOOLEAN;
VAR
	t,l: CARDINAL;
	found: BOOLEAN;
BEGIN
	found := FALSE;
	t := 0; l := 0;
	WHILE (Line[l] # EOS) AND (NOT found) DO
		IF Line[l] = Target[0] THEN
			t := 1;
			WHILE Line[l+t] = Target[t] DO
				INC(t);
			END;
			IF Target[t] = EOS THEN
				found := TRUE;
			END;
		END;
		INC(l);
	END;
	RETURN found
END findTarget;

PROCEDURE Usage();
BEGIN
	WriteString('find');WriteLn;
	WriteString('      TargetString File1 File2 FIle3,,,,');WriteLn 
END Usage;

PROCEDURE MatchLines(FileName,TargetString: ARRAY OF CHAR);
VAR
	pFileIO: pFileIOStruct;
	Line: ARRAY [0..MAXLINE-1] OF CHAR;
	Chars: CARDINAL;
	NLines: CARDINAL;
	Junk: FileIOStatus;

BEGIN
	IF OpenFile(FileName,pFileIO) = Success THEN
		NLines := 1;
		WHILE NOT EOFFile(pFileIO) DO
			Chars := GetLine(pFileIO,Line);
			IF findTarget(TargetString,Line) THEN
				putLine(FileName,NLines,Line);
			END;
			INC(NLines);
		END;
		Junk := CloseFile(pFileIO)
	END
END MatchLines;

BEGIN
	nArgc := Argc();
	IF nArgc < 1 THEN
		Usage();
		HALT
	END;
	Argv(0,Target);
	FileNumber := 1;
	WHILE FileNumber < nArgc DO
		Argv(FileNumber,FileMatch);
		pList := MakeFileList(FileMatch);
		WHILE pList # NIL DO
			GetFileName(pList,FileName);
			MatchLines(FileName,Target);
			pList := GetNextFile(pList)
		END;
		INC(FileNumber)
	END
END Find.

これで、FileSearchモジュール内部のデータ構造をFindが知る必要がなくなりました。