(**********************************************************************

    :Program.    ModuleInfos.mod
    :Contents.   Procedures reading m2amiga def, mod, sym and obj files
    :Author.     Nicolas Benezan [bne]
    :Address.    Postwiesenstr. 2, D7000 Stuttgart 60
    :Phone.      711/333679
    :Copyright.  Shareware, see Dok-File
    :Language.   Modula-2
    :Translator. M2Amiga AMSoft V3.2d
    :Imports.    MemSystem1.3, ErrorReq1.2, PathFinder1.3 [bne]
    :Imports.    Queue1.2 [mif]
    :History.    V1.0a [bne] 4.Feb.1989
    :History.    V1.1b [bne] 12.Feb.1989 (+ ReadMainInfo, project)
    :History.    V1.2d [bne] 22.Feb.1989 (bugs fixed, cosmetics)
    :Update.     4.Mar.1989 [bne] adapted MemSystem V1.3
    :History.    V1.3a [bne] 4.Apr.1989 (+ LastFileDate);
    :History.    V1.4 [bne] 18.May.1989 (+ ExDos)
    :History.    V1.5 [bne] 18.May.1989 (+ test if main module exists)

**********************************************************************)

IMPLEMENTATION MODULE ModuleInfos;

FROM SYSTEM     IMPORT ADR;
FROM Arts       IMPORT ModKeys, ModName, ModType;
FROM Dos        IMPORT Date;
FROM ExDos      IMPORT Examine, FileInfoBlock, FileLockPtr, UnLock;
FROM ErrorReq   IMPORT ABORT, Assert, IGNORE, Message, RecoverableExit;
FROM MemSystem  IMPORT Deallocate, NoCareAllocate;
FROM PathFinder IMPORT ExtendFileName, FindLocal, FindProject, GoToProject,
                       ModNamePtr, PathFlags, PathFlagSet, ProjectLock;
FROM Queue      IMPORT queue, Write;
FROM Str        IMPORT Concat;
IMPORT SrcScanner, SymScanner, ObjScanner;

CONST
  QueueFail       = "Queue import failed";
  ExamineFail     = "Dos.Examine() failed";
  ScanSrcFail     = "ScanSrcFile failed";
  ScanSymFail     = "ScanSymFile failed";
  ScanObjFail     = "ScanObjFile failed";
  MissingModule   = "Missing module ";
  MainNotFound    = "Main module not found";

PROCEDURE ResetLastFileDate;
  BEGIN
    WITH LastFileDate DO
      days:=0;
      minute:=0;
      tick:=0;
    END;
  END ResetLastFileDate;

PROCEDURE GetDate(file:FileLockPtr;VAR date:Date);
  VAR
    Info:FileInfoBlock;
    Ok:BOOLEAN;
  BEGIN
    Ok:=FALSE;
    Assert(Examine(file,Info),ADR(ExamineFail));
    date:=Info.date;
    IF EarlierDate(LastFileDate,date) THEN
      LastFileDate:=date;
    END;
  END GetDate;

PROCEDURE MatchKeys(key1,key2:ModKeys):BOOLEAN;
  BEGIN
    RETURN (key1[0]=key2[0]) AND
           (key1[1]=key2[1]) AND
           (key1[2]=key2[2]);
  END MatchKeys;

PROCEDURE EarlierDate(earlyDate,lateDate:Date):BOOLEAN;
  BEGIN
    IF earlyDate.days#lateDate.days THEN
      RETURN earlyDate.days<lateDate.days;
    ELSIF earlyDate.minute#lateDate.minute THEN
      RETURN earlyDate.minute<lateDate.minute;
    ELSE
      RETURN earlyDate.tick<lateDate.tick;
    END;
  END EarlierDate;

