-- Yodel: File and Owner Property commands
-- Hauser, March 19, 1985 4:42:49 pm PST
-- YodelProperties.mesa
-- Last Edited by: Hagmann, April 4, 1984 4:25:35 pm PST
DIRECTORY
AlpFile,
AlpineEnvironment,
AlpineFile,
AlpineInterimDirectory,
AlpInstance,
AlpTransaction ,
BasicTime,
Buttons,
IO,
Rope,
ViewerClasses,
ViewerTools,
YodelData;
YodelProperties: CEDAR PROGRAM
IMPORTS AlpineInterimDirectory, AlpFile, AlpInstance, AlpTransaction, IO, Rope, ViewerTools, YodelData
EXPORTS YodelData
 
 =
BEGIN OPEN YodelData;
  
 
ROPE: TYPE = Rope.ROPE;
accessListToCommaROPE: 
PROC [ accessList: AlpineEnvironment.AccessList]
RETURNS [resultRope: 
ROPE ← 
NIL] = {
IF accessList = 
NIL 
THEN resultRope ← Rope.Concat[resultRope,"*none*"]
ELSE {
UNTIL accessList = 
NIL 
DO
accessItem: AlpineEnvironment.RName ← accessList.first;
resultRope ← Rope.Concat[resultRope,accessItem];
accessList ← accessList.rest;
IF accessList # NIL THEN resultRope ← Rope.Concat[resultRope,", "];
ENDLOOP;
 
}
 
};
 
