Modula-2でプログラミング -- 文字列探索プログラム find の制作(その1)2024年12月28日 21:00

指定された文字列を 指定されたファイルの中から探し出すプログラム find を紹介します。 このように使います。

C0#find

arguments>GetLine *.def *.mod

C:STDFILEI.DEF 6:       GetLine;
C:STDFILEI.DEF 8:PROCEDURE GetLine(VAR pFileIO: pFileIOStruct;
C:STDFILEI.MOD 10:PROCEDURE GetLine(VAR pFileIO: pFileIOStruct;
C:STDFILEI.MOD 36:END GetLine;
C:FIND.MOD 8:FROM StdFileIO IMPORT GetLine;
C:FIND.MOD 71:                  Chars := GetLine(pFileIO,Line);


63K QP/M VER 2.7   (BIOS VER 1.0)

                   (C) 1988.12.1.  K.YOSHIDA

             2018.10.28.  MODEFIED BY H.KIDA
C0#find

arguments>GetChar *.def *.mod

C:BASICFIL.DEF 7:       ReadFile,GetChar;
C:BASICFIL.DEF 28:PROCEDURE GetChar(VAR pFileIO: pFileIOStruct): CHAR;
C:STDFILEI.MOD 3:       pFileIOStruct,GetChar;
C:STDFILEI.MOD 20:              C := GetChar(pFileIO);
C:BASICFIL.MOD 65:PROCEDURE GetChar(VAR pFileIO: pFileIOStruct): CHAR;
C:BASICFIL.MOD 89:END GetChar;


63K QP/M VER 2.7   (BIOS VER 1.0)

                   (C) 1988.12.1.  K.YOSHIDA

             2018.10.28.  MODEFIED BY H.KIDA
C0#

find は、ファイルを読みますので、ファイルアクセスのルーチンが必要になります。 Bdosを直接使うことができるので、かんたんなファイル読み込みモジュール BasicFileIO を作成しました。 更に、行単位のファイル読み出しが必要なので、上位のモジュール StdFileIO を作成しました。StdFileIOは、 Bdosとは、独立しています。

まず、 BasicFileIO です。基本的なファイルアクセスルーチンの集まりです。 今回は、読み出せれば良いので、書き込み関係のルーチンは作成していません。基本的なファイルのアクセスは、 一文字単位です、 定義モジュールは、以下のとおりです。

DEFINITION MODULE BasicFileIO;
FROM OpSys IMPORT FCB,FCBFileName;

EXPORT QUALIFIED 
	FileIOStatus,FileIOStruct,pFileIOStruct,
	OpenFile,CloseFile,EOFFile,
	ReadFile,GetChar;

CONST
	DMASize = 128;
TYPE
	FileIOStatus = (Success,InvalidFileName,
		OpenError,CloseError,IOError);
	DMA = ARRAY [0..DMASize-1] OF CHAR;
	pFileIOStruct = POINTER TO FileIOStruct;
	FileIOStruct = RECORD
		FCBBuf: FCB;
		DMABuf: DMA;
		ReadPos: CARDINAL;
		WritePos: CARDINAL;
		EOFFlag: BOOLEAN;
	END;

PROCEDURE OpenFile(FileName: ARRAY OF CHAR;
	VAR pFileIO: pFileIOStruct): FileIOStatus;
PROCEDURE CloseFile(VAR pFileIO: pFileIOStruct): FileIOStatus;
PROCEDURE ReadFile(VAR pFileIO: pFileIOStruct): FileIOStatus;
PROCEDURE GetChar(VAR pFileIO: pFileIOStruct): CHAR;
PROCEDURE EOFFile(pFileIO: pFileIOStruct): BOOLEAN;
END BasicFileIO.

実装モジュールは、以下のとおりです。手続き GetChar では、CR LF と連続した場合、 LF を読み飛ばします。

IMPLEMENTATION MODULE BasicFileIO;
FROM InOut IMPORT Write,WriteString,WriteLn;
FROM OpSys IMPORT BdosFunctions,FCBFileName,Bdos,FCB;
FROM FileNames IMPORT StrToFCB,NameState;
FROM SYSTEM IMPORT ALLOCATE,ADR,TSIZE,WORD;

CONST
	CR = 15C;
	LF = 12C;
	EOFMARK = 32C;

PROCEDURE OpenFile(FileName: ARRAY OF CHAR;
	VAR pFileIO: pFileIOStruct): FileIOStatus;
VAR
	BdosRc: CARDINAL;
	Junk: WORD;

BEGIN
	(* Allocate FCB and DMA / pre-Set *)
	ALLOCATE(pFileIO,TSIZE(FileIOStruct));
	pFileIO^.FCBBuf.name.text := '';
	pFileIO^.FCBBuf.rest := '';
	pFileIO^.DMABuf := '';
	IF StrToFCB(FileName,pFileIO^.FCBBuf.name) # NameOK THEN
		RETURN InvalidFileName
	END;
	Bdos(setDMA,ADR(pFileIO^.DMABuf),Junk);
	Bdos(openF,ADR(pFileIO^.FCBBuf),BdosRc);
	IF BdosRc # 255 THEN
		pFileIO^.ReadPos := DMASize;
		pFileIO^.WritePos := 0;
		pFileIO^.EOFFlag := FALSE;
		RETURN Success
	ELSE
		RETURN OpenError
	END
END OpenFile;

PROCEDURE CloseFile(VAR pFileIO: pFileIOStruct): FileIOStatus;
VAR
	BdosRc: CARDINAL;

BEGIN
	Bdos(closeF,ADR(pFileIO^.FCBBuf),BdosRc);
	IF BdosRc = 0 THEN
		RETURN Success
	ELSE
		RETURN CloseError
	END
END CloseFile;

PROCEDURE ReadFile(VAR pFileIO: pFileIOStruct): FileIOStatus;
VAR
	BdosRc: CARDINAL;

BEGIN
	Bdos(readSeq,ADR(pFileIO^.FCBBuf),BdosRc);
	IF BdosRc = 0 THEN
		RETURN Success
	ELSE
		RETURN IOError
	END
END ReadFile;

PROCEDURE GetChar(VAR pFileIO: pFileIOStruct): CHAR;
VAR
	C: CHAR;

BEGIN
	IF DMASize = pFileIO^.ReadPos THEN
		IF ReadFile(pFileIO) = Success THEN
			pFileIO^.ReadPos := 0
		ELSE
			pFileIO^.ReadPos := 0;
			pFileIO^.DMABuf[pFileIO^.ReadPos] := EOFMARK
		END
	END;
	C := pFileIO^.DMABuf[pFileIO^.ReadPos];
	IF C = CR THEN
		IF pFileIO^.DMABuf[pFileIO^.ReadPos+1] = LF THEN
			INC(pFileIO^.ReadPos)
		END
	END;
	INC(pFileIO^.ReadPos);
	IF C = EOFMARK THEN
		pFileIO^.EOFFlag := TRUE
	END;
	RETURN C
END GetChar;

PROCEDURE EOFFile(pFileIO: pFileIOStruct): BOOLEAN;
BEGIN
	RETURN pFileIO^.EOFFlag
END EOFFile;
END BasicFileIO.

GetChar で、 LF を読み飛ばす以外は、面倒くさいところは、ないでしょう。

この、 BasicFileIO モジュールをつかって、行単位の読み出しルーチンを作ってます。 StdFileIO モジュールです。 このモジュールは、 CP/M のファイル構造と独立です。必要最低限の行単位の読み出ししか作成していません。

定義モジュールは、以下の通り。

DEFINITION MODULE StdFileIO;
FROM BasicFileIO IMPORT
	pFileIOStruct;

EXPORT QUALIFIED 
	GetLine;

PROCEDURE GetLine(VAR pFileIO: pFileIOStruct;
		VAR LineBuffer: ARRAY OF CHAR): CARDINAL;
END StdFileIO.

実装モジュールは、以下の通りです。

IMPLEMENTATION MODULE StdFileIO;
FROM BasicFileIO IMPORT
	pFileIOStruct,GetChar;

CONST
	EOS = 0C;
	EOFMARK = 32C;
	CR = 15C;

PROCEDURE GetLine(VAR pFileIO: pFileIOStruct;
		VAR LineBuffer: ARRAY OF CHAR): CARDINAL;

VAR
	C: CHAR;
	CPos: CARDINAL;

BEGIN
	CPos := 0;
	LOOP
		C := GetChar(pFileIO);
		CASE C OF
			EOFMARK:
				LineBuffer[CPos] := EOS;
				RETURN CPos
		|	CR:
				LineBuffer[CPos] := EOS;
				RETURN CPos
		END;
		LineBuffer[CPos] := C;
		INC(CPos);
		IF CPos = HIGH(LineBuffer) THEN
			LineBuffer[CPos] := EOS;
			RETURN CPos
		END
	END
END GetLine;
END StdFileIO.

GetLine は、一文字読み出しを繰り返し、 CR または EOF が来たら、その一文字前の文字までを一行とします。 また、バッファーのサイズ以上に読み取ろうとした場合は、強制的に一行にしてしまいます。溢れてしまうと困りますので。

find 本体の紹介は次回にします。

Modula-2 でプログラミング -- 文字列探索プログラム find の製作 (その2)2024年12月28日 21:13

BasicFileIO モジュール、StdFileIO モジュールをつかった 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 pFileList,FileNameNode,MakeFileList;
FROM BasicFileIO IMPORT FileIOStruct,pFileIOStruct,
	OpenFile,CloseFile,EOFFile,FileIOStatus;
FROM StdFileIO IMPORT GetLine;

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

VAR
	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
			MatchLines(pList^.FileName,Target);
			pList := pList^.Next
		END;
		INC(FileNumber)
	END
END Find.

MachLine で、指定された文字列が読み込んだ行に存在するかを検査し、存在したら書き出します。

ファイル名のリスト構造が丸見えなのでひと工夫する必要がありますが、今回は見送りました。