home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / modu1096.zip / sample / gpm.mod < prev    next >
Text File  |  1995-05-12  |  18KB  |  525 lines

  1. (****************************************************************)
  2. (*                                                              *)
  3. (*              Modula-2 Compiler Source Module                 *)
  4. (*                                                              *)
  5. (*               Compiler Main Control Program.                 *)
  6. (*                                                              *)
  7. (*     (c) copyright 1989 Faculty of Information Technology.    *)
  8. (*              Queensland University of Technology             *)
  9. (*                                                              *)
  10. (*     Permission is granted to use, copy and change this       *)
  11. (*     program as long as the copyright message is left intact  *)
  12. (*                                                              *)
  13. (****************************************************************)
  14.  
  15. MODULE GPM;
  16.  
  17.  (*
  18.   * This program starts off as a relatively small process, which
  19.   * forks and then execs the compiler proper "gpm2". The parent
  20.   * process waits for gpm2 to complete.
  21.   *
  22.   * gpm2 sends back a termination code which indicates the type
  23.   * of termination, and hence the further action to be taken.
  24.   *
  25.   * gpm2 exit codes have the following meaning:
  26.   *    0 ==> normal exit, ok to chain to cc
  27.   *    1 ==> normal exit, no further action required
  28.   *    2 ==> abnormal exit, gpm2 signalled errors
  29.   *    3 ==> interactive exit, chain to vi
  30.   *
  31.   * In the case of interactive operation, when vi exits gpm2
  32.   * is restarted with an explanatory message.
  33.   *
  34.   * The exit codes of this program are similar, and have
  35.   * the following meanings to the shell:
  36.   *      0 ==> normal exit, compilation succeeded
  37.   *      0 ==> normal exit, no object code produced
  38.   *      2 ==> abnormal, gpm2 signalled errors
  39.   *      4 ==> abortion, gpm2 aborted with core dump
  40.   *      5 ==> abnormal, gpm signalled bad arguments
  41.   *
  42.   * All messages from this program are directed to stderr.
  43.   *
  44.   * The temporary file is used by gpm2 to pass either:
  45.   *   (1) the intermediate file name back to gpm, and thence to cc
  46.   *   (2) an ex command back to gpm and thence to vi 
  47.   * For example, if file xxxx.mod contains module AVeryLongName,
  48.   * the temporary file will contain the name of the intermediate
  49.   * file "averylon.c". The object filename will be "averylon.o".
  50.   * Note the truncation and case transformation. This behaviour
  51.   * can be overriden in the case of implementation modules so 
  52.   * as to produce the output filenames "xxxx.c" and "xxxx.o"
  53.   *)
  54. (* ------------------------------------------------------------------ *)
  55.  
  56.   IMPORT StdStrings;
  57.   FROM IntStr IMPORT Give, left;
  58.   FROM SYSTEM IMPORT ADR, CAST, ADDRESS;
  59.   FROM Types  IMPORT Int32;
  60.   FROM ProgArgs IMPORT 
  61.         ArgNumber, GetArg, UNIXexit, Assert, EnvironString, VersionTime;
  62.   FROM StdError IMPORT 
  63.         WriteString, WriteCard, Write, WriteLn;
  64.   FROM BuildArgs IMPORT 
  65.         ArgPtr, ArgBlock, DisposeArgBlock, Arg3, Arg4,
  66.     NewArgBlock, AppendArg, ArgsOf;
  67.   FROM UxFiles IMPORT
  68.     Open, Close, Delete, File, OpenMode, ReadNBytes;
  69.   FROM Ascii IMPORT ht, lf;
  70.   FROM PcProcesses IMPORT Spawnv, PSP;
  71.  
  72. (* ------------------------------------------------------------------ *)
  73.  
  74.   CONST edEnvStr = "GPMEDITOR";
  75.     FrontEnd = "gpmd";
  76.     gpm      = "gpm";
  77.     GPM      = "GPM";
  78.  
  79.   CONST WS = WriteString; 
  80.   CONST version = "OS/2";
  81.  
  82.   TYPE  MiddleString = ARRAY [0 .. 93] OF CHAR;
  83.         NameString   = ARRAY [0 .. 15] OF CHAR;
  84.  
  85.   VAR   tmpNam : MiddleString;            (* name of tmp file  *)
  86.         optStr : MiddleString;           (* option string     *)
  87.         argStr : MiddleString;          (* input file name   *)
  88.         msg    : MiddleString;          (* intermediate name *)
  89.         edNam  : MiddleString;          (* name of editor    *)
  90.     objNam : MiddleString;          (* object file name  *)
  91.      
  92.         persistent : BOOLEAN; (* ==> name.c file is not deleted  *)
  93.     dPersists  : BOOLEAN; (* ==> -D switch *)
  94.         profile    : BOOLEAN; (* ==> -p switch *)
  95.         debug      : BOOLEAN; (* ==> -g switch *)
  96.         explain    : BOOLEAN; (* ==> -X switch *)
  97.     optimise   : BOOLEAN; (* ==> -O switch *)
  98.  
  99.         gpmArg : ArgPtr;   (* argument bundle for exec of gpm2 *)
  100.         dgenBlk: ArgBlock; (* argument block for exec for dgen *)
  101.         ccBlk  : ArgBlock; (* argument block for exec for cc   *)
  102.         edBlk  : ArgBlock; (* argument block for exec of editor*)
  103.  
  104.         tmpFil : File;           (* the temporary, message file *)
  105.  
  106.         ok    : BOOLEAN;
  107.     argN  : CARDINAL;     (* number of arguments to gpm  *)
  108.         argIx : CARDINAL;      (* index into the arg list     *)
  109.         optIx : CARDINAL;     (* index into the option str   *)
  110.     defaultBuffSize : CARDINAL;
  111.         result, retVal  : Int32;
  112.     spitName : BOOLEAN;     (* name is emitted to stdErr   *)
  113.     dgenOFlg : CHAR;     (* -O option for dgen          *)
  114.     dgenNCnt : CARDINAL;     (* -N option stuff for dgen    *)
  115.     dgenNFlg : ARRAY [1 .. 20] OF CHAR;
  116.  
  117.   PROCEDURE Copyright();
  118.     VAR str : ARRAY [0..127] OF CHAR;
  119.   BEGIN
  120.     WS(GPM + " (" + version + ") version of "); VersionTime(str); WS(str);
  121.     WS("Copyright 1995 Office of Commercial Services, " + 
  122.        "Qld. University of Technology" + lf + lf +
  123.     "This compiler is a licensed commercial product.  You are using a free,"+lf+
  124.     "unsupported copy that carries absolutely no warranty.  You are welcome"+lf+
  125.     "to use it and distribute free copies of it for trial or educational use."+lf+
  126.     "Read SUPPORT.DOC for information about obtaining the commercial package"+lf+
  127.     "from a GPM distributor." + lf + lf);
  128.   END Copyright;
  129.  
  130.   PROCEDURE DoUsageStr();
  131.     VAR str : ARRAY [0..127] OF CHAR;
  132.   BEGIN
  133.     Copyright();
  134.     WS("Usage: " + gpm + " [options] filename(s)" + lf +
  135.     "Options may be in any order, and in one or more groups" + lf +
  136.     "Wildcards in filenames are permitted. " + gpm +
  137.             " will warn if no files found" + lf);
  138.     WS(" -a  turn off assertion checks        -Bn allocate 'n' buffer entries"+lf);
  139.     WS(" -D  D-Code output only               -d  dangerous: turn off warnings"+lf);
  140.     WS(" -f  filename used as outname         -g  add debugging information"+lf);
  141.     WS(" -I  interactive mode with editor     -i  turn off index checks"+lf);
  142.     WS(" -l  listing name.lst is created      -n  no object code produced"+lf);
  143.     WS(" -N[cflpr] turn off dgen optimisation -O0 turn off all optimisations"+lf);
  144.     WS(" -O1 default optimisations (= -Oc)    -O2 turn on all optimisations"+lf);
  145.     WS(" -Of optimise for speed               -r  turn off range checks"+lf);
  146.     WS(" -S  assembler output only            -s  turn off stack checks"+lf);
  147.     WS(" -t  turn off overflow checks         -v  verbose compile messages"+lf);
  148.     WS(" -V  super-verbose compile messages   -X  verbose error explanations"+lf);
  149.     UNIXexit(1);
  150.   END DoUsageStr;
  151.  
  152.   PROCEDURE Abort(str : ARRAY OF CHAR; cmd : ARRAY OF CHAR);
  153.   BEGIN
  154.     WriteString(gpm + ": ");
  155.     WriteString(str); WriteString(cmd); WriteLn;
  156.     UNIXexit(5);
  157.   END Abort;
  158.  
  159.   PROCEDURE GetMessage(VAR str : ARRAY OF CHAR);
  160.     VAR read : CARDINAL;
  161.   BEGIN
  162.     Open(tmpFil,tmpNam,ReadOnly,ok);
  163.     IF ok THEN
  164.       ReadNBytes(tmpFil,ADR(str),93,read);
  165.       str[read] := "";
  166.       Close(tmpFil,ok);
  167.     ELSE Abort("Can't open ",tmpNam);
  168.     END;
  169.   END GetMessage;
  170.  
  171.   PROCEDURE ChangeExt(VAR name : ARRAY OF CHAR; ext : ARRAY OF CHAR);
  172.     VAR ix,t : CARDINAL;
  173.   BEGIN
  174.     ix := LENGTH(name);
  175.     WHILE ((ix > 0) AND (name[ix] <> ".")) DO DEC(ix) END;
  176.     IF name[ix] <> "." THEN ix := LENGTH(name) END;
  177.     name[ix] := ".";
  178.     INC(ix);
  179.     FOR t := 0 TO HIGH(ext) DO
  180.       name[ix+t] := ext[t];
  181.       IF ext[t] = "" THEN RETURN END;
  182.     END;
  183.   END ChangeExt;
  184.  
  185.   PROCEDURE GetEditorInfo(VAR nam : ARRAY OF CHAR;
  186.               VAR blk : ArgBlock);
  187.     VAR rIdx, wIdx, mIdx : CARDINAL;
  188.     wrkStr : MiddleString;            (* working string  *)
  189.     msgStr : MiddleString;            (* mesg from gpm2  *)
  190.         envStr : ARRAY [0 .. 255] OF CHAR;    (* environment str *) 
  191.  
  192.     PROCEDURE SkipSpace(VAR ix : CARDINAL);
  193.     BEGIN
  194.       WHILE (envStr[ix] = " ") OR (envStr[ix] = ht) DO INC(ix) END;
  195.     END SkipSpace;
  196.  
  197.   BEGIN
  198.     EnvironString(edEnvStr,envStr);
  199.     GetMessage(msgStr);
  200.     IF envStr[0] = "" THEN (* default editor is vi *)
  201.       edNam := "vi";
  202.       AppendArg(blk,edNam);
  203.       AppendArg(blk,msgStr);
  204.       AppendArg(blk,argStr);
  205.     ELSE 
  206.       Assert(msgStr[0] = "+");
  207.       (*
  208.        *  must parse envStr and construct the calling args
  209.        *
  210.        *    [space] edFilNam {arg | "%"}
  211.        *
  212.        *  within an arg "#" == line no
  213.        *  "%" stands for the file_name
  214.        *)
  215.       rIdx := 0;
  216.       wIdx := 0;
  217.       SkipSpace(rIdx);
  218.       WHILE envStr[rIdx] > " " DO
  219.     edNam[wIdx] := envStr[rIdx]; INC(rIdx); INC(wIdx);
  220.       END;
  221.       edNam[wIdx] := "";
  222.       AppendArg(edBlk,edNam);
  223.       SkipSpace(rIdx);
  224.       WHILE envStr[rIdx] <> "" DO (* split into args *)
  225.        (*
  226.     *   args are of two types -- "%" and others
  227.     *)
  228.     IF envStr[rIdx] = "%" THEN (* ==> filNam *)
  229.       AppendArg(edBlk,argStr); 
  230.       INC(rIdx); (* and go to next argument *)
  231.     ELSE (* others *)
  232.           wIdx := 0;
  233.           WHILE envStr[rIdx] > " " DO  (* for every char in arg do... *)
  234.           IF envStr[rIdx] <> "#" THEN (* copy char *)
  235.           wrkStr[wIdx] := envStr[rIdx]; 
  236.           INC(wIdx);
  237.         ELSE (* copy line *)
  238.           mIdx := 1;
  239.           WHILE msgStr[mIdx] <> "" DO
  240.             wrkStr[wIdx] := msgStr[mIdx]; 
  241.         INC(wIdx); INC(mIdx);
  242.           END; (* cp *)
  243.         END; (* process one char *)
  244.         INC(rIdx); (* to next char *)
  245.           END; (* while *)
  246.           wrkStr[wIdx] := "";
  247.       AppendArg(edBlk,wrkStr);
  248.           SkipSpace(rIdx);
  249.         END; (* normal arg *)
  250.       END; (* for each arg *)
  251.     END; (* env is defined *)
  252.   END GetEditorInfo;
  253.  
  254.   PROCEDURE FormTmpNam();
  255.     VAR   index, pid : CARDINAL;
  256.        pidStr    : ARRAY [0 .. 15] OF CHAR;
  257.     VAR   prefix : MiddleString;
  258.   BEGIN
  259.     EnvironString("TEMP",prefix);
  260.     index := LENGTH(prefix);
  261.     IF (index > 0) AND (prefix[index-1] <> "\") THEN
  262.       prefix[index] := "\";
  263.       INC(index);
  264.     END;
  265.     prefix[index]   := "g"; prefix[index+1] := "p";
  266.     prefix[index+2] := "m"; prefix[index+3] := "";
  267.     pid := CAST(CARDINAL,PSP());
  268.     tmpNam := prefix;
  269.     Give(pidStr,pid,1,left);
  270.     pidStr[5] := 0C;
  271.     StdStrings.Append(pidStr,tmpNam);
  272.   END FormTmpNam;
  273.  
  274.   PROCEDURE Spawn(path : ARRAY OF CHAR; argv : ArgPtr) : CARDINAL;
  275.     VAR id     : INTEGER;
  276.         result : Int32;
  277.  
  278.     PROCEDURE WriteArgs(argv : ArgPtr);
  279.       TYPE C = ARRAY [0 .. 99] OF CHAR;
  280.       TYPE S = POINTER TO C;
  281.       TYPE P = POINTER TO ARRAY [0 .. 99] OF S;
  282.       VAR p : P; c : C; i, cx : [0 .. 99];
  283.     BEGIN
  284.       p := CAST(P,argv);
  285.       i := 0;
  286.       WriteString(gpm + ": ");
  287.       WHILE p^[i] <> NIL DO
  288.     cx := 0;
  289.     REPEAT c[cx] := p^[i]^[cx]; INC(cx) UNTIL c[cx-1] = "";
  290.         WriteString(c); Write(" "); INC(i);
  291.       END;
  292.       WriteLn;
  293.     END WriteArgs;
  294.  
  295.   BEGIN
  296.    IF explain THEN WriteArgs(argv) END;
  297.     id := Spawnv(path,argv);
  298.     IF id = -1 THEN
  299.       Abort("Couldn't exec ",path);
  300.     END;
  301.     RETURN CAST(CARDINAL,id);
  302.   END Spawn;
  303.  
  304.   PROCEDURE ScanOptStr(VAR oIx : CARDINAL);
  305.     (* 
  306.      *  scan a single option string for options
  307.      *  which need to be passed to cc, and add
  308.      *  the current arg string to the optStr
  309.      *)
  310.     VAR ix : CARDINAL; ch : CHAR;
  311.  
  312.     PROCEDURE GetNext();
  313.     BEGIN
  314.       optStr[oIx] := ch;
  315.       INC(oIx); INC(ix);
  316.       ch := argStr[ix];
  317.     END GetNext;
  318.  
  319.   BEGIN
  320.     (* assert: argStr[0] = "-" *)
  321.     ix := 1; ch := argStr[1];
  322.     WHILE ch <> "" DO
  323.     (*
  324.      * Note: No need to pick up -I switch
  325.      *       Front ends will return 3 if they want us to load an editor
  326.      *)
  327.       IF    ch = "S" THEN persistent  := TRUE;
  328.       ELSIF ch = "D" THEN
  329.         dPersists := TRUE; persistent := TRUE;    (* This is not build -D !! *)
  330.     DEC(oIx); ch := optStr[oIx];        (* only for gpm...       *)
  331.       ELSIF ch = "p" THEN profile     := TRUE;    (* AppendArg(ccBlk,"-p"); *)
  332.       ELSIF ch = "g" THEN debug       := TRUE;    (* AppendArg(ccBlk,"-g"); *)
  333.       ELSIF ch = "X" THEN explain     := TRUE;
  334.       ELSIF ch = "O" THEN            (* Turns on optimisations *)
  335.     GetNext;
  336.     dgenOFlg := ch;
  337.     IF ch = "0" THEN
  338.       optimise := FALSE;
  339.       DEC(oIx,2); ch := optStr[oIx];    (* Throw away -O0 *)
  340.     ELSE
  341.       optimise := TRUE;
  342.       IF    ch = "1" THEN
  343.         ch := "c";                (* -O1 = -Oc *)
  344.       ELSIF ch = "2" THEN
  345.         ch := "f";                (* -O2 = -Of *)
  346.       ELSIF ch = "f" THEN
  347.         dgenOFlg := "2";
  348.       ELSE
  349.         dgenOFlg := "1";            (* for dgen -Oc = -O1 *)
  350.       END;
  351.     END;
  352.       ELSIF ch = "N" THEN            (* Turns off optimisations *)
  353.         DEC(oIx); ch := optStr[oIx];        (* just for dgen *)
  354.     INC(ix); INC(dgenNCnt);
  355.     dgenNFlg[dgenNCnt] := argStr[ix];
  356.       ELSIF (ch = "W") OR (ch = "Y") THEN    (* for Build... ignore     *)
  357.     DEC(oIx); ch := optStr[oIx];
  358.       ELSIF ch = "B" THEN            (* default buffer size *)
  359.     DEC(oIx); ch := optStr[oIx];
  360.     defaultBuffSize := 0;
  361.     WHILE (argStr[ix+1] >= "0") AND (argStr[ix+1] <= "9") DO
  362.       INC(ix);
  363.       defaultBuffSize := defaultBuffSize * 10 + ORD(argStr[ix]) - ORD("0");
  364.         END;
  365.       END;
  366.       GetNext;
  367.     END;
  368.     optStr[oIx] := "";
  369.   END ScanOptStr;
  370.  
  371.   VAR    ix, buffSize   : CARDINAL;
  372.     dgenOpt        : ARRAY [0 ..  2] OF CHAR;
  373.     tmpStr, sizStr : ARRAY [0 .. 15] OF CHAR;
  374. BEGIN
  375.   (*
  376.    *  first some housekeeping chores
  377.    *)
  378.   FormTmpNam();       (* forms name "gpmNNNNN"  *)
  379.   argN := ArgNumber();
  380.   persistent  := FALSE;
  381.   dPersists   := FALSE;
  382.   debug       := FALSE;
  383.   profile     := FALSE;
  384.   explain     := FALSE;
  385.   optimise    := FALSE;
  386.   dgenOFlg    := "";
  387.   dgenNCnt    := 0;
  388.   defaultBuffSize := 5000;
  389.   (*
  390.    * building the arg list for gpm2
  391.    * args are "gpm", [options,] tmpFileName, sourceFileName
  392.    * in this case the fixed length arg pointer facilities are used
  393.    *)
  394.   argIx := 1;
  395.   IF argN = 1 THEN DoUsageStr () END;
  396.   (*
  397.    *  first fetch all options -- these apply to all compilations
  398.    *)
  399.   optIx := 1; optStr := "-";
  400.   GetArg(argIx,argStr);
  401.   WHILE argStr[0] = '-' DO
  402.     ScanOptStr(optIx);
  403.     INC(argIx);
  404.     IF argIx < argN THEN
  405.       GetArg(argIx,argStr);
  406.     ELSE DoUsageStr(); 
  407.     END;
  408.   END;
  409.   (*
  410.    *  now the main loop, which is executed for
  411.    *  every separate remaining command line arg
  412.    *)
  413.   spitName := (argIx + 1) < argN;
  414.   LOOP
  415.     (*
  416.      *  at this stage argStr is presumed to be a filename
  417.      *)
  418.     IF spitName THEN 
  419.       WriteString(argStr); WriteLn;
  420.     END;
  421.     IF optIx = 1 THEN (* no options to pass *)
  422.       gpmArg := Arg3(FrontEnd,tmpNam,argStr);
  423.     ELSE 
  424.       gpmArg := Arg4(FrontEnd,optStr,tmpNam,argStr);
  425.     END;
  426.     (*
  427.      *  now the interactive loop is executed
  428.      *  for each remaining argument in list
  429.      *  this loop is normally traversed once only
  430.      *  but may be traversed repeatedly for 
  431.      *  compilations using the -I option
  432.      *)
  433.     LOOP  (* start compilation of a single file *)
  434.       result := Spawn(FrontEnd,gpmArg);
  435.       retVal := result;
  436.       IF retVal <= 2 THEN EXIT;
  437.       ELSIF retVal = 3 THEN (* chain to editor *)
  438.        (*
  439.         *  allocate an arg block for the editor
  440.         *)
  441.         NewArgBlock(edBlk,16);
  442.         GetEditorInfo(edNam,edBlk);
  443.     result := Spawn(edNam,ArgsOf(edBlk));
  444.     DisposeArgBlock(edBlk);
  445.         WriteString(lf + gpm + ": recompiling <");
  446.         WriteString(argStr); Write(">"); WriteLn;
  447.       ELSE EXIT;
  448.       END; (* select on return value *)
  449.     END; (* main loop *)
  450.  
  451.     IF result = 0 THEN
  452.       GetMessage(msg);    (* fetches the intermediate file name *)
  453.     END;
  454.    (* 
  455.     *  following actions depend on the returned value
  456.     *  and the value of the persistent Booleans
  457.     *)
  458.     IF (result = 0) AND NOT dPersists THEN (* chain to dgen *)
  459.      (*
  460.       *  building the arg list for dgen
  461.       *  this uses a (variable length) ArgBlock
  462.       *)
  463.       buffSize := defaultBuffSize;
  464.       LOOP
  465.         NewArgBlock(dgenBlk,16);
  466.         AppendArg(dgenBlk,"dgen");
  467.         IF debug   THEN AppendArg(dgenBlk,"-g") END;
  468.         IF explain THEN AppendArg(dgenBlk,"-X") END;
  469.         IF profile THEN AppendArg(dgenBlk,"-p") END;
  470.         IF dgenOFlg <> "" THEN 
  471.           dgenOpt := "-O"; dgenOpt[2] := dgenOFlg;
  472.           AppendArg(dgenBlk,dgenOpt);
  473.         END;
  474.         FOR ix := 1 TO dgenNCnt DO
  475.       dgenOpt := "-N"; dgenOpt[2] := dgenNFlg[ix];
  476.           AppendArg(dgenBlk,dgenOpt);
  477.         END;
  478.         Give(sizStr,buffSize,1,left);
  479.     tmpStr := "-B";
  480.         StdStrings.Append(sizStr,tmpStr);
  481.         AppendArg(dgenBlk,tmpStr);
  482.         AppendArg(dgenBlk,msg);
  483.         result := Spawn("dgen",ArgsOf(dgenBlk));
  484.         DisposeArgBlock(dgenBlk);
  485.         IF result <> 0 THEN
  486.       IF result = 3 THEN    (* Try again with double the buffer size *)
  487.         buffSize := buffSize * 2;
  488.             WriteString("Retrying with -B"); WriteCard(buffSize,0); WriteLn;
  489.       ELSE
  490.             WriteString("** dgen failed, result # ");
  491.             WriteCard(result,3); WriteLn;
  492.             retVal := 4;
  493.         EXIT;
  494.       END;
  495.     ELSE EXIT;
  496.     END;
  497.       END;
  498.       Delete(msg,ok);        (* deletes the name.dcf file  *)
  499.       objNam := msg; ChangeExt(objNam,"o");
  500.       ChangeExt(msg,"s");    (* changes name.dcf to name.s *)
  501.     END;
  502.  
  503.     IF (result = 0) AND NOT persistent THEN (* chain to cc *)
  504.      (*
  505.       *  building the arg list for _cc_
  506.       *  this uses a (variable length) ArgBlock
  507.       *)
  508.       NewArgBlock(ccBlk,16);  (* start arg block for cc *)
  509.       AppendArg(ccBlk,"as");
  510.       AppendArg(ccBlk,"-o");
  511.       AppendArg(ccBlk,objNam); (* Output .o filename *)
  512.       AppendArg(ccBlk,msg);    (* Input .s filename  *)
  513.       result := Spawn("as",ArgsOf(ccBlk));
  514.       DisposeArgBlock(ccBlk);
  515.       IF result <> 0 THEN retVal := 4 END;   (* for gpmake *)
  516.       Delete(msg,ok);          (* deletes the name.c/s file *)
  517.     END;
  518.     Delete(tmpNam,ok);        (* deletes temporary file    *)
  519.     INC(argIx);
  520.     IF argIx = argN THEN EXIT ELSE GetArg(argIx,argStr) END;
  521.   END; (* of per file loop *)
  522.   IF retVal = 1 THEN retVal := 0 END; (* to keep Unix "make" happy *)
  523.   UNIXexit(retVal);                   (* value for final file *)
  524. END GPM.
  525.