stable/src/StableLog.m3


 Copyright (C) 1994, Digital Equipment Corporation           
 All rights reserved.                                        
 See the file COPYRIGHT for a full description.              
                                                             
 Created by Carsten Weich                                    
                                                             
 Last modified on Thu Jan 19 13:13:18 PST 1995 by kalsow     
      modified on Tue Sep 27 19:18:12 PDT 1994 by weich      

UNSAFE MODULE StableLog;

IMPORT Rd, Wr, WrClass, RdClass, Text, Text8,
       Thread, Pickle,
       StableError, RdUtils, Word;

REVEAL
  WrClass.Private <: MUTEX;
  RdClass.Private <: MUTEX;

<*FATAL Thread.Alerted*>

CONST
  CallMark     = 27353;  (* this is Gregs birthday... *)
  EndCallMark  = 30965;  (* ...Carstens birthday...*)

  Mtext        = ORD('t');
  Mpickle      = ORD('P');

PROCEDURE OutCall (log: Wr.T; procId: CARDINAL) =
  BEGIN
    OutInteger(log, CallMark);
    OutInteger(log, procId)
  END OutCall;

PROCEDURE OutCallEndMark (log: Wr.T) =
  BEGIN
    OutInteger(log, EndCallMark)
  END OutCallEndMark;

PROCEDURE InCall (log: Rd.T; max: CARDINAL): CARDINAL RAISES {Error} =
  BEGIN
    IF InInteger(log) # CallMark THEN RAISE Error END;
    VAR res:= InInteger(log); BEGIN
      IF res > max OR res < 0 THEN RAISE Error END;
      RETURN res
    END
  END InCall;

PROCEDURE CheckCallEndMark (log: Rd.T): BOOLEAN =
  BEGIN
    TRY
      RETURN EndCallMark = InInteger(log)
    EXCEPT
      Error => RETURN FALSE
    END
  END CheckCallEndMark;

PROCEDURE OutRef(log: Wr.T; r: REFANY) =
  BEGIN
    TYPECASE r OF
    | TEXT(x) => OutInteger(log, Mtext); OutText(log, x);
    ELSE
      TRY
        OutInteger(log, Mpickle);
        Pickle.Write(log, r)
      EXCEPT
        Wr.Failure (err) =>
        StableError.Halt(
            "Cannot write to logfile: " & RdUtils.FailureText(err))
      | Pickle.Error (msg) =>
        StableError.Halt("Cannot write to logfile: Pickle error: " & msg)
      END
    END
  END OutRef;

PROCEDURE InRef(log: Rd.T): REFANY RAISES {Error} =
  VAR r: REFANY;
      code:= InInteger(log);
  BEGIN
    IF code = Mpickle THEN
      TRY
        r:= Pickle.Read(log)
      EXCEPT
      | Pickle.Error, Rd.EndOfFile => RAISE Error
      | Rd.Failure (err) =>
        StableError.Halt(
            "InRef: Can not read log file: " & RdUtils.FailureText(err))
      END
    ELSIF code = Mtext THEN
      r:= InText(log)
    ELSE
      RAISE Error
    END;
    RETURN r
  END InRef;
Procedures for generic logging of procedure parameters

PROCEDURE OutChar (log: Wr.T; c: CHAR) =
  BEGIN
    TRY
      Wr.PutChar(log, c)
    EXCEPT
      Wr.Failure (err) =>
        StableError.Halt(
          "Cannot write to logfile: " & RdUtils.FailureText(err))
    END
  END OutChar;

PROCEDURE OutChars (log: Wr.T; READONLY chars: ARRAY OF CHAR) =
  VAR n:= NUMBER(chars) - NUMBER(chars) MOD BYTESIZE(Word.T);
  BEGIN
    TRY
      Wr.PutString(log, SUBARRAY(chars, 0, n));
      FOR i:= n TO LAST(chars) DO
        Wr.PutChar(log, chars[i])
      END
    EXCEPT
      Wr.Failure (err) =>
        StableError.Halt(
          "Cannot write to logfile: " & RdUtils.FailureText(err))
    END
  END OutChars;

PROCEDURE OutInteger (log: Wr.T; i: INTEGER) =
  BEGIN
    TRY
      Wr.PutString(log, LOOPHOLE(i, ARRAY [0..BYTESIZE(INTEGER)-1] OF CHAR));
    EXCEPT
      Wr.Failure (err) =>
        StableError.Halt(
          "Cannot write to logfile: " & RdUtils.FailureText(err))
    END
  END OutInteger;