(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
(* read information out of a source (mod or def)                        *)
(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
PROCEDURE ScanSrcFile(Name:ModName;ExtensType:PathFlags;
                      ImportList:queue;VAR Key:ModKeys;VAR Type:ModType);
  VAR
    Info:ImportInfo;
  BEGIN
    ExtendFileName(Name,ExtensType);
    Assert(SrcScanner.StartScan(Name),ADR(ScanSrcFail));
    Info.key[0]:=0;
    Info.key[1]:=0;
    Info.key[2]:=0;
    WHILE SrcScanner.FindNextImport(Info.name) DO
      Assert(Write(ImportList,Info),ADR(QueueFail));
    END;
    SrcScanner.EndScan(Type);
    Key:=Info.key;
  END ScanSrcFile;

PROCEDURE ReadDefInfo(Project: ProjectLock;
                      Module: ModNamePtr;
                      ImportList: queue;
                  VAR Info: ModuleInfoPtr);
  VAR
    DefFile, SymFile: FileLockPtr;
    DefDate, SymDate: Date;
    Msg: Message;

(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
(* read information out of a symbol file (sym)                          *)
(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
  PROCEDURE ScanSymFile(VAR Key:ModKeys;VAR Type:ModType;Import:BOOLEAN);
    VAR
      Info:ImportInfo;
      ExtName:ModName;
    BEGIN
      ExtName:=Module^;
      ExtendFileName(ExtName,sym);
      Assert(SymScanner.StartScan(ExtName),ADR(ScanSymFail));
      IF Import THEN
        WHILE SymScanner.FindNextImport(Info.name,Info.key,Type) DO
          Assert(Write(ImportList,Info),ADR(QueueFail));
        END;
      END;
      SymScanner.EndScan(Type,Key);
    END ScanSymFile;

  BEGIN
    NoCareAllocate(Info,SIZE(ModuleInfo));
    WITH Info^ DO
      name:=Module^;
      project:=Project;
      DefFile:=FindProject((*Info^.*)project, name, def);
      IF DefFile#NIL THEN
        flags:=InfoFlagSet{defExist};
        SymFile:=FindLocal(project, name, sym);
        IF SymFile#NIL THEN          (* sym+def exist *)
          GetDate(DefFile,DefDate);
          GetDate(SymFile,SymDate);
          IF EarlierDate(DefDate,SymDate) THEN (* check version *)
            INCL(flags,symValid);
            ScanSymFile(key,type,TRUE);
          END;
          UnLock(SymFile);
        END;
        IF NOT(symValid IN flags) THEN
          GoToProject(project,def);
          ScanSrcFile(name, def, ImportList, key, type);
        END;
        UnLock(DefFile);
      ELSE
        project:=Project; (* start to search again *)
        SymFile:=FindProject(project, name, sym);
        IF SymFile#NIL THEN
          UnLock(SymFile);
          ScanSymFile(key,type,FALSE); (* sym without def must be a stdlib *)
          flags:=InfoFlagSet{symValid}; (* stdlib imports are not examined *)
        ELSE
          Msg:=MissingModule;
          Concat(Msg, name);
          RecoverableExit(ADR(Msg),ADR(IGNORE),ADR(ABORT));
          flags:=InfoFlagSet{};
        END;
      END;
      compiled:=FALSE;
    END; (* with Info^ *)
  END ReadDefInfo;

PROCEDURE ReadModInfo(    ImportList: queue;
                          Info: ModuleInfoPtr;
                      VAR ObjKey: ModKeys);
  VAR
    ModFile, ObjFile: FileLockPtr;
    ModDate, ObjDate: Date;

(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
(* read information out of an object file (obj)                         *)
(*нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн*)
  PROCEDURE ScanObjFile(VAR Key: ModKeys);
    VAR
      Import: ImportInfo;
      ExtName: ModName;
      Type: ModType;
    BEGIN
      ExtName:=Info^.name;
      ExtendFileName(ExtName, obj);
      Assert(ObjScanner.StartScan(ExtName), ADR(ScanObjFail));
      WHILE ObjScanner.FindNextImport(Import.name, Import.key, Type) DO
        Assert(Write(ImportList, Import), ADR(QueueFail));
      END;
      ObjScanner.EndScan(Key);
    END ScanObjFile;

  BEGIN
    WITH Info^ DO
      IF defExist IN flags THEN
        ModFile:=FindLocal(project, name, imp);
        IF ModFile#NIL THEN
          INCL(flags,modExist);
          ObjFile:=FindLocal(project, name, obj);
          IF ObjFile#NIL THEN
            GetDate(ModFile,ModDate);
            GetDate(ObjFile,ObjDate);
            IF EarlierDate(ModDate,ObjDate) THEN
              INCL(flags,objValid);
              ScanObjFile(ObjKey);
            END;
            UnLock(ObjFile);
          END;
          IF NOT(objValid IN flags) THEN
            GoToProject(project,imp);
            ScanSrcFile(name, imp, ImportList, ObjKey, type);
          END;
          UnLock(ModFile);
          RETURN
        END; (* if ModFile#NIL *)
      END; (* if defExist *)
      INCL(flags,objValid); (* last chance if mod missing *)
      ObjKey:=key
    END; (* with Info^ *)
  END ReadModInfo;

PROCEDURE ReadMainInfo(    Project: ProjectLock;
                           Module: ModNamePtr;
                       VAR Info: ModuleInfoPtr);
  VAR
    MainModule: FileLockPtr;
  BEGIN
    MainModule:=FindLocal(Project, Module^, imp);
    Assert(MainModule#NIL, ADR(MainNotFound));
    UnLock(MainModule);
    NoCareAllocate(Info,SIZE(ModuleInfo));
    WITH Info^ DO
      name:=Module^;
      project:=Project;
      (* noDef -> no key *)
      type:=mod;
      compiled:=FALSE;
      flags:=InfoFlagSet{defExist}; (* def does not exist, of course,
        but we must set the flag to avoid errors *)
    END;
  END ReadMainInfo;

END ModuleInfos.

