home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
modu1096.zip
/
sample
/
gpm.mod
< prev
next >
Wrap
Text File
|
1995-05-12
|
18KB
|
525 lines
(****************************************************************)
(* *)
(* Modula-2 Compiler Source Module *)
(* *)
(* Compiler Main Control Program. *)
(* *)
(* (c) copyright 1989 Faculty of Information Technology. *)
(* Queensland University of Technology *)
(* *)
(* Permission is granted to use, copy and change this *)
(* program as long as the copyright message is left intact *)
(* *)
(****************************************************************)
MODULE GPM;
(*
* This program starts off as a relatively small process, which
* forks and then execs the compiler proper "gpm2". The parent
* process waits for gpm2 to complete.
*
* gpm2 sends back a termination code which indicates the type
* of termination, and hence the further action to be taken.
*
* gpm2 exit codes have the following meaning:
* 0 ==> normal exit, ok to chain to cc
* 1 ==> normal exit, no further action required
* 2 ==> abnormal exit, gpm2 signalled errors
* 3 ==> interactive exit, chain to vi
*
* In the case of interactive operation, when vi exits gpm2
* is restarted with an explanatory message.
*
* The exit codes of this program are similar, and have
* the following meanings to the shell:
* 0 ==> normal exit, compilation succeeded
* 0 ==> normal exit, no object code produced
* 2 ==> abnormal, gpm2 signalled errors
* 4 ==> abortion, gpm2 aborted with core dump
* 5 ==> abnormal, gpm signalled bad arguments
*
* All messages from this program are directed to stderr.
*
* The temporary file is used by gpm2 to pass either:
* (1) the intermediate file name back to gpm, and thence to cc
* (2) an ex command back to gpm and thence to vi
* For example, if file xxxx.mod contains module AVeryLongName,
* the temporary file will contain the name of the intermediate
* file "averylon.c". The object filename will be "averylon.o".
* Note the truncation and case transformation. This behaviour
* can be overriden in the case of implementation modules so
* as to produce the output filenames "xxxx.c" and "xxxx.o"
*)
(* ------------------------------------------------------------------ *)
IMPORT StdStrings;
FROM IntStr IMPORT Give, left;
FROM SYSTEM IMPORT ADR, CAST, ADDRESS;
FROM Types IMPORT Int32;
FROM ProgArgs IMPORT
ArgNumber, GetArg, UNIXexit, Assert, EnvironString, VersionTime;
FROM StdError IMPORT
WriteString, WriteCard, Write, WriteLn;
FROM BuildArgs IMPORT
ArgPtr, ArgBlock, DisposeArgBlock, Arg3, Arg4,
NewArgBlock, AppendArg, ArgsOf;
FROM UxFiles IMPORT
Open, Close, Delete, File, OpenMode, ReadNBytes;
FROM Ascii IMPORT ht, lf;
FROM PcProcesses IMPORT Spawnv, PSP;
(* ------------------------------------------------------------------ *)
CONST edEnvStr = "GPMEDITOR";
FrontEnd = "gpmd";
gpm = "gpm";
GPM = "GPM";
CONST WS = WriteString;
CONST version = "OS/2";
TYPE MiddleString = ARRAY [0 .. 93] OF CHAR;
NameString = ARRAY [0 .. 15] OF CHAR;
VAR tmpNam : MiddleString; (* name of tmp file *)
optStr : MiddleString; (* option string *)
argStr : MiddleString; (* input file name *)
msg : MiddleString; (* intermediate name *)
edNam : MiddleString; (* name of editor *)
objNam : MiddleString; (* object file name *)
persistent : BOOLEAN; (* ==> name.c file is not deleted *)
dPersists : BOOLEAN; (* ==> -D switch *)
profile : BOOLEAN; (* ==> -p switch *)
debug : BOOLEAN; (* ==> -g switch *)
explain : BOOLEAN; (* ==> -X switch *)
optimise : BOOLEAN; (* ==> -O switch *)
gpmArg : ArgPtr; (* argument bundle for exec of gpm2 *)
dgenBlk: ArgBlock; (* argument block for exec for dgen *)
ccBlk : ArgBlock; (* argument block for exec for cc *)
edBlk : ArgBlock; (* argument block for exec of editor*)
tmpFil : File; (* the temporary, message file *)
ok : BOOLEAN;
argN : CARDINAL; (* number of arguments to gpm *)
argIx : CARDINAL; (* index into the arg list *)
optIx : CARDINAL; (* index into the option str *)
defaultBuffSize : CARDINAL;
result, retVal : Int32;
spitName : BOOLEAN; (* name is emitted to stdErr *)
dgenOFlg : CHAR; (* -O option for dgen *)
dgenNCnt : CARDINAL; (* -N option stuff for dgen *)
dgenNFlg : ARRAY [1 .. 20] OF CHAR;
PROCEDURE Copyright();
VAR str : ARRAY [0..127] OF CHAR;
BEGIN
WS(GPM + " (" + version + ") version of "); VersionTime(str); WS(str);
WS("Copyright 1995 Office of Commercial Services, " +
"Qld. University of Technology" + lf + lf +
"This compiler is a licensed commercial product. You are using a free,"+lf+
"unsupported copy that carries absolutely no warranty. You are welcome"+lf+
"to use it and distribute free copies of it for trial or educational use."+lf+
"Read SUPPORT.DOC for information about obtaining the commercial package"+lf+
"from a GPM distributor." + lf + lf);
END Copyright;
PROCEDURE DoUsageStr();
VAR str : ARRAY [0..127] OF CHAR;
BEGIN
Copyright();
WS("Usage: " + gpm + " [options] filename(s)" + lf +
"Options may be in any order, and in one or more groups" + lf +
"Wildcards in filenames are permitted. " + gpm +
" will warn if no files found" + lf);
WS(" -a turn off assertion checks -Bn allocate 'n' buffer entries"+lf);
WS(" -D D-Code output only -d dangerous: turn off warnings"+lf);
WS(" -f filename used as outname -g add debugging information"+lf);
WS(" -I interactive mode with editor -i turn off index checks"+lf);
WS(" -l listing name.lst is created -n no object code produced"+lf);
WS(" -N[cflpr] turn off dgen optimisation -O0 turn off all optimisations"+lf);
WS(" -O1 default optimisations (= -Oc) -O2 turn on all optimisations"+lf);
WS(" -Of optimise for speed -r turn off range checks"+lf);
WS(" -S assembler output only -s turn off stack checks"+lf);
WS(" -t turn off overflow checks -v verbose compile messages"+lf);
WS(" -V super-verbose compile messages -X verbose error explanations"+lf);
UNIXexit(1);
END DoUsageStr;
PROCEDURE Abort(str : ARRAY OF CHAR; cmd : ARRAY OF CHAR);
BEGIN
WriteString(gpm + ": ");
WriteString(str); WriteString(cmd); WriteLn;
UNIXexit(5);
END Abort;
PROCEDURE GetMessage(VAR str : ARRAY OF CHAR);
VAR read : CARDINAL;
BEGIN
Open(tmpFil,tmpNam,ReadOnly,ok);
IF ok THEN
ReadNBytes(tmpFil,ADR(str),93,read);
str[read] := "";
Close(tmpFil,ok);
ELSE Abort("Can't open ",tmpNam);
END;
END GetMessage;
PROCEDURE ChangeExt(VAR name : ARRAY OF CHAR; ext : ARRAY OF CHAR);
VAR ix,t : CARDINAL;
BEGIN
ix := LENGTH(name);
WHILE ((ix > 0) AND (name[ix] <> ".")) DO DEC(ix) END;
IF name[ix] <> "." THEN ix := LENGTH(name) END;
name[ix] := ".";
INC(ix);
FOR t := 0 TO HIGH(ext) DO
name[ix+t] := ext[t];
IF ext[t] = "" THEN RETURN END;
END;
END ChangeExt;
PROCEDURE GetEditorInfo(VAR nam : ARRAY OF CHAR;
VAR blk : ArgBlock);
VAR rIdx, wIdx, mIdx : CARDINAL;
wrkStr : MiddleString; (* working string *)
msgStr : MiddleString; (* mesg from gpm2 *)
envStr : ARRAY [0 .. 255] OF CHAR; (* environment str *)
PROCEDURE SkipSpace(VAR ix : CARDINAL);
BEGIN
WHILE (envStr[ix] = " ") OR (envStr[ix] = ht) DO INC(ix) END;
END SkipSpace;
BEGIN
EnvironString(edEnvStr,envStr);
GetMessage(msgStr);
IF envStr[0] = "" THEN (* default editor is vi *)
edNam := "vi";
AppendArg(blk,edNam);
AppendArg(blk,msgStr);
AppendArg(blk,argStr);
ELSE
Assert(msgStr[0] = "+");
(*
* must parse envStr and construct the calling args
*
* [space] edFilNam {arg | "%"}
*
* within an arg "#" == line no
* "%" stands for the file_name
*)
rIdx := 0;
wIdx := 0;
SkipSpace(rIdx);
WHILE envStr[rIdx] > " " DO
edNam[wIdx] := envStr[rIdx]; INC(rIdx); INC(wIdx);
END;
edNam[wIdx] := "";
AppendArg(edBlk,edNam);
SkipSpace(rIdx);
WHILE envStr[rIdx] <> "" DO (* split into args *)
(*
* args are of two types -- "%" and others
*)
IF envStr[rIdx] = "%" THEN (* ==> filNam *)
AppendArg(edBlk,argStr);
INC(rIdx); (* and go to next argument *)
ELSE (* others *)
wIdx := 0;
WHILE envStr[rIdx] > " " DO (* for every char in arg do... *)
IF envStr[rIdx] <> "#" THEN (* copy char *)
wrkStr[wIdx] := envStr[rIdx];
INC(wIdx);
ELSE (* copy line *)
mIdx := 1;
WHILE msgStr[mIdx] <> "" DO
wrkStr[wIdx] := msgStr[mIdx];
INC(wIdx); INC(mIdx);
END; (* cp *)
END; (* process one char *)
INC(rIdx); (* to next char *)
END; (* while *)
wrkStr[wIdx] := "";
AppendArg(edBlk,wrkStr);
SkipSpace(rIdx);
END; (* normal arg *)
END; (* for each arg *)
END; (* env is defined *)
END GetEditorInfo;
PROCEDURE FormTmpNam();
VAR index, pid : CARDINAL;
pidStr : ARRAY [0 .. 15] OF CHAR;
VAR prefix : MiddleString;
BEGIN
EnvironString("TEMP",prefix);
index := LENGTH(prefix);
IF (index > 0) AND (prefix[index-1] <> "\") THEN
prefix[index] := "\";
INC(index);
END;
prefix[index] := "g"; prefix[index+1] := "p";
prefix[index+2] := "m"; prefix[index+3] := "";
pid := CAST(CARDINAL,PSP());
tmpNam := prefix;
Give(pidStr,pid,1,left);
pidStr[5] := 0C;
StdStrings.Append(pidStr,tmpNam);
END FormTmpNam;
PROCEDURE Spawn(path : ARRAY OF CHAR; argv : ArgPtr) : CARDINAL;
VAR id : INTEGER;
result : Int32;
PROCEDURE WriteArgs(argv : ArgPtr);
TYPE C = ARRAY [0 .. 99] OF CHAR;
TYPE S = POINTER TO C;
TYPE P = POINTER TO ARRAY [0 .. 99] OF S;
VAR p : P; c : C; i, cx : [0 .. 99];
BEGIN
p := CAST(P,argv);
i := 0;
WriteString(gpm + ": ");
WHILE p^[i] <> NIL DO
cx := 0;
REPEAT c[cx] := p^[i]^[cx]; INC(cx) UNTIL c[cx-1] = "";
WriteString(c); Write(" "); INC(i);
END;
WriteLn;
END WriteArgs;
BEGIN
IF explain THEN WriteArgs(argv) END;
id := Spawnv(path,argv);
IF id = -1 THEN
Abort("Couldn't exec ",path);
END;
RETURN CAST(CARDINAL,id);
END Spawn;
PROCEDURE ScanOptStr(VAR oIx : CARDINAL);
(*
* scan a single option string for options
* which need to be passed to cc, and add
* the current arg string to the optStr
*)
VAR ix : CARDINAL; ch : CHAR;
PROCEDURE GetNext();
BEGIN
optStr[oIx] := ch;
INC(oIx); INC(ix);
ch := argStr[ix];
END GetNext;
BEGIN
(* assert: argStr[0] = "-" *)
ix := 1; ch := argStr[1];
WHILE ch <> "" DO
(*
* Note: No need to pick up -I switch
* Front ends will return 3 if they want us to load an editor
*)
IF ch = "S" THEN persistent := TRUE;
ELSIF ch = "D" THEN
dPersists := TRUE; persistent := TRUE; (* This is not build -D !! *)
DEC(oIx); ch := optStr[oIx]; (* only for gpm... *)
ELSIF ch = "p" THEN profile := TRUE; (* AppendArg(ccBlk,"-p"); *)
ELSIF ch = "g" THEN debug := TRUE; (* AppendArg(ccBlk,"-g"); *)
ELSIF ch = "X" THEN explain := TRUE;
ELSIF ch = "O" THEN (* Turns on optimisations *)
GetNext;
dgenOFlg := ch;
IF ch = "0" THEN
optimise := FALSE;
DEC(oIx,2); ch := optStr[oIx]; (* Throw away -O0 *)
ELSE
optimise := TRUE;
IF ch = "1" THEN
ch := "c"; (* -O1 = -Oc *)
ELSIF ch = "2" THEN
ch := "f"; (* -O2 = -Of *)
ELSIF ch = "f" THEN
dgenOFlg := "2";
ELSE
dgenOFlg := "1"; (* for dgen -Oc = -O1 *)
END;
END;
ELSIF ch = "N" THEN (* Turns off optimisations *)
DEC(oIx); ch := optStr[oIx]; (* just for dgen *)
INC(ix); INC(dgenNCnt);
dgenNFlg[dgenNCnt] := argStr[ix];
ELSIF (ch = "W") OR (ch = "Y") THEN (* for Build... ignore *)
DEC(oIx); ch := optStr[oIx];
ELSIF ch = "B" THEN (* default buffer size *)
DEC(oIx); ch := optStr[oIx];
defaultBuffSize := 0;
WHILE (argStr[ix+1] >= "0") AND (argStr[ix+1] <= "9") DO
INC(ix);
defaultBuffSize := defaultBuffSize * 10 + ORD(argStr[ix]) - ORD("0");
END;
END;
GetNext;
END;
optStr[oIx] := "";
END ScanOptStr;
VAR ix, buffSize : CARDINAL;
dgenOpt : ARRAY [0 .. 2] OF CHAR;
tmpStr, sizStr : ARRAY [0 .. 15] OF CHAR;
BEGIN
(*
* first some housekeeping chores
*)
FormTmpNam(); (* forms name "gpmNNNNN" *)
argN := ArgNumber();
persistent := FALSE;
dPersists := FALSE;
debug := FALSE;
profile := FALSE;
explain := FALSE;
optimise := FALSE;
dgenOFlg := "";
dgenNCnt := 0;
defaultBuffSize := 5000;
(*
* building the arg list for gpm2
* args are "gpm", [options,] tmpFileName, sourceFileName
* in this case the fixed length arg pointer facilities are used
*)
argIx := 1;
IF argN = 1 THEN DoUsageStr () END;
(*
* first fetch all options -- these apply to all compilations
*)
optIx := 1; optStr := "-";
GetArg(argIx,argStr);
WHILE argStr[0] = '-' DO
ScanOptStr(optIx);
INC(argIx);
IF argIx < argN THEN
GetArg(argIx,argStr);
ELSE DoUsageStr();
END;
END;
(*
* now the main loop, which is executed for
* every separate remaining command line arg
*)
spitName := (argIx + 1) < argN;
LOOP
(*
* at this stage argStr is presumed to be a filename
*)
IF spitName THEN
WriteString(argStr); WriteLn;
END;
IF optIx = 1 THEN (* no options to pass *)
gpmArg := Arg3(FrontEnd,tmpNam,argStr);
ELSE
gpmArg := Arg4(FrontEnd,optStr,tmpNam,argStr);
END;
(*
* now the interactive loop is executed
* for each remaining argument in list
* this loop is normally traversed once only
* but may be traversed repeatedly for
* compilations using the -I option
*)
LOOP (* start compilation of a single file *)
result := Spawn(FrontEnd,gpmArg);
retVal := result;
IF retVal <= 2 THEN EXIT;
ELSIF retVal = 3 THEN (* chain to editor *)
(*
* allocate an arg block for the editor
*)
NewArgBlock(edBlk,16);
GetEditorInfo(edNam,edBlk);
result := Spawn(edNam,ArgsOf(edBlk));
DisposeArgBlock(edBlk);
WriteString(lf + gpm + ": recompiling <");
WriteString(argStr); Write(">"); WriteLn;
ELSE EXIT;
END; (* select on return value *)
END; (* main loop *)
IF result = 0 THEN
GetMessage(msg); (* fetches the intermediate file name *)
END;
(*
* following actions depend on the returned value
* and the value of the persistent Booleans
*)
IF (result = 0) AND NOT dPersists THEN (* chain to dgen *)
(*
* building the arg list for dgen
* this uses a (variable length) ArgBlock
*)
buffSize := defaultBuffSize;
LOOP
NewArgBlock(dgenBlk,16);
AppendArg(dgenBlk,"dgen");
IF debug THEN AppendArg(dgenBlk,"-g") END;
IF explain THEN AppendArg(dgenBlk,"-X") END;
IF profile THEN AppendArg(dgenBlk,"-p") END;
IF dgenOFlg <> "" THEN
dgenOpt := "-O"; dgenOpt[2] := dgenOFlg;
AppendArg(dgenBlk,dgenOpt);
END;
FOR ix := 1 TO dgenNCnt DO
dgenOpt := "-N"; dgenOpt[2] := dgenNFlg[ix];
AppendArg(dgenBlk,dgenOpt);
END;
Give(sizStr,buffSize,1,left);
tmpStr := "-B";
StdStrings.Append(sizStr,tmpStr);
AppendArg(dgenBlk,tmpStr);
AppendArg(dgenBlk,msg);
result := Spawn("dgen",ArgsOf(dgenBlk));
DisposeArgBlock(dgenBlk);
IF result <> 0 THEN
IF result = 3 THEN (* Try again with double the buffer size *)
buffSize := buffSize * 2;
WriteString("Retrying with -B"); WriteCard(buffSize,0); WriteLn;
ELSE
WriteString("** dgen failed, result # ");
WriteCard(result,3); WriteLn;
retVal := 4;
EXIT;
END;
ELSE EXIT;
END;
END;
Delete(msg,ok); (* deletes the name.dcf file *)
objNam := msg; ChangeExt(objNam,"o");
ChangeExt(msg,"s"); (* changes name.dcf to name.s *)
END;
IF (result = 0) AND NOT persistent THEN (* chain to cc *)
(*
* building the arg list for _cc_
* this uses a (variable length) ArgBlock
*)
NewArgBlock(ccBlk,16); (* start arg block for cc *)
AppendArg(ccBlk,"as");
AppendArg(ccBlk,"-o");
AppendArg(ccBlk,objNam); (* Output .o filename *)
AppendArg(ccBlk,msg); (* Input .s filename *)
result := Spawn("as",ArgsOf(ccBlk));
DisposeArgBlock(ccBlk);
IF result <> 0 THEN retVal := 4 END; (* for gpmake *)
Delete(msg,ok); (* deletes the name.c/s file *)
END;
Delete(tmpNam,ok); (* deletes temporary file *)
INC(argIx);
IF argIx = argN THEN EXIT ELSE GetArg(argIx,argStr) END;
END; (* of per file loop *)
IF retVal = 1 THEN retVal := 0 END; (* to keep Unix "make" happy *)
UNIXexit(retVal); (* value for final file *)
END GPM.