PROCEDURE OutLongint (log: Wr.T; i: LONGINT) =
  BEGIN
    TRY
      Wr.PutString(log, LOOPHOLE(i, ARRAY [0..BYTESIZE(LONGINT)-1] OF CHAR));
    EXCEPT
      Wr.Failure (err) =>
        StableError.Halt(
          "Cannot write to logfile: " & RdUtils.FailureText(err))
    END
  END OutLongint;

PROCEDURE OutCardinal (log: Wr.T; card: CARDINAL) =
  BEGIN
    OutInteger(log, card)
  END OutCardinal;

PROCEDURE OutLongcard (log: Wr.T; card: LONGCARD) =
  BEGIN
    OutLongint(log, card)
  END OutLongcard;

PROCEDURE OutBoolean (log: Wr.T; bool: BOOLEAN) =
  BEGIN
    TRY
      Wr.PutChar(log, VAL(ORD(bool), CHAR))
    EXCEPT
      Wr.Failure (err) =>
        StableError.Halt(
          "Cannot write to logfile: " & RdUtils.FailureText(err))
    END
  END OutBoolean;

PROCEDURE OutReal (log: Wr.T; r: REAL) =
  BEGIN
    TRY
      Wr.PutString(log, LOOPHOLE(r, ARRAY [0..BYTESIZE(REAL)-1] OF CHAR));
    EXCEPT
      Wr.Failure (err) =>
        StableError.Halt(
          "Cannot write to logfile: " & RdUtils.FailureText(err))
    END
  END OutReal;

PROCEDURE OutLongreal (log: Wr.T; r: LONGREAL) =
  BEGIN
    TRY
      Wr.PutString(log, LOOPHOLE(r, ARRAY [0..BYTESIZE(LONGREAL)-1] OF CHAR));
    EXCEPT
      Wr.Failure (err) =>
        StableError.Halt(
          "Cannot write to logfile: " & RdUtils.FailureText(err))
    END
  END OutLongreal;

PROCEDURE OutExtended (log: Wr.T; r: EXTENDED) =
  BEGIN
    TRY
      Wr.PutString(log, LOOPHOLE(r, ARRAY [0..BYTESIZE(EXTENDED)-1] OF CHAR));
    EXCEPT
      Wr.Failure (err) =>
        StableError.Halt(
          "Cannot write to logfile: " & RdUtils.FailureText(err))
    END
  END OutExtended;

PROCEDURE OutText(log: Wr.T; text: TEXT) =
  VAR len, start: INTEGER;  buf: ARRAY [0..255] OF CHAR;
  BEGIN
    IF text # NIL THEN
      len := Text.Length(text)
    ELSE
      len := -1
    END;
    OutInteger(log, len);
    start := 0;
    WHILE start < len DO
      Text.SetChars(buf, text, start);
      OutChars(log, SUBARRAY(buf, 0, MIN(NUMBER(buf), len - start)));
      INC (start, NUMBER(buf));
    END;
  END OutText;
The following procedures are provided in support of generic reading the log.

PROCEDURE InChar (log: Rd.T): CHAR RAISES {Error} =
  BEGIN
    TRY
      RETURN Rd.GetChar(log)
    EXCEPT
      Rd.EndOfFile => RAISE Error
    | Rd.Failure (err) =>
        <*NOWARN*> StableError.Halt(
          "InChar: Can not read log file: " & RdUtils.FailureText(err))
    END
  END InChar;

PROCEDURE InCharsLen (log: Rd.T): CARDINAL RAISES {Error} =
  BEGIN
    RETURN InInteger(log)
  END InCharsLen;

PROCEDURE InChars (    log  : Rd.T;
                   VAR chars: ARRAY OF CHAR)
  RAISES {Error} =
  VAR n:= NUMBER(chars) - NUMBER(chars) MOD BYTESIZE(Word.T);
  BEGIN
    TRY
      IF Rd.GetSub(log, SUBARRAY(chars, 0, n)) # n THEN
        RAISE Error
      END;
      FOR i:= n TO LAST(chars) DO
        chars[i]:= Rd.GetChar(log)
      END
    EXCEPT
      Rd.EndOfFile => RAISE Error
    | Rd.Failure (err) =>
        StableError.Halt("InChars: Can not read log file: "
                           & RdUtils.FailureText(err))
    END
  END InChars;

PROCEDURE InInteger (log: Rd.T;
                     min         := FIRST(INTEGER);
                     max         := LAST(INTEGER)   ):
    INTEGER RAISES {Error} =
  VAR
    i: INTEGER;
  BEGIN
    TRY
      IF Rd.GetSub(log, LOOPHOLE(i, ARRAY [0..BYTESIZE(INTEGER)-1] OF CHAR))
          # BYTESIZE(INTEGER) THEN
        RAISE Error
      END;
    EXCEPT
    | Rd.Failure (err) =>
        StableError.Halt("InInteger: Can not read log file: "
                           & RdUtils.FailureText(err))
    END;

    IF min <= i AND i <= max THEN
      RETURN i
    ELSE
      RAISE Error
    END (*IF*)
  END InInteger;

