久しぶりに、Modula-2でプログラミング --その4-- CmdArgsの修正 ― 2023年01月30日 15:50
先に公開した"CmdArgs"モジュールに危ないところがありました。エクスポートしている変数"Argc"は、
インポートしたモジュールで変更できます。変えられてしまうと、"CmdArgs"モジュールが誤作動します。これを
さけるために、引数の数を手続き"Argc()"で返すようにしました。 これで、不用意に"CmdArgs"モジュールにダメージを
与えることがなくなりました。
修正版の定義モジュール"CmdArgs.def"は、以下の通り。
DEFINITION MODULE CmdArgs; EXPORT QUALIFIED Argv,Argc; PROCEDURE Argc(): CARDINAL; PROCEDURE Argv(n: CARDINAL;VAR ArgString: ARRAY OF CHAR); END CmdArgs.
修正版の実装モジュール"CmdArgs.mod"は、以下の通り。
IMPLEMENTATION MODULE CmdArgs; FROM ASCII IMPORT nul,ht,lf,cr; FROM SYSTEM IMPORT ADR,WORD; FROM OpSys IMPORT BdosFunctions,Bdos,CPMStringBuffer; CONST SPACE = ' '; TAB = ht; COMMA = ','; SQUORT = "'"; DQUORT = '"'; EOS = nul; MAXARGS = 20; EMPTY = -1; VAR ArgList: ARRAY [0..MAXARGS-1] OF INTEGER; CommandLine: CPMStringBuffer; QChar: CHAR; c,s: CARDINAL; CommaFlag: BOOLEAN; nArgc: CARDINAL; Msg: ARRAY [0..80] OF CHAR; PROCEDURE Argc(): CARDINAL; BEGIN RETURN nArgc; END Argc; PROCEDURE Argv(n: CARDINAL;VAR ArgString: ARRAY OF CHAR); VAR s,d: CARDINAL; BEGIN IF nArgc < n THEN ArgString[0] := EOS; ELSIF ArgList[n] = EMPTY THEN ArgString[0] := EOS; ELSE s := ArgList[n]; d := 0; WHILE CommandLine.text[s] # EOS DO ArgString[d] := CommandLine.text[s]; INC(s);INC(d); END; ArgString[d] := EOS; END; END Argv; PROCEDURE skipBlanks(); BEGIN WHILE isBlankChar(CommandLine.text[c]) DO INC(c); END; END skipBlanks; PROCEDURE skipSeparator(); BEGIN WHILE isSeparator(CommandLine.text[c]) DO INC(c); END; END skipSeparator; PROCEDURE isBlankChar(ch: CHAR): BOOLEAN; BEGIN IF (ch = SPACE) OR (ch = TAB) THEN RETURN TRUE; END; RETURN FALSE; END isBlankChar; PROCEDURE nextQuort(); BEGIN WHILE (CommandLine.text[c] # EOS) AND (CommandLine.text[c] # QChar) DO INC(c); END; END nextQuort; PROCEDURE nextSeparator(); BEGIN WHILE (CommandLine.text[c] # EOS) AND (NOT isSeparator(CommandLine.text[c])) DO INC(c); END; END nextSeparator; PROCEDURE getOneArg(): CARDINAL; VAR s: CARDINAL; BEGIN IF CommandLine.text[c] # EOS THEN IF isQuort(CommandLine.text[c]) THEN QChar := CommandLine.text[c]; INC(c); s := c; nextQuort(); ELSE s := c; nextSeparator(); END; CommaFlag := FALSE; IF CommandLine.text[c] # EOS THEN IF CommandLine.text[c] # COMMA THEN CommandLine.text[c] := EOS; INC(c); skipBlanks(); END; IF CommandLine.text[c] = COMMA THEN CommaFlag := TRUE; CommandLine.text[c] := EOS; INC(c); END; END; ELSE s := 0; END; RETURN s; END getOneArg; PROCEDURE isQuort(ch: CHAR): BOOLEAN; BEGIN IF (ch = SQUORT) OR (ch = DQUORT) THEN RETURN TRUE; END; RETURN FALSE; END isQuort; PROCEDURE isSeparator(ch: CHAR): BOOLEAN; BEGIN CASE ch OF SPACE: RETURN TRUE; | TAB: RETURN TRUE; | COMMA: RETURN TRUE; ELSE RETURN FALSE; END; END isSeparator; PROCEDURE getCmdLine(); VAR BDOSCmd: RECORD CASE BOOLEAN OF TRUE: Func: BdosFunctions; | FALSE: Cmd: WORD; END; END; junk: WORD; BEGIN BDOSCmd.Func := prtStr; Msg := 'arguments>$'; Bdos(BDOSCmd.Cmd,ADR(Msg),junk); CommandLine.maxLen := CHR(255); CommandLine.curLen := CHR(0); BDOSCmd.Func := rdCBuf; Bdos(BDOSCmd.Cmd,ADR(CommandLine),junk); CommandLine.text[ORD(CommandLine.curLen)] := EOS; BDOSCmd.Func := prtStr; Msg[0] := cr; Msg[1] := lf; Msg[2] := '$'; Bdos(BDOSCmd.Cmd,ADR(Msg),junk); FOR nArgc := 0 TO MAXARGS-1 DO ArgList[nArgc] := EMPTY; END; nArgc := 0; c := 0; CommaFlag := FALSE; skipBlanks(); WHILE (CommandLine.text[c] # EOS) AND (nArgc < MAXARGS) DO ArgList[nArgc] := getOneArg(); skipBlanks(); INC(nArgc); END; IF (CommandLine.text[c] = EOS) AND CommaFlag THEN ArgList[nArgc] := c; INC(nArgc); END; END getCmdLine; BEGIN getCmdLine(); END CmdArgs.
この修正にあわせて、"Find"モジュールの変更をしました。
最近のコメント