ExamineFile: PUBLIC PROC [trans: AlpTransaction.Handle,
       server: ROPE, directory: ROPE, file: ROPE,
       user: ROPE ← NIL, password: ROPE ← NIL ,
       displayProperties: AlpineFile.PropertySet ← AlpineFile.allProperties, 
       d: MyData]
       RETURNS [LIST OF REF ANY] = {
    fullFileName: ROPE;
    resultList: LIST OF REF ANY ← NIL;
    universalFile: AlpineEnvironment.UniversalFile;
    createdFile: BOOLEAN;
    properties: LIST OF AlpineEnvironment.PropertyValuePair;
    resultRope: ROPE ← NIL;
    fileHandle: AlpFile.Handle;
    outcome: AlpTransaction.Outcome;
    failureName: ROPE ← "" ;
    {    
      ENABLE UNWIND => IF trans # NIL THEN outcome ← AlpTransaction.Finish[trans, abort];
      IF trans = NIL THEN GOTO notAlpineFile;
      IF d.assertWheel THEN trans.AssertAlpineWheel[TRUE];
      fullFileName ← Rope.Concat["[",Rope.Concat[server,Rope.Concat["]",
       Rope.Concat[directory,file]]]];
      [universalFile, createdFile] ← AlpineInterimDirectory.OpenUnderTrans[
       transHandle: trans,
       fileName: fullFileName,
       createOptions: oldOnly];
      [fileHandle]← AlpFile.Open[transHandle: trans,
       universalFile: universalFile,
       access: readOnly,
       lock: [read, fail]
      ];
      properties ← AlpFile.ReadProperties[handle: fileHandle, lock: [read, fail]
       ! AlpInstance.LockFailed => GOTO lockError;
      ];
      {
        size: INT;
        size ← fileHandle.GetSize[];
        ViewerTools.SetContents[viewer: d.oSize,
                 contents: IO.PutFR["%g", IO.int[size]],
                 paint: FALSE];
      };
      UNTIL properties = NIL DO
          property: AlpineEnvironment.PropertyValuePair ← properties.first ;
          properties ← properties.rest;
          BEGIN
            SELECT property.property FROM
                 -- byteLength
              byteLength => {
                byteLength: INT ← NARROW[property,
                  AlpineEnvironment.PropertyValuePair.byteLength].byteLength;
                ViewerTools.SetContents[viewer: d.oByteLength,
                 contents: IO.PutFR["%g", IO.int[byteLength]],
                 paint: FALSE]; 
              };
                 -- createTime
              createTime => {
                createTime: BasicTime.GMT ← NARROW[property,
                  AlpineEnvironment.PropertyValuePair.createTime].createTime;
                -- ViewerTools.SetContents[viewer: d.oCreateTime,
                 -- contents: IO.PutFR["%g", IO.time[createTime]],
                 -- paint: FALSE]; 
              };
                  -- highWaterMark
              highWaterMark => {
                highWaterMark: AlpineEnvironment.PageCount ← NARROW[property,
                  AlpineEnvironment.PropertyValuePair.highWaterMark].highWaterMark;
                ViewerTools.SetContents[viewer: d.oHighWaterMark,
                 contents: IO.PutFR["%g", IO.int[highWaterMark]],
                 paint: FALSE];
              };
                   -- modifyAccess
              modifyAccess => {
                modifyAccess: AlpineEnvironment.AccessList ← NARROW[property,
                  AlpineEnvironment.PropertyValuePair.modifyAccess].modifyAccess;
                resultRope ← accessListToCommaROPE[modifyAccess];
                ViewerTools.SetContents[viewer: d.oModifyAccess,
                 contents: resultRope,
                 paint: FALSE];
              };
                  -- owner
              owner => {
                owner: AlpineEnvironment.OwnerName ← NARROW[property,
                  AlpineEnvironment.PropertyValuePair.owner].owner;
                ViewerTools.SetContents[viewer: d.oOwner,
                 contents: IO.PutFR["%g", IO.rope[owner]],
                 paint: FALSE];
              };
                    -- readAccess
              readAccess => {
                readAccess: AlpineEnvironment.AccessList ← NARROW[property,
                  AlpineEnvironment.PropertyValuePair.readAccess].readAccess;
                resultRope ←  accessListToCommaROPE[readAccess] ;
                                ViewerTools.SetContents[viewer: d.oReadAccess,
                 contents: resultRope,
                 paint: FALSE];
              };
                   -- stringName
              stringName => {
                stringName: ROPE ← NARROW[property,
                  AlpineEnvironment.PropertyValuePair.stringName].stringName;
                ViewerTools.SetContents[viewer: d.oStringName,
                 contents: IO.PutFR["%g", IO.rope[stringName]],
                 paint: FALSE];
              };
                -- bogus (Well, you see that if you don't use all the
                -- variants from PropertyValuePair somewhere, then
                -- the compiler has this fatal error in pass 4.)
                   -- version
              version => {
                version: AlpineEnvironment.FileVersion ← NARROW[property,
                  AlpineEnvironment.PropertyValuePair.version].version;
                -- bogus (Well, you see that if you don't use all the
                -- variants from PropertyValuePair somewhere, then
                -- the compiler has this fatal error in pass 4.)
              };
              
            ENDCASE;
          END;
        ENDLOOP;
      outcome ← AlpTransaction.Finish[trans, commit];
      RETURN [resultList];
   
      EXITS
         lockError => {
           outcome ← AlpTransaction.Finish[trans, abort];
           ViewerTools.SetContents[viewer: d.oByteLength, contents: "", paint: FALSE];
           ViewerTools.SetContents[viewer: d.oHighWaterMark, contents: "", paint: FALSE];
           ViewerTools.SetContents[viewer: d.oModifyAccess, contents: "", paint: FALSE];
           ViewerTools.SetContents[viewer: d.oOwner, contents: "", paint: FALSE];
           ViewerTools.SetContents[viewer: d.oReadAccess, contents: "", paint: FALSE];
           ViewerTools.SetContents[viewer: d.oStringName, contents: "", paint: FALSE];
           RETURN[CONS[
            NARROW["lock prevents access to file",ROPE]
            ,NIL]];
          };
         notAlpineFile => {
          RETURN[CONS[
            NARROW["Cannot Examine a non-Alpine file",ROPE]
            ,NIL]];
          };
    };       
};
ExamineProc: PUBLIC Buttons.ButtonProc=
  BEGIN
    resultList: LIST OF REF ANY ← NIL;
      d: MyData = NARROW[clientData];
      p: ViewerClasses.Viewer = NARROW[parent];
      server, user, file, password: ROPE;
      directory, restOfPattern: ROPE;
      
      callExamine: YodelData.PerformProc = {
         RETURN[ExamineFile[trans, server, directory, restOfPattern, user, password,
          d.displayProperties, d]];
       };
        
      
      d.stopFlag ← FALSE;
      
      [user, password, server, directory, file] ← ParseSArgs[d];
      
    [directory, restOfPattern] ← DecomposePattern[server: server, pattern: file, user: user];
    
      d.out.PutF["\nExamine of [%g]%g%g\n",
       IO.rope[server], IO.rope[directory], IO.rope[restOfPattern]];
      resultList ← PerformOp[performProc: callExamine,
       server: server, user: user, password: password];
      DO
      nowRope: ROPE ← NARROW[IF resultList = NIL THEN NIL ELSE resultList.first];
      IF resultList = NIL THEN EXIT;
      resultList ← resultList.rest;
      d.out.PutF["  %g\n", IO.rope[nowRope]];
    ENDLOOP;
    CreateButtons[d, p.parent.parent];
    END;
