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 ],
FS USING[ 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],
VM USING[ AddressForPageNumber, Allocate, Free, Interval],
YodelData;
YodelUserImpl2: CEDAR PROGRAM
IMPORTS AlpDirectory, AlpFile, AlpInstance, AlpTransaction, AlpineInterimDirectory, CedarProcess, FS, IO, List, Rope, RPC, ViewerTools, VM, 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:
ROPE ←
NIL, parseError:
BOOL ←
FALSE, errorExplanation:
ROPE ←
NIL] = {
destFileTemp: ROPE ;
fullFName: ROPE;
cp: FS.ComponentPositions;
dirOmitted: BOOL;
trimStar: BOOL ← FALSE;
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];
};
PrintResult:
PUBLIC
PROC [out:
IO.
STREAM, resultList:
LIST
OF
REF
ANY]
RETURNS [printedSomething:
BOOL ←
FALSE] = {
DO
nowRope: ROPE ← NARROW[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:
ROPE ←
NIL, password:
ROPE ←
NIL, expunging:
BOOLEAN ←
FALSE, excessBytes: AlpineEnvironment.ByteCount ← 0, caller: AlpineEnvironment.Principal, key:
RPC.EncryptionKey, serverForTrans:
ROPE, d: MyData]
RETURNS [copyResult:
LIST
OF
REF
ANY ←
NIL] = {
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:
BOOL ←
FALSE]= {
pagesCopied ← 0;
UNTIL pagesCopied = pageCount
DO
pagesLeft: CARDINAL ← pageCount-pagesCopied;
pagesToMove: CARDINAL ← MIN [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:
ROPE ←
NIL] = {
destFileName: ROPE ← fullNDest;
sourceIsASegment: BOOL ← FALSE;
fullSourceFName: ROPE;
sourceFcp: FS.ComponentPositions;
parseError: BOOL ← FALSE;
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: BOOL ← FALSE ;
bufferPtr: LONG POINTER;
pagesCopied: INT;
cp: FS.ComponentPositions;
toServer, fromServer: ROPE ;
fullNSource, fullNDest: ROPE ;
IFSResult: BOOL ;
alpineSource: BOOL ;
alpineResult: BOOL ;
alpineSourceAndResult: BOOL;
twoDifferentAlpine: BOOL ← FALSE;
alpineProperties: LIST OF AlpineEnvironment.PropertyValuePair ← NIL;
highWaterMark: AlpineEnvironment.PageCount ← 0;
createLength: AlpineEnvironment.ByteCount;
alpineSize: INT;
toDir: BOOL;
fromWithVersion: ROPE ← from;
parseOfToError: BOOL ← FALSE;
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: ROPE ← NIL;
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
ANY ←
NIL, mouseButton: Menus.MouseButton ← red, shift, control:
BOOL ←
FALSE] = {
d: MyData = NARROW[clientData];
resultList: LIST OF REF ANY ← NIL;
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"];
};
};
};
fileRename:
PROC [trans: AlpTransaction.Handle, to:
ROPE, from:
ROPE, user:
ROPE ←
NIL, password:
ROPE ←
NIL, caller: AlpineEnvironment.Principal, key:
RPC.EncryptionKey, d: MyData]
RETURNS [resultList:
LIST
OF
REF
ANY ←
NIL] = {
resultRope: ROPE ← NIL;
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 ANY ← NIL;
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]];
};
};
};
ConvertDirectoryProc:
PUBLIC Buttons.ButtonProc = {
d: MyData = NARROW[clientData];
resultList: LIST OF REF ANY ← NIL;
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"];
};
};
};
Convert:
PROC [trans: AlpTransaction.Handle, user:
ROPE ←
NIL, password:
ROPE ←
NIL, directoryName:
ROPE ←
NIL, key:
RPC.EncryptionKey, serverForTrans:
ROPE, d: MyData]
RETURNS [convertResult:
LIST
OF
REF
ANY ←
NIL] ~ {
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:
BOOL ←
FALSE] = {
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 : BOOL ← FALSE;
[] ← 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)