PROCEDURE InLongint (log: Rd.T;
                     min         := FIRST(LONGINT);
                     max         := LAST(LONGINT)   ):
    LONGINT RAISES {Error} =
  VAR
    i: LONGINT;
  BEGIN
    TRY
      IF Rd.GetSub(log, LOOPHOLE(i, ARRAY [0..BYTESIZE(LONGINT)-1] OF CHAR))
          # BYTESIZE(LONGINT) THEN
        RAISE Error
      END;
    EXCEPT
    | Rd.Failure (err) =>
        StableError.Halt("InLongint: Can not read log file: "
                           & RdUtils.FailureText(err))
    END;

    IF min <= i AND i <= max THEN
      RETURN i
    ELSE
      RAISE Error
    END (*IF*)
  END InLongint;

PROCEDURE InCardinal (log: Rd.T;
                      lim: CARDINAL := LAST(CARDINAL)):
  CARDINAL RAISES {Error} =
  BEGIN
    RETURN InInteger(log, 0, lim)
  END InCardinal;

PROCEDURE InLongcard (log: Rd.T;
                      lim: LONGCARD := LAST(LONGCARD)):
  LONGCARD RAISES {Error} =
  BEGIN
    RETURN InLongint(log, 0L, lim)
  END InLongcard;

PROCEDURE InBoolean (log: Rd.T): BOOLEAN RAISES {Error} =
  BEGIN
    TRY
      RETURN Rd.GetChar(log) = VAL(ORD(TRUE), CHAR)
    EXCEPT
      Rd.EndOfFile => RAISE Error
    | Rd.Failure (err) =>
        <*NOWARN*> StableError.Halt(
          "InBoolean: Can not read log file: " & RdUtils.FailureText(err))
    END
  END InBoolean;

PROCEDURE InReal (log: Rd.T): REAL RAISES {Error} =
  VAR
    r: REAL;
  BEGIN
    TRY
      IF Rd.GetSub(log, LOOPHOLE(r, ARRAY [0..BYTESIZE(REAL)-1] OF CHAR))
          # BYTESIZE(REAL) THEN
        RAISE Error
      END;
    EXCEPT
    | Rd.Failure (err) =>
        StableError.Halt("InReal: Can not read log file: "
                           & RdUtils.FailureText(err))
    END;
    RETURN r;
  END InReal;

PROCEDURE InLongreal (log: Rd.T): LONGREAL RAISES {Error} =
  VAR
    r: LONGREAL;
  BEGIN
    TRY
      IF Rd.GetSub(log, LOOPHOLE(r, ARRAY [0..BYTESIZE(LONGREAL)-1] OF CHAR))
          # BYTESIZE(LONGREAL) THEN
        RAISE Error
      END;
    EXCEPT
    | Rd.Failure (err) =>
        StableError.Halt("InLongreal: Can not read log file: "
                           & RdUtils.FailureText(err))
    END;
    RETURN r;
  END InLongreal;

PROCEDURE InExtended (log: Rd.T): EXTENDED RAISES {Error} =
  VAR
    r: EXTENDED;
  BEGIN
    TRY
      IF Rd.GetSub(log, LOOPHOLE(r, ARRAY [0..BYTESIZE(EXTENDED)-1] OF CHAR))
          # BYTESIZE(EXTENDED) THEN
        RAISE Error
      END;
    EXCEPT
    | Rd.Failure (err) =>
        StableError.Halt("InExtended: Can not read log file: "
                           & RdUtils.FailureText(err))
    END;
    RETURN r;
  END InExtended;

PROCEDURE InText(log: Rd.T) : TEXT
   RAISES {Error} =
  VAR len: INTEGER;
      txt: Text8.T;
      buf: ARRAY [0..255] OF CHAR;
  BEGIN
    len := InInteger(log);
    IF len = -1 THEN
      RETURN NIL
    ELSIF len < 0 THEN
      RAISE Error
    ELSIF len = 0 THEN
      RETURN "";
    ELSIF len <= NUMBER(buf) THEN
      InChars(log, SUBARRAY(buf, 0, len));
      RETURN Text.FromChars(SUBARRAY(buf, 0, len));
    ELSE
      txt := Text8.Create(len);
      InChars(log, SUBARRAY(txt.contents^, 0, len));
      RETURN txt
    END
  END InText;

BEGIN
END StableLog.