-- Transport mechanism: hack to re-order list members

-- Test.mesa

-- Andrew Birrell   4-Sep-80 12:35:25

DIRECTORY
 BodyDefs, BTreeDefs, HeapDefs, InlineDefs, IODefs, LogDefs, ProcessDefs,
 ProtocolDefs,
 RegBTreeDefs, RegistryDefs, RegServerDefs, RestartDefs, StringDefs,
 VMDefs;

Test: PROGRAM
IMPORTS BodyDefs, BTreeDefs, HeapDefs, InlineDefs, IODefs, LogDefs,
        ProcessDefs, RegBTreeDefs, RegistryDefs, RegServerDefs, StringDefs,
        VMDefs
EXPORTS RestartDefs =

BEGIN

OPEN IODefs;

btreeFile: VMDefs.FileHandle = VMDefs.OpenFile[name:"Test.BTree"L,
              options: oldOrNew];

groups: HeapDefs.ReaderHandle;
groupsRC: ProtocolDefs.ReturnCode;
groupsStamp: BodyDefs.Timestamp ← BodyDefs.oldestTime;

CompareRNames: PROC[n1, n2: BodyDefs.RName]
                  RETURNS [RegistryDefs.Comparison] =
   BEGIN
   length: CARDINAL = MIN [n1.length, n2.length];
   i: CARDINAL;
   FOR i IN [0 .. length)
   DO BEGIN
      ch1: CHARACTER = IF n1[i] IN CHARACTER['A .. 'Z] 
                       THEN (n1[i] - 'A) + 'a ELSE n1[i];
      ch2: CHARACTER = IF n2[i] IN CHARACTER['A .. 'Z] 
                       THEN (n2[i] - 'A) + 'a ELSE n2[i];
      IF ch1 < ch2 THEN RETURN [less];
      IF ch1 > ch2 THEN RETURN [greater];
      END
   ENDLOOP;
   IF n1.length < n2.length
   THEN RETURN [less]
   ELSE IF n1.length = n2.length
        THEN RETURN [equal]
        ELSE RETURN [greater];
   END;

EachGroup: PROC[name: BodyDefs.RName] RETURNS[ done: BOOLEAN ] =
   BEGIN
   regobj: RegBTreeDefs.RegistryObject ← RegBTreeDefs.Lookup[name, readGroup];
   orderWrong: BOOLEAN ← FALSE;
   prev: BodyDefs.RName ← [BodyDefs.maxRNameLength];
   CheckOrder: PROC[member: BodyDefs.RName] RETURNS[ done: BOOLEAN ] =
      BEGIN
      IF NOT orderWrong
      THEN BEGIN
           IF CompareRNames[prev, member] # less THEN orderWrong ← TRUE;
           prev.length ← 0; StringDefs.AppendString[prev, member];
           END;
      done ← FALSE;
      END;
   IF regobj.type # group THEN ERROR;
   RegistryDefs.Skip[regobj.reader]; --prefix--
   RegistryDefs.Skip[regobj.reader]; --remark--
   RegistryDefs.EnumerateRList[regobj.reader, CheckOrder];
   IF orderWrong
   THEN BEGIN
        LogWrongOrder[name];
        WriteString[name]; WriteString[" wrong ... "L];
        DO writer: HeapDefs.WriterHandle =
                       HeapDefs.HeapStartWrite[RSobject];
           stamp: BodyDefs.Timestamp ← RegistryDefs.MakeTimestamp[];
           HeapDefs.SetReaderOffset[regobj.reader, HeapDefs.objectStart];
           [,] ← RegistryDefs.ReadPrefix[regobj.reader,name];
           RegistryDefs.WritePrefix[writer, group, @stamp, name];
           RegistryDefs.Copy[regobj.reader, writer]; --remark--
           OrderSublist[regobj.reader, writer]; --members--
           THROUGH [1..8] -- owners, friends --
           DO RegistryDefs.Copy[regobj.reader, writer]; ENDLOOP;
           RegBTreeDefs.Insert[name, group, @stamp, writer, @regobj !
              RegBTreeDefs.UpdateFailed =>
                 BEGIN
                 WriteString["synchronizing! ... "L];
                 regobj ← info; LOOP
                 END ];
           EXIT
        ENDLOOP;
        WriteString["done;"L];
        END
   ELSE HeapDefs.HeapEndRead[regobj.reader];
   done ← FALSE;
   END;

LogWrongOrder: PROC[name: BodyDefs.RName] =
   BEGIN
   log: STRING = [128];
   StringDefs.AppendString[log, "Wrong order in group "L];
   StringDefs.AppendString[log, name];
   LogDefs.WriteLogEntry[log];
   END;

LogDeletion: PROC[name: BodyDefs.RName] =
   BEGIN
   log: STRING = [128];
   StringDefs.AppendString[log, "Group had extra name "L];
   StringDefs.AppendString[log, name];
   LogDefs.WriteLogEntry[log];
   END;

OrderSublist: PROC[reader: HeapDefs.ReaderHandle,
                   writer: HeapDefs.WriterHandle] =
   BEGIN
   memberReader: HeapDefs.ReaderHandle = HeapDefs.CopyReader[reader];
   stampReader: HeapDefs.ReaderHandle;
   count: CARDINAL;
   memberLength: CARDINAL ← 0;
   stampLength: CARDINAL ← 0;
   btree: BTreeDefs.BTreeHandle ← BTreeDefs.CreateAndInitializeBTree [
        fileH: LOOPHOLE[btreeFile],
        initializeFile: TRUE, useDefaultOrderingRoutines: FALSE,
        isFirstGreaterOrEqual: IsFirstGE,
        areTheyEqual: AreTheyEq ];
   EachMember: PROC[member: BodyDefs.RName] RETURNS[done: BOOLEAN] =
      BEGIN
      newStamp: BodyDefs.Timestamp;
      oldStamp: BodyDefs.Timestamp;
      [,] ← HeapDefs.HeapReadData[stampReader,
                                  [@newStamp,SIZE[BodyDefs.Timestamp]] ];
      IF BTreeDefs.Lookup[btree, RNameDesc[member],
                       DESCRIPTOR[@oldStamp,SIZE[BodyDefs.Timestamp]] ] #
          BTreeDefs.KeyNotFound
      THEN BEGIN
           BTreeDefs.Delete[btree, RNameDesc[member] ];
           END
      ELSE BEGIN
           memberLength ← memberLength + BodyDefs.RNameSize[member];
           stampLength ← stampLength + SIZE[BodyDefs.Timestamp];
           END;
      BTreeDefs.Insert[btree, RNameDesc[member],
                       DESCRIPTOR[@newStamp,SIZE[BodyDefs.Timestamp]] ];
      done ← FALSE;
      END;
   EachDelMember: PROC[member: BodyDefs.RName] RETURNS[done: BOOLEAN] =
      BEGIN
      stamp: BodyDefs.Timestamp;
      [,] ← HeapDefs.HeapReadData[stampReader,
                                  [@stamp,SIZE[BodyDefs.Timestamp]] ];
      IF BTreeDefs.Lookup[btree, RNameDesc[member],
                       DESCRIPTOR[@stamp,SIZE[BodyDefs.Timestamp]] ] #
          BTreeDefs.KeyNotFound
      THEN BEGIN
           LogDeletion[member];
           memberLength ← memberLength - BodyDefs.RNameSize[member];
           stampLength ← stampLength - SIZE[BodyDefs.Timestamp];
           BTreeDefs.Delete[btree, RNameDesc[member] ];
           END;
      done ← FALSE;
      END;
   WriteLength: PROC[length:CARDINAL] =
      BEGIN
      HeapDefs.HeapWriteData[writer, [@length,SIZE[CARDINAL]] ];
      END;
   WriteMembers: BTreeDefs.Call =
      BEGIN
      rName: BodyDefs.RName = [BodyDefs.maxRNameLength];
      rName.length ← 2*LENGTH[k];
      more ← TRUE; dirty ← FALSE;
      IF LENGTH[v] # SIZE[BodyDefs.Timestamp] THEN RETURN;
      InlineDefs.COPY[from: BASE[k], to: @(rName.text), nwords: LENGTH[k]];
      IF rName.length > 0 AND rName[rName.length-1] = '@
      THEN rName.length ← rName.length-1 -- undo padding kludge --;
      IF memberLength < BodyDefs.RNameSize[rName] THEN ERROR;
      memberLength ← memberLength - BodyDefs.RNameSize[rName];
      HeapDefs.HeapWriteRName[writer, rName];
      END; 
   WriteStamps: BTreeDefs.Call =
      BEGIN
      more ← TRUE; dirty ← FALSE;
      IF LENGTH[v] # SIZE[BodyDefs.Timestamp] THEN RETURN;
      IF stampLength < SIZE[BodyDefs.Timestamp] THEN ERROR;
      stampLength ← stampLength - SIZE[BodyDefs.Timestamp];
      HeapDefs.HeapWriteData[writer, [BASE[v], LENGTH[v]] ];
      END;
   RegistryDefs.Skip[reader]; stampReader ←  HeapDefs.CopyReader[reader];
   [,] ← HeapDefs.HeapReadData[stampReader, [@count,SIZE[CARDINAL]] ];
   RegistryDefs.Skip[reader]; -- reader is now at DelMember list --
   RegistryDefs.EnumerateRList[memberReader, EachMember];
   RegistryDefs.Skip[stampReader]; -- to DelMemberStamps --
   [,] ← HeapDefs.HeapReadData[stampReader, [@count,SIZE[CARDINAL]] ];
   RegistryDefs.Skip[memberReader]; -- to DelMember --
   RegistryDefs.EnumerateRList[memberReader, EachDelMember];
   HeapDefs.HeapEndRead[memberReader];
   HeapDefs.HeapEndRead[stampReader];
   WriteLength[memberLength];
   BTreeDefs.EnumerateFrom[btree, DESCRIPTOR[NIL,0], WriteMembers];
   IF memberLength # 0 THEN ERROR;
   WriteLength[stampLength];
   BTreeDefs.EnumerateFrom[btree, DESCRIPTOR[NIL,0], WriteStamps];
   IF stampLength # 0 THEN ERROR;
   [] ← BTreeDefs.ReleaseBTree[btree];
   RegistryDefs.Copy[reader, writer]; --DelMember--
   RegistryDefs.Copy[reader, writer]; --DelMemberStamps--
   END;

LowerCase: PROCEDURE[c: CHARACTER] RETURNS[CHARACTER] = INLINE
   { RETURN[IF c IN ['A..'Z] THEN c - 'A + 'a ELSE c] };

IsFirstGE: BTreeDefs.TestKeys =
   BEGIN
   -- parameters a,b: DESC FOR ARRAY OF WORD returns[ BOOLEAN]--
   aC: POINTER TO PACKED ARRAY OF CHARACTER = LOOPHOLE[BASE[a]];
   bC: POINTER TO PACKED ARRAY OF CHARACTER = LOOPHOLE[BASE[b]];
   FOR i:CARDINAL IN [0..2*MIN[LENGTH[a],LENGTH[b]])
   DO IF LowerCase[aC[i]] < LowerCase[bC[i]] THEN RETURN[FALSE];
      IF LowerCase[aC[i]] > LowerCase[bC[i]] THEN RETURN[TRUE];
   ENDLOOP;
   RETURN[LENGTH[a] >= LENGTH[b]];
   END;

AreTheyEq: BTreeDefs.TestKeys =
   BEGIN
   aC: POINTER TO PACKED ARRAY OF CHARACTER =LOOPHOLE[BASE[a]];
   bC: POINTER TO PACKED ARRAY OF CHARACTER = LOOPHOLE[BASE[b]];
   IF LENGTH[a] = LENGTH[b]
   THEN FOR i:CARDINAL IN [0..2*LENGTH[a])
        DO IF LowerCase[aC[i]] # LowerCase[bC[i]] THEN EXIT;
        REPEAT FINISHED => RETURN[TRUE];
        ENDLOOP;
   RETURN[FALSE];
   END;

RNameDesc: PROCEDURE[ name: BodyDefs.RName ]
             RETURNS[ DESCRIPTOR FOR ARRAY OF WORD ] =
   BEGIN
   IF name.length MOD 2 # 0 THEN name[name.length] ← '@;
   RETURN[ DESCRIPTOR[ @(name.text), (1+name.length)/2 ] ]
   END;

Main: PROCEDURE =
   BEGIN

   [groups, groupsRC] ← RegServerDefs.ReadMembers["Groups"L, @groupsStamp];

   IF groupsRC # [code:done, type:group] THEN ERROR;

   WriteLine["Test-hack running"L];

   RegistryDefs.EnumerateRList[groups, EachGroup];

   HeapDefs.HeapEndRead[groups];

   VMDefs.WaitFile[btreeFile];
   VMDefs.CloseFile[btreeFile];

   WriteString[" *** done *** "L];

   END;

ProcessDefs.Detach[ FORK Main[] ];

END.