YodelUserImpl2.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Yodel: User commands
Last Edited by:
Jack Kent, September 4, 1986 7:30:59 pm PDT
Carl Hauser, February 13, 1986 1:34:22 pm PST
Hauser, April 19, 1985 4:45:25 pm PST
Bob Hagmann October 11, 1985 11:34:52 am PDT
DIRECTORY
AlpFile USING[ GetSize, SetSize, Handle, LockOption, ReadProperties, ReadPages, WritePages, WriteProperties],
AlpineEnvironment USING[ AccessList, ByteCount, bytesPerPage, nullUniversalFile, nullVolumeGroupID, Outcome, OwnerName, OwnerPropertySet, OwnerPropertyValuePair, PageCount, Principal, Property, PropertyValuePair, UniversalFile, wordsPerPage ],
AlpineFile USING [ PropertySet],
AlpDirectory,
AlpineDirectory,
AlpineInterimDirectory,
AlpInstance,
AlpTransaction,
BasicTime USING[ GMT],
Buttons USING[ ButtonProc ],
CedarProcess USING[ SetPriority ],
FSUSING[ Close, ComponentPositions, Copy, Create, Delete, EnumerateForNames, Error, ExpandName, GetInfo, GetName, Open, OpenFile, NameProc, Read, Rename, SetByteCountAndCreatedTime, Write],
IO,
List,
Menus,
RefText,
Rope,
RPC,
ViewerClasses USING[ Viewer ],
ViewerTools USING[ GetContents],
VMUSING[ AddressForPageNumber, Allocate, Free, Interval],
YodelData;
YodelUserImpl2: CEDAR PROGRAM
IMPORTS AlpDirectory, AlpFile, AlpInstance, AlpTransaction, AlpineInterimDirectory, CedarProcess, FS, IO, List, Rope, RPC, ViewerTools, VM, YodelData
EXPORTS YodelData
=
BEGIN OPEN AE: AlpineEnvironment, YodelData;
ROPE: TYPE = Rope.ROPE;
ByteCount: TYPE = AE.ByteCount;
bytesPerPage: INT = AE.bytesPerPage;
PageCount: TYPE = AE.PageCount;
PropertyValuePair: TYPE = AE.PropertyValuePair;
UniversalFile: TYPE = AE.UniversalFile;
wordsPerPage: INT = AE.wordsPerPage;
PagesForBytes: PROC [byteLength: ByteCount] RETURNS [pageCount: PageCount] = {
RETURN [(byteLength+bytesPerPage-1)/bytesPerPage];
};
BytesForPages: PROC [pageCount: PageCount] RETURNS [byteLength: ByteCount] = {
RETURN [pageCount * bytesPerPage];
};
hasPattern: PUBLIC SAFE PROC [pattern: ROPE] RETURNS [BOOL] = {
RETURN [-1 # Rope.Find[s1: pattern, s2: "*"]];
};
ParseDArgs: PROC [user: ROPE, d: MyData] RETURNS [destServer, destDir, destFile: ROPENIL, parseError: BOOLFALSE, errorExplanation: ROPENIL] = {
destFileTemp: ROPE ;
fullFName: ROPE;
cp: FS.ComponentPositions;
dirOmitted: BOOL;
trimStar: BOOLFALSE;
destFileTemp ← ViewerTools.GetContents[d.dest];
[fullFName: fullFName, cp: cp, dirOmitted: dirOmitted] ← FS.ExpandName[name: destFileTemp, wDir: "[]<>" ! FS.Error => {
parseError ← TRUE;
errorExplanation ← error.explanation;
CONTINUE ;
};
];
IF parseError THEN {
[fullFName: fullFName, cp: cp, dirOmitted: dirOmitted] ← FS.ExpandName[name: (destFileTemp ← destFileTemp.Concat["*"]), wDir: "[]<>" ! FS.Error => {
GOTO FSError ;
};
];
trimStar ← TRUE;
};
parseError ← FALSE;
destServer ← fullFName.Substr[cp.server.start, cp.server.length];
IF dirOmitted THEN destFile ← ""
ELSE {
destFile ← fullFName.Substr[cp.dir.start - 1];
IF trimStar THEN destFile ← destFile.Substr[0, destFile.Length[]-1];
};
[destDir] ← DecomposePattern[server: destServer, pattern: destFile, user: user];
EXITS
FSError => {};
};
PrintResult: PUBLIC PROC [out: IO.STREAM, resultList: LIST OF REF ANY] RETURNS [printedSomething: BOOLFALSE] = {
DO
nowRope: ROPENARROW[IF resultList = NIL THEN NIL ELSE resultList.first];
IF resultList = NIL THEN EXIT;
resultList ← resultList.rest;
out.PutF[" %g\n", IO.rope[nowRope]];
printedSomething ← TRUE ;
ENDLOOP;
};
isDirectory: PROC [fileName: ROPE] RETURNS [dir: BOOL] = {
len: INT ;
IF fileName.IsEmpty[] THEN RETURN[TRUE];
len ← fileName.Length[];
IF fileName.InlineFetch[len-1] = '> OR fileName.InlineFetch[len-1] = '/ THEN RETURN [TRUE] ELSE RETURN[FALSE];
};
excessBytes is only used if expunging = TRUE. - this is a holdover from earlier code
fileCopy: PROC [trans: AlpTransaction.Handle, to: ROPE, from: ROPE, fullCopy: BOOL, user: ROPENIL, password: ROPENIL, expunging: BOOLEANFALSE, excessBytes: AlpineEnvironment.ByteCount ← 0, caller: AlpineEnvironment.Principal, key: RPC.EncryptionKey, serverForTrans: ROPE, d: MyData] RETURNS [copyResult: LIST OF REF ANYNIL] = {
FileStateObject: TYPE = RECORD [
SELECT type: {alpine, fs} FROM
alpine => [fileHandle: AlpFile.Handle],
fs => [openFile: FS.OpenFile],
ENDCASE];
AlpineFileStateObject: TYPE = FileStateObject.alpine;
FSFileStateObject: TYPE = FileStateObject.fs;
FileState: TYPE = REF FileStateObject;
AlpineFileState: TYPE = REF AlpineFileStateObject;
FSFileState: TYPE = REF FSFileStateObject;
CreateFileState: PROC [file: ROPE, createOptions: AlpineInterimDirectory.CreateOptions, createLength: INT ← 0, trans: AlpTransaction.Handle ← transHandle] RETURNS [FileState] = {
 test for alpine files
IF Rope.Match[pattern: "[*.alpine]*", object: file, case: FALSE] THEN {
alpineFileState: AlpineFileState;
rightSquareBracket: INT ← file.Find["]", 1];
IF rightSquareBracket IN [-1..1] THEN ERROR;
IF createOptions = oldOnly THEN {
alpineFileState ← NEW[AlpineFileStateObject ←
[alpine[fileHandle: AlpDirectory.OpenFile[transHandle: trans, name: file,
access: readOnly,
lock: [read, wait],
recoveryOption: log, referencePattern: sequential].openFileID]]];
}
ELSE {
open the file and contract or extend it as neccessary
alpineFileState ← NEW[AlpineFileStateObject ←
[alpine[fileHandle: AlpDirectory.OpenFile[transHandle: trans, name: file,
access: readWrite,
lock: [read, wait],
recoveryOption: log, referencePattern: sequential].openFileID]]];
AlpFile.SetSize[handle: alpineFileState.fileHandle, size: PagesForBytes[createLength]];
};
RETURN [alpineFileState];
}
ELSE {
openFile: FS.OpenFile;
fsFileState: FSFileState;
IF createOptions = oldOnly THEN openFile ← FS.Open[file]
ELSE {
openFile ← FS.Create[ name: file, setPages: TRUE, pages: PagesForBytes[createLength]];
};
fsFileState ← NEW[FSFileStateObject ← [fs[openFile: openFile]]];
RETURN [fsFileState];
};
};
CleanupFileState: PROC [fileState: FileState] = {
WITH fileState SELECT FROM
alpineFileState: AlpineFileState => {
};
fsFileState: FSFileState => {
fsFileState.openFile.Close[];
};
ENDCASE => ERROR;
};
GetFileProperties: PROC [fileState: FileState] RETURNS [ byteLength: AlpineEnvironment.ByteCount, createTime: BasicTime.GMT, mutableAlpineProperites: LIST OF AlpineEnvironment.PropertyValuePair ← NIL, highWaterMark: AlpineEnvironment.PageCount ← 0, alpineSize: INT ← 0] = {
WITH fileState SELECT FROM
alpineFileState: AlpineFileState => {
properties: LIST OF PropertyValuePair ;
alpineSize ← alpineFileState.fileHandle.GetSize[];
properties ← alpineFileState.fileHandle.ReadProperties[];
WHILE properties # NIL DO
property: AlpineEnvironment.PropertyValuePair ← properties.first ;
properties ← properties.rest;
SELECT property.property FROM
byteLength => {
byteLength ← NARROW[property, AlpineEnvironment.PropertyValuePair.byteLength].byteLength;
mutableAlpineProperites ← CONS[[byteLength[byteLength]], mutableAlpineProperites];
};
createTime => {
createTime ← NARROW[property,
AlpineEnvironment.PropertyValuePair.createTime].createTime;
mutableAlpineProperites ← CONS[[createTime[createTime]], mutableAlpineProperites];
};
highWaterMark => {
highWaterMark ← NARROW[property, AlpineEnvironment.PropertyValuePair.highWaterMark].highWaterMark;
mutableAlpineProperites ← CONS[[highWaterMark[highWaterMark]], mutableAlpineProperites];
};
modifyAccess => {
modifyAccess: AlpineEnvironment.AccessList ← NARROW[property, AlpineEnvironment.PropertyValuePair.modifyAccess].modifyAccess;
mutableAlpineProperites ← CONS[[modifyAccess[modifyAccess]], mutableAlpineProperites];
};
readAccess => {
readAccess: AlpineEnvironment.AccessList ← NARROW[property, AlpineEnvironment.PropertyValuePair.readAccess].readAccess;
mutableAlpineProperites ← CONS[[readAccess[readAccess]], mutableAlpineProperites];
};
ENDCASE;
ENDLOOP;
};
fsFileState: FSFileState => {
[bytes: byteLength, created: createTime] ← fsFileState.openFile.GetInfo[];
};
ENDCASE => ERROR;
};
SetFileProperties: PROC [fileState: FileState, byteLength: AlpineEnvironment.ByteCount, createTime: BasicTime.GMT, alpineProperties: LIST OF AlpineEnvironment.PropertyValuePair ← NIL] = {
WITH fileState SELECT FROM
alpineFileState: AlpineFileState => {
IF fullCopy AND alpineProperties # NIL THEN alpineFileState.fileHandle.WriteProperties[alpineProperties]
ELSE alpineFileState.fileHandle.WriteProperties[LIST[[byteLength[byteLength]],
[createTime[createTime]]]];
};
fsFileState: FSFileState => {
fsFileState.openFile.SetByteCountAndCreatedTime[bytes: byteLength,
created: createTime];
};
ENDCASE => ERROR;
};
FillBuffer: PROC [fromFile: FileState, pagesToMove: CARDINAL] = {
WITH fromFile SELECT FROM
alpineFileState: AlpineFileState => TRUSTED {
alpineFileState.fileHandle.ReadPages[
pageRun: [firstPage: pagesCopied, count: pagesToMove],
pageBuffer: DESCRIPTOR [
bufferPtr, pagesToMove*wordsPerPage]];
};
fsFileState: FSFileState => {
TRUSTED BEGIN
fsFileState.openFile.Read[from: pagesCopied, nPages: pagesToMove, to: bufferPtr];
END;
};
ENDCASE => ERROR;
};
EmptyBuffer: PROC [toFile: FileState, pagesToMove: CARDINAL] = {
WITH toFile SELECT FROM
alpineFileState: AlpineFileState => {
TRUSTED BEGIN alpineFileState.fileHandle.WritePages[
pageRun: [firstPage: pagesCopied, count: pagesToMove],
pageBuffer: DESCRIPTOR [
bufferPtr, pagesToMove*wordsPerPage]]; END;
};
fsFileState: FSFileState => {
TRUSTED BEGIN
fsFileState.openFile.Write[to: pagesCopied, nPages: pagesToMove, from: bufferPtr];
END;
};
ENDCASE => ERROR;
};
moveTheBits: PROC [] RETURNS [aborted: BOOLFALSE]= {
pagesCopied ← 0;
UNTIL pagesCopied = pageCount DO
pagesLeft: CARDINAL ← pageCount-pagesCopied;
pagesToMove: CARDINALMIN [bufferLen, pagesLeft];
IF d.stopFlag THEN {
WITH toFile SELECT FROM
alpineFileState: AlpineFileState => {
[] ← transHandle.Finish[requestedOutcome: abort, continue: FALSE] ;
IF secondTransHandle # NIL THEN
[] ← secondTransHandle.Finish[requestedOutcome: abort, continue: FALSE] ;
};
fsFileState: FSFileState => {
fullF: ROPE;
created: BasicTime.GMT ;
[fullFName: fullF] ← fsFileState.openFile.GetName[];
[created: created] ← fsFileState.openFile.GetInfo[];
fsFileState.openFile.Close[];
FS.Delete[name: fullF, wantedCreatedTime: created];
};
ENDCASE => ERROR;
RETURN[TRUE];
};
FillBuffer[fromFile, pagesToMove];
EmptyBuffer[toFile, pagesToMove];
pagesCopied ← pagesCopied + pagesToMove;
ENDLOOP;
};
processCopy: PROC [sourceName: ROPE] RETURNS [result: ROPENIL] = {
destFileName: ROPE ← fullNDest;
sourceIsASegment: BOOLFALSE;
fullSourceFName: ROPE;
sourceFcp: FS.ComponentPositions;
parseError: BOOLFALSE;
errorExplanation: ROPE;
IF transHandle # NIL AND d.assertWheel THEN transHandle.AssertAlpineWheel[TRUE];
[fullFName: fullSourceFName, cp: sourceFcp] ← FS.ExpandName[name: sourceName, wDir: "[]<>"! FS.Error => {
parseError ← TRUE;
errorExplanation ← error.explanation;
CONTINUE ;
};
];
IF parseError THEN {
result ← IO.PutFR["Bad file name %g could NOT be copied because %g", IO.rope[to], IO.rope[errorExplanation]];
RETURN;
};
IF toDir THEN {
destFileName ← Rope.Concat[fullNDest, Rope.Substr[fullSourceFName, sourceFcp.base.start, sourceFcp.base.length]];
IF sourceFcp.ext.length # 0 THEN destFileName ← destFileName.Cat[".", Rope.Substr[fullSourceFName, sourceFcp.ext.start, sourceFcp.ext.length]];
};
[byteLength, createTime, alpineProperties, highWaterMark, alpineSize] ← GetFileProperties[fromFile];
IF byteLength < 0 AND (~alpineSourceAndResult OR ~fullCopy) THEN RETURN["The source file has negative length. Copy aborted."];
sourceIsASegment ← Rope.Equal["segment", Rope.Substr[fullSourceFName, sourceFcp.ext.start, sourceFcp.ext.length], FALSE];
SELECT TRUE FROM
alpineSourceAndResult AND fullCopy => createLength ← MAX[byteLength, BytesForPages[highWaterMark], BytesForPages[alpineSize]];
alpineSource AND ~alpineSourceAndResult AND sourceIsASegment => {
IF byteLength = 0 THEN byteLength ← BytesForPages[alpineSize];
createLength ← byteLength;
};
ENDCASE => createLength ← byteLength;
IF Rope.IsEmpty[toServer] OR alpineResult THEN {
IFSResult ← FALSE;
IF alpineSourceAndResult AND ~Rope.Equal[ s1: fromServer, s2: toServer, case: FALSE] THEN {
twoDifferentAlpine ← TRUE;
secondInst ← AlpInstance.Create[fileStore: toServer, caller: caller, key: key];
secondTransHandle ← AlpTransaction.Create[secondInst];
IF d.assertWheel THEN secondTransHandle.AssertAlpineWheel[TRUE];
toFile ← CreateFileState[file: destFileName, createOptions: none, createLength: createLength,
trans: secondTransHandle];
}
ELSE toFile ← CreateFileState[file: destFileName, createOptions: none, createLength: createLength];
}
ELSE {
IFSResult ← TRUE;
toFile ← CreateFileState[file: "///temp/qqqscratch.yodel", createOptions: none,
createLength: createLength];
};
pageCount ← PagesForBytes[createLength];
TRUSTED {
bufferInterval ← VM.Allocate[count: bufferLen];
bufferAllocated ← TRUE ;
bufferPtr ← VM.AddressForPageNumber[bufferInterval.page];
};
IF moveTheBits[] THEN RETURN["Copy aborted "];
SetFileProperties[toFile, byteLength, createTime, alpineProperties];
CleanupFileState[fromFile];
CleanupFileState[toFile];
IF twoDifferentAlpine THEN {
IF (secondTransHandle.Finish[requestedOutcome: commit, continue: FALSE] # commit)
THEN RETURN["Transaction on destination server failed to commit - validity of copy is in question"];
};
TRUSTED {
VM.Free[bufferInterval];
bufferAllocated ← FALSE ;
};
IF IFSResult THEN {
[] ← FS.Copy[from: "///temp/qqqscratch.yodel", to: destFileName ];
FS.Delete[name: "///temp/qqqscratch.yodel"];
};
IF transHandle # NIL AND
(transHandle.Finish[requestedOutcome: commit, continue: FALSE] # commit)
THEN RETURN["Transaction on source server failed to commit - validity of copy is in question"];
result ← IO.PutFR["%g written", IO.rope[destFileName]];
};
transHandle: AlpTransaction.Handle ← trans;
secondInst: AlpInstance.Handle ← NIL;
secondTransHandle: AlpTransaction.Handle ← NIL;
toFile, fromFile: FileState;
byteLength: AlpineEnvironment.ByteCount;
createTime: BasicTime.GMT;
bufferLen: CARDINAL = 12;
pageCount: PageCount ;
bufferInterval: VM.Interval ← TRASH;
bufferAllocated: BOOLFALSE ;
bufferPtr: LONG POINTER;
pagesCopied: INT;
cp: FS.ComponentPositions;
toServer, fromServer: ROPE ;
fullNSource, fullNDest: ROPE ;
IFSResult: BOOL ;
alpineSource: BOOL ;
alpineResult: BOOL ;
alpineSourceAndResult: BOOL;
twoDifferentAlpine: BOOLFALSE;
alpineProperties: LIST OF AlpineEnvironment.PropertyValuePair ← NIL;
highWaterMark: AlpineEnvironment.PageCount ← 0;
createLength: AlpineEnvironment.ByteCount;
alpineSize: INT;
toDir: BOOL;
fromWithVersion: ROPE ← from;
parseOfToError: BOOLFALSE;
body of procedure fileCopy
{
ENABLE UNWIND => {
IF transHandle # NIL THEN [] ← AlpTransaction.Finish[transHandle, abort
! RPC.CallFailed => CONTINUE];
IF secondTransHandle # NIL THEN [] ← AlpTransaction.Finish[secondTransHandle, abort
! RPC.CallFailed => CONTINUE];
IF bufferAllocated THEN TRUSTED {VM.Free[bufferInterval]};
bufferAllocated ← FALSE;
IF fromFile # NIL THEN CleanupFileState[fromFile ! FS.Error => CONTINUE];
IF toFile # NIL THEN CleanupFileState[toFile ! FS.Error => CONTINUE];
};
IF Rope.Find[from,"!"] < 0 THEN fromWithVersion ← from.Concat["!H"];
[fullFName: fullNSource, cp: cp] ← FS.ExpandName[name: fromWithVersion];
fromServer ← Rope.Substr[fullNSource, cp.server.start, cp.server.length];
alpineSource ← Rope.Match[pattern: "*.alpine", object: fromServer, case: FALSE] ;
[fullFName: fullNDest, cp: cp] ← FS.ExpandName[name: to ! FS.Error => {
parseOfToError ← TRUE;
CONTINUE ;
};
];
IF parseOfToError THEN {
[fullFName: fullNDest, cp: cp] ← FS.ExpandName[name: to.Concat["*"]];
fullNDest ← fullNDest.Substr[0, fullNDest.Length[]-1];
};
toServer ← Rope.Substr[fullNDest, cp.server.start, cp.server.length];
alpineResult ← Rope.Match[pattern: "*.alpine", object: toServer, case: FALSE] ;
alpineSourceAndResult ← alpineResult AND alpineSource ;
toDir ← isDirectory[fullNDest];
IF alpineSource THEN {
previousFile: ROPENIL;
DO
file: AE.UniversalFile;
fullPathName, link: ROPE;
oneResult: ROPE;
IF d.stopFlag THEN {
oneResult ← "Copy Aborted";
copyResult ← CONS[first: oneResult, rest: copyResult];
EXIT;
};
IF transHandle = NIL THEN {
inst: AlpInstance.Handle ← NIL;
inst ← AlpInstance.Create[fileStore: serverForTrans, caller: caller, key: key];
transHandle ← AlpTransaction.Create[inst];
};
IF d.assertWheel THEN transHandle.AssertAlpineWheel[TRUE];
[file, fullPathName, link] ← AlpDirectory.Enumerate[ transHandle: transHandle, pattern: fullNSource, previousFile: previousFile];
IF fullPathName.IsEmpty[] THEN {
message: ROPE = "No files match the source pattern - No files copied";
IF copyResult = NIL THEN copyResult ← CONS[first: message, rest: NIL];
[] ← transHandle.Finish[requestedOutcome: abort, continue: FALSE !
RPC.CallFailed => CONTINUE;
];
EXIT;
};
fromFile ← CreateFileState[file: fullPathName, createOptions: oldOnly];
oneResult ← processCopy[fullPathName];
copyResult ← CONS[first: oneResult, rest: copyResult];
transHandle ← NIL;
previousFile ← fullPathName;
ENDLOOP;
}
ELSE {
copyEnum: FS.NameProc = {
oneResult: ROPE;
continue ← TRUE;
IF alpineResult AND transHandle = NIL THEN {
inst: AlpInstance.Handle ← NIL;
inst ← AlpInstance.Create[fileStore: serverForTrans, caller: caller, key: key];
transHandle ← AlpTransaction.Create[inst];
};
IF alpineResult AND d.assertWheel THEN transHandle.AssertAlpineWheel[TRUE];
fromFile ← CreateFileState[file: fullFName, createOptions: oldOnly];
oneResult ← processCopy[fullFName];
copyResult ← CONS[first: oneResult, rest: copyResult];
transHandle ← NIL;
IF d.stopFlag THEN {
oneResult ← "Copy Aborted";
copyResult ← CONS[first: oneResult, rest: copyResult];
continue ← FALSE;
};
};
FS.EnumerateForNames[pattern: fullNSource, proc: copyEnum];
};
IF copyResult # NIL THEN copyResult ← List.Reverse[copyResult];
};
};
CopyFilesProc: PUBLIC Buttons.ButtonProc = {
InnerCopyFilesProc[FALSE, parent, clientData, mouseButton, shift, control];
};
FullCopyFilesProc: PUBLIC Buttons.ButtonProc = {
InnerCopyFilesProc[TRUE, parent, clientData, mouseButton, shift, control];
};
InnerCopyFilesProc: PROC [fullCopy: BOOL, parent: REF ANY, clientData: REF ANYNIL, mouseButton: Menus.MouseButton ← red, shift, control: BOOLFALSE] = {
d: MyData = NARROW[clientData];
resultList: LIST OF REF ANYNIL;
server, srcServer, srcFile, srcDir, destServer, destFile, destDir: ROPE;
user, password: ROPE;
directory, sRestOfPattern, dRestOfPattern: ROPE;
toFile, fromFile: ROPE;
parseError: BOOL;
errorExplanation: ROPE;
callCopy: YodelData.PerformProc = {
RETURN[fileCopy[trans: trans, to: toFile, from: fromFile,
fullCopy: fullCopy, user: user, password: password, caller: caller, key: key, serverForTrans: serverForTrans, d: d ]];
};
typeParseError: PROC [sOrD: ROPE] = {
d.out.PutF["\nBad pattern in %g name for Copy because %g\n", IO.rope[sOrD], IO.rope[errorExplanation]];
};
d.stopFlag ← FALSE;
IF d.background THEN CedarProcess.SetPriority[background];
[user, password, srcServer, srcDir, srcFile, parseError, errorExplanation] ← ParseSArgs[d];
IF parseError THEN { typeParseError["source"]; GOTO badParse;};
[destServer, destDir, destFile, parseError, errorExplanation] ← ParseDArgs[user, d];
IF parseError THEN { typeParseError["destination"]; GOTO badParse;};
[directory, sRestOfPattern] ← DecomposePattern[server: srcServer, pattern: srcFile, user: user];
IF (Rope.Equal[srcServer,""] OR Rope.Equal[srcServer,NIL]) THEN {
IF srcFile.InlineLength[]>0 AND srcFile.InlineFetch[0] = '<
THEN fromFile ← Rope.Concat["[]", srcFile]
ELSE fromFile ← srcFile;
}
ELSE {
fromFile ← Rope.Concat["[",Rope.Concat[srcServer,Rope.Concat["]", Rope.Concat[directory,sRestOfPattern]]]];
};
[directory, dRestOfPattern] ← DecomposePattern[server: destServer, pattern: destFile, user: user, addStar: FALSE];
IF (Rope.Equal[destServer,""] OR Rope.Equal[destServer,NIL]) THEN {
IF destFile.InlineLength[]>0 AND destFile.InlineFetch[0] = '<
THEN toFile ← Rope.Concat["[]", destFile]
ELSE toFile ← destFile;
}
ELSE {
toFile ← Rope.Concat["[",Rope.Concat[destServer,Rope.Concat["]", Rope.Concat[directory,dRestOfPattern]]]];
};
IF hasPattern[destFile] THEN {
d.out.PutF["\nCopy cannot have a pattern in the destination - but a directory is OK\n"];
}
ELSE {
IF hasPattern[sRestOfPattern] AND ~isDirectory[destFile] THEN {
d.out.PutF["\nCopy of a file name pattern only works to a directory\n"];
}
ELSE {
d.out.PutF["\nCopy of %g to %g\n", IO.rope[fromFile], IO.rope[toFile]];
IF Rope.Match[pattern: "*.alpine", object: srcServer, case: FALSE]
THEN server ← srcServer
ELSE server ← destServer ;
resultList ← PerformOp[performProc: callCopy, server: server, user: user, password: password];
IF ~PrintResult[d.out, resultList] THEN d.out.PutF[" Copy seems OK.\n"];
};
};
EXITS
badParse => {};
};
fileRename: PROC [trans: AlpTransaction.Handle, to: ROPE, from: ROPE, user: ROPENIL, password: ROPENIL, caller: AlpineEnvironment.Principal, key: RPC.EncryptionKey, d: MyData] RETURNS [resultList: LIST OF REF ANYNIL] = {
resultRope: ROPENIL;
outcome: AlpTransaction.Outcome;
IF trans # NIL THEN {
 alpine files to rename
oldName, newName: Rope.ROPE;
IF d.assertWheel THEN trans.AssertAlpineWheel[TRUE];
[oldName, newName] ← AlpDirectory.Rename[old: from, new: to, transHandle: trans];
outcome ← AlpTransaction.Finish[trans, commit];
IF outcome # commit THEN {
resultList ← CONS[
first: Rope.Concat["Alpine transaction aborted -- rename NOT done",
IF resultList = NIL THEN NIL ELSE "; log before abort is:"],
rest: resultList];
};
}
ELSE {
 FS or IFS files to rename
FS.Rename[from: from, to: to];
};
};
RenameProc: PUBLIC Buttons.ButtonProc = {
d: MyData = NARROW[clientData];
resultList: LIST OF REF ANYNIL;
result: ROPE;
srcServer, srcFile, srcDir, destServer, destFile, destDir: ROPE;
user, password: ROPE;
directory, sRestOfPattern, dRestOfPattern: ROPE;
toFile, fromFile: ROPE;
parseError: BOOL;
errorExplanation: ROPE;
callRename: YodelData.PerformProc = {
RETURN[fileRename[trans: trans, to: toFile, from: fromFile,
user: user, password: password, caller: caller, key: key, d: d ]];
};
typeParseError: PROC [sOrD: ROPE] = {
d.out.PutF["\nBad pattern in %g name for Rename because %g\n", IO.rope[sOrD], IO.rope[errorExplanation]];
};
d.stopFlag ← FALSE;
[user, password, srcServer, srcDir, srcFile, parseError, errorExplanation] ← ParseSArgs[d];
IF parseError THEN { typeParseError["source"]; GOTO badParse;};
[destServer, destDir, destFile, parseError, errorExplanation] ← ParseDArgs[user, d];
IF parseError THEN { typeParseError["destination"]; GOTO badParse;};
IF ~Rope.Equal[destServer,srcServer, FALSE] THEN {
d.out.PutF["\nFor Rename, source (%g) and destination (%g) servers must be the same\n", IO.rope[srcServer], IO.rope[destServer]];
GOTO badParse;
};
[directory, sRestOfPattern] ← DecomposePattern[server: srcServer, pattern: srcFile, user: user];
IF (Rope.Equal[srcServer,""] OR Rope.Equal[srcServer,NIL]) THEN {
IF srcFile.InlineLength[]>0 AND srcFile.InlineFetch[0] = '<
THEN fromFile ← Rope.Concat["[]", srcFile]
ELSE fromFile ← srcFile;
}
ELSE {
fromFile ← Rope.Concat["[",Rope.Concat[srcServer,Rope.Concat["]", Rope.Concat[directory,sRestOfPattern]]]];
};
[directory, dRestOfPattern] ← DecomposePattern[server: destServer, pattern: destFile, user: user];
IF (Rope.Equal[destServer,""] OR Rope.Equal[destServer,NIL]) THEN {
IF destFile.InlineLength[]>0 AND destFile.InlineFetch[0] = '<
THEN toFile ← Rope.Concat["[]", destFile]
ELSE toFile ← destFile;
}
ELSE {
toFile ← Rope.Concat["[",Rope.Concat[destServer,Rope.Concat["]", Rope.Concat[directory,dRestOfPattern]]]];
};
IF hasPattern[sRestOfPattern] OR hasPattern[dRestOfPattern] THEN {
d.out.PutF["\nRename only works on a single file to a single file\n"];
}
ELSE {
d.out.PutF["\nRename of %g to %g\n", IO.rope[fromFile], IO.rope[toFile]];
resultList ← PerformOp[performProc: callRename, server: srcServer, user: user, password: password];
IF resultList = NIL THEN {
d.out.PutF[" Rename seems OK.\n"];
}
ELSE {
result ← NARROW[IF resultList = NIL THEN NIL ELSE resultList.first];
d.out.PutF[" %g\n", IO.rope[result]];
};
};
EXITS
badParse => {};
};
ConvertDirectoryProc: PUBLIC Buttons.ButtonProc = {
d: MyData = NARROW[clientData];
resultList: LIST OF REF ANYNIL;
srcServer, srcFile, srcDir : ROPE;
user, password: ROPE;
directory, sRestOfPattern: ROPE;
parseError: BOOL;
errorExplanation: ROPE;
callConvert: YodelData.PerformProc = {
RETURN[Convert[trans: trans, user: user, password: password, directoryName: directory, key: key, serverForTrans: serverForTrans, d: d ]];
};
typeParseError: PROC = {
d.out.PutF["\nBad pattern in source name for Convert because %g\n", IO.rope[errorExplanation]];
};
[user, password, srcServer, srcDir, srcFile, parseError, errorExplanation] ← ParseSArgs[d];
IF parseError THEN { typeParseError[]; GOTO badParse;};
[directory, sRestOfPattern] ← DecomposePattern[server: srcServer, pattern: srcFile, user: user];
IF Rope.Compare[sRestOfPattern, "*"] # equal THEN {
d.out.PutF["\nCan only convert a directory"];
}
ELSE {
IF NOT Rope.Match[pattern: "*.alpine", object: srcServer, case: FALSE] THEN {
d.out.PutF["\nCan only convert a directory on an Alpine Server"];
}
ELSE {
resultList ← PerformOp[performProc: callConvert, server: srcServer, user: user, password: password];
IF ~PrintResult[d.out, resultList] THEN d.out.PutF[" Convert seems OK.\n"];
};
};
EXITS
badParse => {};
};
Convert: PROC [trans: AlpTransaction.Handle, user: ROPENIL, password: ROPENIL, directoryName: ROPENIL, key: RPC.EncryptionKey, serverForTrans: ROPE, d: MyData] RETURNS [convertResult: LIST OF REF ANYNIL] ~ {
basicpath: Rope.ROPE ← Rope.Cat["[", serverForTrans, "]", directoryName];
administratorPath: Rope.ROPE ← Rope.Cat["[", serverForTrans, "]<InterimDirectoryOwner>"];
rootFileProperty: AlpineEnvironment.OwnerPropertySet;
owner: ROPE;
FileItem : TYPE = RECORD [
name: ROPE,
file: AlpineEnvironment.UniversalFile
];
itemList : LIST OF FileItem ← NIL;
InterimEnumProc: PROC [ fileName: REF TEXT, universalFile: AlpineEnvironment.UniversalFile] RETURNS [quit: BOOLFALSE] = {
name : ROPE ← Rope.FromRefText[fileName];
itemList ← CONS[ first: [name: name, file: universalFile], rest: itemList ];
};
interimDirectoryFile, oldDirectoryFile: AlpineEnvironment.UniversalFile;
writeRootFileProperty: AlpineEnvironment.OwnerPropertyValuePair.rootFile;
rootFileProperty[rootFile] ← TRUE;
IF d.assertWheel THEN trans.AssertAlpineWheel[TRUE]
ELSE {
msg: ROPE ← "You must be an Alpine Wheel to use Convert";
convertResult ← CONS[ msg, convertResult];
RETURN;
};
Copy the interimDirectory to a local data structure;
AlpineInterimDirectory.EnumerateDirectoryUnderTrans[
transHandle: trans,
directoryName: basicpath,
enumProc: InterimEnumProc];
IF itemList = NIL THEN {
convertResult ← CONS[ Rope.Cat[ basicpath, " -- no files to move"], convertResult];
RETURN;
};
See if there's already a valid AlpineDirectory file.
{
continue : BOOLFALSE;
[] ← AlpDirectory.Lookup[ transHandle: trans, fileName: Rope.Cat[basicpath, "$$$.btree"] ! AlpDirectory.Error => IF type = damagedDirectory THEN {continue ← TRUE; CONTINUE} ];
IF NOT continue THEN {
convertResult ← CONS[ Rope.Cat[ basicpath, " -- directory already exists"], convertResult];
RETURN;
};
};
Remember the file containing the interim directory.
owner ← Rope.Substr[directoryName, 1, Rope.Length[directoryName]-2];
interimDirectoryFile ← NARROW[trans.ReadOwnerProperties[ volumeGroupID: trans.GetNextVolumeGroup[previousGroup: AlpineEnvironment.nullVolumeGroupID, lock: [none, wait]], owner: owner, desiredProperties: rootFileProperty ].first, AlpineEnvironment.OwnerPropertyValuePair.rootFile].rootFile;
Nullify the rootFile property so AlpDirectory will create a new file for the directory.
writeRootFileProperty.rootFile ← AlpineEnvironment.nullUniversalFile;
trans.WriteOwnerProperties[ volumeGroupID: trans.GetNextVolumeGroup[previousGroup: AlpineEnvironment.nullVolumeGroupID, lock: [none, wait]], owner: owner, properties: LIST[writeRootFileProperty] ];
Create the Alpine Directory file and copy info from itemList into it.
FOR localList: LIST OF FileItem ← itemList, localList.rest UNTIL localList = NIL DO
item: FileItem ← localList.first;
[] ← AlpDirectory.Insert[ transHandle: trans, fileName: Rope.Cat[basicpath, item.name], file: item.file ];
ENDLOOP;
Make an entry for the old directory file in the new directory
[oldDirectoryFile, ] ← AlpDirectory.Insert[ transHandle: trans, fileName: Rope.Cat[administratorPath, owner, ".InterimDirectoryFile"], file: interimDirectoryFile ];
IF oldDirectoryFile # AlpineEnvironment.nullUniversalFile THEN { -- if there was already a file by that name something's wrong
[] ← AlpTransaction.Finish[ trans, abort, FALSE ];
convertResult ← LIST[Rope.Cat[basicpath, " -- Naming problems detected, convert NOT done" ]];
RETURN;
};
convertResult ← LIST[Rope.Cat[basicpath, " converted" ]];
};
END.
Bob Hagmann June 11, 1985 9:11:52 am PDT
reformatted
Bob Hagmann June 12, 1985 8:10:17 am PDT
added Rename, split from YodelUserImpl
Bob Hagmann July 8, 1985 11:09:18 am PDT
changes to: changes for copy multiple files
Bob Hagmann October 10, 1985 5:26:16 pm PDT
changes to: processCopy
Bob Hagmann October 11, 1985 7:58:30 am PDT
changes to: ParseDArgs
Bob Hagmann October 11, 1985 11:07:15 am PDT
changes to: processCopy
Jack Kent, August 18, 1986 10:53:22 am PDT
changes to: CreateFileState (local of fileCopy)
Jack Kent, August 18, 1986 4:42:42 pm PDT
changes to: CreateFileState (local of fileCopy), DIRECTORY
Jack Kent, August 19, 1986 12:01:09 pm PDT
changes to: CreateFileState (local of fileCopy)
Jack Kent, September 4, 1986 7:22:57 pm PDT
changes to: CreateFileState (local of fileCopy)