getAccessList: PROC [stream: IO.STREAM]
 RETURNS [outList: AlpineEnvironment.AccessList ← NIL] = {
  UNTIL IO.EndOf[stream] DO {
    token: ROPE ← IO.GetTokenRope[stream, IO.IDProc 
    ! IO.EndOfStream => GOTO endList].token;
    IF (token # NIL) AND Rope.Fetch[token] # ', THEN
     outList ← CONS[NARROW[token, AlpineEnvironment.RName], outList]; 
  };
  ENDLOOP;
  EXITS
   endList => RETURN;
};
ApplyToFile: PUBLIC PROC [trans: AlpTransaction.Handle,
       server: ROPE, directory: ROPE, file: ROPE,
       user: ROPE ← NIL, password: ROPE ← NIL ,
       displayProperties: AlpineFile.PropertySet ← AlpineFile.allProperties, 
       d: MyData]
       RETURNS [LIST OF REF ANY] = {
    
    fullFileName: ROPE;
    resultList: LIST OF REF ANY ← NIL;
    universalFile: AlpineEnvironment.UniversalFile;
    createdFile: BOOLEAN;
    newProperties: LIST OF AlpineEnvironment.PropertyValuePair ← NIL ;
    properties: LIST OF AlpineEnvironment.PropertyValuePair;
    resultRope: ROPE ← NIL;
    fileHandle: AlpFile.Handle;
    fileID: AlpineEnvironment.FileID;
    outcome: AlpTransaction.Outcome;
    scratchStream: IO.STREAM ← NIL;
    failureName: ROPE ← "" ;
    {    
      ENABLE UNWIND => IF trans # NIL THEN outcome ← AlpTransaction.Finish[trans, abort];
      IF trans = NIL THEN GOTO notAlpineFile;
      IF d.assertWheel THEN trans.AssertAlpineWheel[TRUE];
      fullFileName ← Rope.Concat["[",Rope.Concat[server,Rope.Concat["]",
       Rope.Concat[directory,file]]]];
      [universalFile, createdFile] ← AlpineInterimDirectory.OpenUnderTrans[
       transHandle: trans,
       fileName: fullFileName,
       createOptions: oldOnly];
      [fileHandle, fileID]← AlpFile.Open[transHandle: trans,
       universalFile: universalFile,
       access: readWrite,
       lock: [write, fail]
      ];
      properties ← AlpFile.ReadProperties[handle: fileHandle, lock: [read, fail]
       ! AlpInstance.LockFailed => GOTO lockError;
      ];
      {
        newSizeRope: ROPE;
        newSize: INT;
        size: INT;
        size ← fileHandle.GetSize[];
        newSizeRope ← ViewerTools.GetContents[viewer: d.oSize];
        scratchStream ← IO.RIS[newSizeRope, scratchStream];
        newSize ← NARROW[IO.GetInt[scratchStream]] ;
        -- newSize ← MAX[newSize,size];
        fileHandle.SetSize[newSize];
      };
      UNTIL properties = NIL DO
          property: AlpineEnvironment.PropertyValuePair ← properties.first ;
          properties ← properties.rest;
          BEGIN
            SELECT property.property FROM
                 -- byteLength
              byteLength => {
                newByteLengthRope: ROPE;
                newByteLength: INT ;
                byteLength: INT ← NARROW[property,
                  AlpineEnvironment.PropertyValuePair.byteLength].byteLength;
                newByteLengthRope ← ViewerTools.GetContents[viewer: d.oByteLength];
                scratchStream ← IO.RIS[newByteLengthRope, scratchStream]; 
                newByteLength ← IO.GetInt[scratchStream] ;
                newProperties ← CONS[[byteLength[newByteLength]], newProperties];
              };
                  -- highWaterMark
              highWaterMark => {
                newHighWaterMark: INT ;
                newHighWaterMarkRope: ROPE ←
                 ViewerTools.GetContents[viewer: d.oHighWaterMark];
                scratchStream ← IO.RIS[newHighWaterMarkRope, scratchStream];
                newHighWaterMark ← NARROW[IO.GetInt[scratchStream]] ;
                newProperties ← CONS[[highWaterMark[newHighWaterMark]], newProperties];
              };
                   -- modifyAccess
              modifyAccess => {
                newModifyAccess: AlpineEnvironment.AccessList ← NIL ;
                newModifyAccessRope: ROPE ←
                 ViewerTools.GetContents[viewer: d.oModifyAccess];
                scratchStream ← IO.RIS[newModifyAccessRope, scratchStream];
                newModifyAccess ← getAccessList[scratchStream];
                newProperties ← CONS[[modifyAccess[newModifyAccess]], newProperties];
              };
                  -- owner
              owner => {
                newOwnerRope: ROPE;
                newOwner: ROPE;
                newOwnerRope ← ViewerTools.GetContents[viewer: d.oOwner];
                newOwner ← NARROW[newOwnerRope];
                newProperties ← CONS[[owner[newOwner]], newProperties];
              };
                    -- readAccess
              readAccess => {
                newReadAccess: AlpineEnvironment.AccessList ← NIL ;
                newReadAccessRope: ROPE ←
                 ViewerTools.GetContents[viewer: d.oReadAccess];
                scratchStream ← IO.RIS[newReadAccessRope, scratchStream];
                newReadAccess ← getAccessList[scratchStream];
                newProperties ← CONS[[readAccess[newReadAccess]], newProperties];
              };
                   -- stringName
              stringName => {
                newStringName: AlpineEnvironment.String;
                newStringNameRope: ROPE ←
                 ViewerTools.GetContents[viewer: d.oStringName];
                newStringName ← NARROW[newStringNameRope];
                newProperties ← CONS[[stringName[newStringName]], newProperties];
              };
              
            ENDCASE;
          END;
        ENDLOOP;
      AlpFile.WriteProperties[handle: fileHandle, properties: newProperties,
       lock: [read, fail]
       ! AlpInstance.LockFailed => GOTO lockError;
      ];
      outcome ← AlpTransaction.Finish[trans, commit];
      RETURN [resultList];
   
      EXITS
         lockError => {
           outcome ← AlpTransaction.Finish[trans, abort];
           ViewerTools.SetContents[viewer: d.oByteLength, contents: "", paint: FALSE];
           ViewerTools.SetContents[viewer: d.oHighWaterMark, contents: "", paint: FALSE];
           ViewerTools.SetContents[viewer: d.oModifyAccess, contents: "", paint: FALSE];
           ViewerTools.SetContents[viewer: d.oOwner, contents: "", paint: FALSE];
           ViewerTools.SetContents[viewer: d.oReadAccess, contents: "", paint: FALSE];
           ViewerTools.SetContents[viewer: d.oStringName, contents: "", paint: FALSE];
           RETURN[CONS[
            NARROW["lock prevents access to file",ROPE]
            ,NIL]];
          };
         notAlpineFile => {
          RETURN[CONS[
            NARROW["Cannot Apply to a non-Alpine file",ROPE]
            ,NIL]];
           };
    };       
};
ApplyProc: PUBLIC Buttons.ButtonProc=
  BEGIN
    resultList: LIST OF REF ANY ← NIL;
      d: MyData = NARROW[clientData];
      p: ViewerClasses.Viewer = NARROW[parent];
      server, user, file, password: ROPE;
      directory, restOfPattern: ROPE;
      
      callApply: YodelData.PerformProc = {
         RETURN[ApplyToFile[trans, server, directory, restOfPattern, user, password,
          d.displayProperties, d]];
       };
        
      
      d.stopFlag ← FALSE;
      
      [user, password, server, directory, file] ← ParseSArgs[d];
      
    [directory, restOfPattern] ← DecomposePattern[server:server, pattern: file, user: user];
    
      d.out.PutF["\nApply changes to [%g]%g%g\n",
       IO.rope[server], IO.rope[directory], IO.rope[restOfPattern]];
      resultList ← PerformOp[performProc: callApply,
       server: server, user: user, password: password];
      DO
      nowRope: ROPE ← NARROW[IF resultList = NIL THEN NIL ELSE resultList.first];
      IF resultList = NIL THEN EXIT;
      resultList ← resultList.rest;
      d.out.PutF["  %g\n", IO.rope[nowRope]];
    ENDLOOP;
    CreateButtons[d, p.parent.parent];
    END;
 
ReadQuota: PUBLIC PROC [trans: AlpTransaction.Handle,
       server: ROPE, directory: ROPE,
       user: ROPE ← NIL, password: ROPE ← NIL ,
       d: MyData]
       RETURNS [LIST OF REF ANY] = {
 
    resultList: LIST OF REF ANY ← NIL;
    
    resultRope: ROPE ← NIL;
    outcome: AlpTransaction.Outcome;
    properties: LIST OF AlpineEnvironment.OwnerPropertyValuePair;
    pageLimit, spaceInUse: AlpineEnvironment.PageCount;
    failureName: ROPE ← "" ;
    {    
      
      IF trans = NIL THEN GOTO notAlpineFile;
      IF d.assertWheel THEN trans.AssertAlpineWheel[TRUE];
      properties ← AlpTransaction.ReadOwnerProperties[
       handle: trans,
       volumeGroupID: AlpTransaction.GetNextVolumeGroup[handle: trans,
         previousGroup: AlpineEnvironment.nullVolumeGroupID, lock: [none, fail]],
       owner: directory,
       desiredProperties: [quota: TRUE, spaceInUse: TRUE]];
      outcome ← AlpTransaction.Finish[trans, commit];
      UNTIL properties = NIL DO -- because ReadOwnerProperties does not sort them yet ...
        WITH properties.first SELECT FROM
          q: AlpineEnvironment.OwnerPropertyValuePair.quota => pageLimit ← q.quota;
          s: AlpineEnvironment.OwnerPropertyValuePair.spaceInUse => spaceInUse ← s.spaceInUse;
        ENDCASE ;
        properties ← properties.rest;
      ENDLOOP;
      RETURN [CONS[NARROW[
          IO.PutFR["Page Limit is %g and Space in Use is %g",
           IO.int[pageLimit], IO.int[spaceInUse]],ROPE]
          ,NIL]];
      EXITS
       notAlpineFile => {
          RETURN[CONS[
            NARROW["Cannot get the quota for a non-Alpine server",ROPE]
            ,NIL]];
          };
    };       
};
QuotaProc: PUBLIC Buttons.ButtonProc=
  BEGIN
      result: ROPE;
      resultList: LIST OF REF ANY ← NIL;
      d: MyData = NARROW[clientData];
      server, user, file, password: ROPE;
      directory: ROPE;
      
      callQuota: YodelData.PerformProc = {
         RETURN[ReadQuota[trans, server,
      directory.Substr[start: 1, len: directory.Size[]-2],
      user,
      password,
      d]]
       };
      
      [user, password, server, directory, file] ← ParseSArgs[d];
      
      d.out.PutF["\nQuota for [%g]%g\n", IO.rope[server], IO.rope[directory]];
    resultList ← PerformOp[performProc: callQuota,
     server: server, user: user, password: password ];
    result ← NARROW[IF resultList = NIL THEN NIL ELSE resultList.first];
    d.out.PutF["  %g\n", IO.rope[result]];
    
  END;
 
GetOwnerProperties: PUBLIC PROC [trans: AlpTransaction.Handle, server: ROPE,
directory: ROPE, user: ROPE ← NIL, password: ROPE ← NIL , d: MyData]
RETURNS [LIST OF REF ANY] = {
 
resultList: LIST OF REF ANY ← NIL;
resultRope: ROPE ← NIL;
outcome: AlpTransaction.Outcome;
properties: LIST OF AlpineEnvironment.OwnerPropertyValuePair;
rootProperties: LIST OF AlpineEnvironment.PropertyValuePair;
failureName: ROPE ← "" ;
rootFile: AlpineEnvironment.UniversalFile ← AlpineEnvironment.nullUniversalFile ;
fileHandle: AlpFile.Handle;
{    
ENABLE UNWIND => IF trans # NIL THEN outcome ← AlpTransaction.Finish[trans, abort];
IF trans = NIL THEN GOTO notAlpineFile;
IF d.assertWheel THEN trans.AssertAlpineWheel[TRUE];
properties ← AlpTransaction.ReadOwnerProperties[
handle: trans,
volumeGroupID: AlpTransaction.GetNextVolumeGroup[handle: trans, 
  previousGroup: AlpineEnvironment.nullVolumeGroupID, lock: [none, fail]],
owner: directory];
UNTIL properties = 
NIL 
DO 
-- because ReadOwnerProperties does not sort them yet ...
WITH properties.first 
SELECT 
FROM
q: AlpineEnvironment.OwnerPropertyValuePair.createAccessList => {
createList: AlpineEnvironment.AccessList ← q.createAccessList;
resultRope: ROPE ← accessListToCommaROPE[createList];
ViewerTools.SetContents[viewer: d.oCreateAccessList, contents: resultRope, paint: FALSE];
};
 
s: AlpineEnvironment.OwnerPropertyValuePair.modifyAccessList => {
modifyList: AlpineEnvironment.AccessList ← s.modifyAccessList;
};
 
filler to use all the variants so the compiler won't blow up
(this is the cedar version of the "don't delete this line" comment
r: AlpineEnvironment.OwnerPropertyValuePair.rootFile => {
rootFile ← r.rootFile ;
};
 
t: AlpineEnvironment.OwnerPropertyValuePair.quota => {};
u: AlpineEnvironment.OwnerPropertyValuePair.spaceInUse => {};
ENDCASE ;
 
properties ← properties.rest;
ENDLOOP;
 
IF rootFile = AlpineEnvironment.nullUniversalFile 
THEN {
RETURN [CONS[NARROW[
 "Could not find root file on owner property list",ROPE]
 ,NIL]];
};
 
[fileHandle]← AlpFile.Open[transHandle: trans,
universalFile: rootFile,
access: readOnly,
lock: [read, fail]
];
rootProperties ← AlpFile.ReadProperties[handle: fileHandle, lock: [read, fail]
! AlpInstance.LockFailed => GOTO lockError;
];
UNTIL rootProperties = 
NIL 
DO
property: AlpineEnvironment.PropertyValuePair ← rootProperties.first ;
SELECT property.property 
FROM
modifyAccess => {
modifyAccess: AlpineEnvironment.AccessList ← NARROW[property,
AlpineEnvironment.PropertyValuePair.modifyAccess].modifyAccess;
resultRope ← accessListToCommaROPE[modifyAccess];
ViewerTools.SetContents[viewer: d.oRootModifyAccess,
contents: resultRope,
paint: FALSE];
};
 
readAccess => {
readAccess: AlpineEnvironment.AccessList ← NARROW[property,
AlpineEnvironment.PropertyValuePair.readAccess].readAccess;
resultRope ← accessListToCommaROPE[readAccess];
ViewerTools.SetContents[viewer: d.oRootReadAccess,
contents: resultRope,
paint: FALSE];
};
 
ENDCASE;
 
rootProperties ← rootProperties.rest;
ENDLOOP;
 
outcome ← AlpTransaction.Finish[trans, commit];
RETURN [CONS[NARROW[
 "Owner Properties access successful",ROPE]
 ,NIL]];
EXITS
notAlpineFile => {
RETURN[CONS[
  NARROW["Cannot get owner properties for a non-Alpine server",ROPE]
  ,NIL]];
};
 
lockError => {
RETURN[CONS[
  NARROW["Lock prevents access to root file",ROPE]
  ,NIL]];
};
 
 
};       
 
};
 
GetOwnerPropertiesProc: PUBLIC Buttons.ButtonProc=
  BEGIN
      resultList: LIST OF REF ANY ← NIL;
      d: MyData = NARROW[clientData];
      p: ViewerClasses.Viewer = NARROW[parent];
      server, user, file, password: ROPE;
      directory: ROPE;
      
      callGet: YodelData.PerformProc = {
         RETURN[GetOwnerProperties[trans,
          server,
      directory.Substr[start: 1, len: directory.Size[]-2],
      user,
      password,
      d]]
       };
      
      d.stopFlag ← FALSE;
      
      [user, password, server, directory, file] ← ParseSArgs[d];
      
      d.out.PutF["\nGet Properties for [%g]%g\n", IO.rope[server], IO.rope[directory]];
    resultList ← PerformOp[performProc: callGet,
     server: server, user: user, password: password];
    DO
      nowRope: ROPE ← NARROW[IF resultList = NIL THEN NIL ELSE resultList.first];
      IF resultList = NIL THEN EXIT;
      resultList ← resultList.rest;
      d.out.PutF["  %g\n", IO.rope[nowRope]];
    ENDLOOP;
    CreateButtons[d, p.parent.parent];
    
  END;
 
PutOwnerProperties: PUBLIC PROC [trans: AlpTransaction.Handle,
server: ROPE, directory: ROPE, user: ROPE ← NIL, password: ROPE ← NIL , d: MyData]
RETURNS [LIST OF REF ANY] = {
 
resultList: LIST OF REF ANY ← NIL;
resultRope: ROPE ← NIL;
rootFileHandle: AlpFile.Handle;
outcome: AlpTransaction.Outcome;
rootUniversalFile: AlpineEnvironment.UniversalFile;
properties: LIST OF AlpineEnvironment.OwnerPropertyValuePair;
rootNewProperties: LIST OF AlpineEnvironment.PropertyValuePair ← NIL ;
fooNewProperties, newProperties: LIST OF AlpineEnvironment.OwnerPropertyValuePair ← NIL ;
scratchStream: IO.STREAM ← NIL;
failureName: ROPE ← "" ;
{    
ENABLE UNWIND => IF trans # NIL THEN outcome ← AlpTransaction.Finish[trans, abort];
      
IF trans = NIL THEN GOTO notAlpineFile;
IF d.assertWheel THEN trans.AssertAlpineWheel[TRUE];
properties ← AlpTransaction.ReadOwnerProperties[
handle: trans,
volumeGroupID: AlpTransaction.GetNextVolumeGroup[handle: trans,
previousGroup: AlpineEnvironment.nullVolumeGroupID, lock: [none, fail]],
owner: directory];
UNTIL properties = 
NIL 
DO 
-- because ReadOwnerProperties does not sort them yet ...
WITH properties.first 
SELECT 
FROM
q: AlpineEnvironment.OwnerPropertyValuePair.createAccessList => {
createList: AlpineEnvironment.AccessList ← q.createAccessList;
newCreateList: AlpineEnvironment.AccessList ← NIL ;
newCreateListRope: ROPE ←ViewerTools.GetContents[viewer: d.oCreateAccessList];
scratchStream ← IO.RIS[newCreateListRope, scratchStream];
newCreateList ← getAccessList[scratchStream];
newProperties ← CONS[[createAccessList[newCreateList]], newProperties];
scratchStream ← IO.RIS[ViewerTools.GetContents[viewer: d.oRootReadAccess], scratchStream];
rootNewProperties ← CONS[[readAccess[getAccessList[scratchStream]]], rootNewProperties];
scratchStream ← IO.RIS[ViewerTools.GetContents[viewer: d.oRootModifyAccess], scratchStream];
rootNewProperties ← CONS[[modifyAccess[getAccessList[scratchStream]]], rootNewProperties];
};
 
s: AlpineEnvironment.OwnerPropertyValuePair.modifyAccessList => {
The compiler insists that I leave this code here.
modifyList: AlpineEnvironment.AccessList ← s.modifyAccessList;
newModifyList: AlpineEnvironment.AccessList ← NIL ;
fooNewProperties ← CONS[[modifyAccessList[newModifyList]], newProperties];
};
 
r: AlpineEnvironment.OwnerPropertyValuePair.rootFile => {
rootUniversalFile  ← r.rootFile;
};
 
ENDCASE;
 
properties ← properties.rest;
ENDLOOP;
 
AlpTransaction.WriteOwnerProperties[
handle: trans,
volumeGroupID: AlpTransaction.GetNextVolumeGroup[handle: trans,
  previousGroup: AlpineEnvironment.nullVolumeGroupID, lock: [none, wait]],
owner: directory,
overCommitQuotasIfNeeded: TRUE,
properties: newProperties];
[rootFileHandle]← AlpFile.Open[transHandle: trans,
universalFile: rootUniversalFile,
access: readWrite,
lock: [write, fail]];
AlpFile.WriteProperties[handle: rootFileHandle, properties: rootNewProperties,
lock: [read, fail]
! AlpInstance.LockFailed => GOTO lockError;
];
outcome ← AlpTransaction.Finish[trans, commit];
RETURN [
CONS[
NARROW[
"Put of owner properties successful",ROPE]
,NIL]];
 
   
EXITS
lockError => {
outcome ← AlpTransaction.Finish[trans, abort];
RETURN[
CONS[
NARROW["lock prevents access to root file",ROPE]
,NIL]];
 
};
 
notAlpineFile => {
RETURN[
CONS[
NARROW["Cannot put owner properties for a non-Alpine server",ROPE]
,NIL]];
 
};
 
 
};       
 
};
 
PutOwnerPropertiesProc: PUBLIC Buttons.ButtonProc=
  BEGIN
      resultList: LIST OF REF ANY ← NIL;
      d: MyData = NARROW[clientData];
      p: ViewerClasses.Viewer = NARROW[parent];
      server, user, file, password: ROPE;
      directory: ROPE;
      
      callPut: YodelData.PerformProc = {
         RETURN[PutOwnerProperties[trans,
          server,
      directory.Substr[start: 1, len: directory.Size[]-2],
      user,
      password,
      d]]
       };
      
      d.stopFlag ← FALSE;
      
      [user, password, server, directory, file] ← ParseSArgs[d];
      
      d.out.PutF["\nPut Properties for [%g]%g\n", IO.rope[server], IO.rope[directory]];
    resultList ← PerformOp[performProc: callPut,
     server: server, user: user, password: password];
    DO
      nowRope: ROPE ← NARROW[IF resultList = NIL THEN NIL ELSE resultList.first];
      IF resultList = NIL THEN EXIT;
      resultList ← resultList.rest;
      d.out.PutF["  %g\n", IO.rope[nowRope]];
    ENDLOOP;
    CreateButtons[d, p.parent.parent];
    
  END;
END.