-- file ModuleMaker.mesa
-- last edited by Satterthwaite, July 2, 1982 3:57 pm

DIRECTORY
  Environment: TYPE USING [bytesPerWord],
  File: TYPE USING [Capability],
  FileStream: TYPE USING [FileByteIndex, Create, GetLength, SetIndex],
  OSMiscOps: TYPE USING [FindFile],
  PGSConDefs: TYPE USING [
    objectVersion, sourceVersion, FixupBcdHeader, WriteBcdHeader],
  Spaces: TYPE USING [FreeWords, Words],
  Stream: TYPE USING [Handle, Delete, GetBlock, GetWord, PutBlock],
  Strings: TYPE USING [
    String, SubStringDescriptor, AppendChar, AppendString, EquivalentSubStrings],
  TableCommand: TYPE USING [CreateTime, GenerateVersion];

ModuleMaker: PROGRAM
    IMPORTS
      FileStream, OSMiscOps, PGSConDefs, Spaces, Stream, Strings, TableCommand
    EXPORTS TableCommand = {

  StreamIndex: TYPE = FileStream.FileByteIndex;
  bytesPerWord: CARDINAL = Environment.bytesPerWord;

 -- interface to PGSBcd
 
  CreateBCDStream: PUBLIC PROC [
	in: Stream.Handle,
	modId, interfaceId: Strings.String,
	altoCode: BOOL ← FALSE]
      RETURNS [output: Stream.Handle] = {
    modRoot: STRING ← [40];
    interfaceRoot: STRING ← [40];
    self: Strings.SubStringDescriptor ← ["SELF"L, 0, ("SELF"L).length];
    interface: Strings.SubStringDescriptor;
    t: STRING = [40];
    SetSourceVersion[in];
    PGSConDefs.objectVersion ← TableCommand.GenerateVersion[];
    FOR i: CARDINAL IN [0..modId.length) DO
      IF modId[i] = '. THEN EXIT;
      Strings.AppendChar[modRoot, modId[i]];
      ENDLOOP;
    Strings.AppendString[t, modId];
    output ← FileStream.Create[OSMiscOps.FindFile[t, write]];
    -- fill in interface info
    FOR i: CARDINAL IN [0..interfaceId.length) DO
      IF interfaceId[i] = '. THEN EXIT;
      Strings.AppendChar[interfaceRoot, interfaceId[i]];
      ENDLOOP;
    interface ← [interfaceRoot, 0, interfaceRoot.length];
    PGSConDefs.WriteBcdHeader[
      outStream: output,
      tableId: modRoot, binaryId: modId,
      interfaceId:
        IF Strings.EquivalentSubStrings[@interface, @self] THEN NIL ELSE interfaceRoot,
      fileId: NIL,	-- for now
      altoCode: altoCode];
    RETURN};

  FinishBcdStream: PUBLIC PROC = {
    PGSConDefs.FixupBcdHeader[]};
    
  -- input management

  CheckForBr: PROC [name: Strings.String] RETURNS [BOOL] = {
    FOR i: CARDINAL IN [0..name.length) DO
      IF name[i] = '. THEN GOTO extension;
      REPEAT
	extension =>
	  IF name.length = i+3 AND
            (name[i+1] = 'b OR name[i+1] = 'B) AND
            (name[i+2] = 'r OR name[i+2] = 'R) THEN RETURN[TRUE];
      ENDLOOP;
    RETURN[FALSE]};

  FindSegmentSize: PROC [br: BOOL, file: File.Capability]
      RETURNS [count: CARDINAL, in: Stream.Handle] = {
    in ← FileStream.Create[file];
    IF br THEN {
      FileStream.SetIndex[in, 7*Environment.bytesPerWord];
      count ← in.GetWord[];
      FileStream.SetIndex[in, count*Environment.bytesPerWord];
      count ← in.GetWord[]}
    ELSE {
      eof: StreamIndex = FileStream.GetLength[in];
      count ← eof/Environment.bytesPerWord;
      FileStream.SetIndex[in, 0]};
    RETURN};

  CreateSegmentStream: PROC [segmentId: Strings.String]
      RETURNS [in: Stream.Handle, count: CARDINAL] = {
    brFile: BOOL = CheckForBr[segmentId];
    t: STRING = [40];
    file: File.Capability;
    Strings.AppendString[t, segmentId];
    file ← OSMiscOps.FindFile[t, read];
    [count, in] ← FindSegmentSize[brFile, file];
    RETURN};

  SetSourceVersion: PROC [sh: Stream.Handle] = {
    PGSConDefs.sourceVersion ← [0, 0, TableCommand.CreateTime[sh]]};

 -- overall control

  WriteSegment: PROC [in, out: Stream.Handle, count: CARDINAL] = {
    p: LONG POINTER ← Spaces.Words[count];
    [] ← in.GetBlock[[p, 0, count*Environment.bytesPerWord]];
    out.PutBlock[[p, 0, count*Environment.bytesPerWord]];
    Spaces.FreeWords[p]};

  MakeModule: PUBLIC PROC [
      inputFile, moduleId, interfaceId: Strings.String, altocode: BOOL] = {
    in, output: Stream.Handle;
    count: CARDINAL;
    [in, count] ← CreateSegmentStream[inputFile];
    output ← CreateBCDStream[in, moduleId, interfaceId, altocode];
    WriteSegment[in, output, count];
    FinishBcdStream[];
    Stream.Delete[in];
    Stream.Delete[output]};

  }.