YodelUserImpl1.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Yodel: User commands
Last Edited by:
Carl Hauser, April 17, 1986 10:01:23 am PST
Hauser, April 19, 1985 4:45:25 pm PST
Bob Hagmann January 6, 1986 4:48:38 pm PST
DIRECTORY
AlpFile USING[ FileID, Handle, LockOption, Open, ReadProperties],
AlpineEnvironment USING[ AccessList, ByteCount, NeededAccess, Outcome, OwnerName, PageCount, Principal, Property, PropertyValuePair, RName, UniversalFile, nullUniversalFile ],
AlpineFile USING [ allProperties, PropertySet],
AlpDirectory,
AlpineDirectory,
AlpineInterimDirectory,
AlpInstance USING[ AccessFailed, Create, Handle, LockFailed, Unknown ],
AlpTransaction USING[ AssertAlpineWheel, Create, Finish, Handle, Outcome],
BasicTime USING[ GMT],
Basics USING[ Comparison],
Buttons USING[ Button, ButtonProc, ReLabel, SetDisplayStyle ],
CedarProcess USING[ SetPriority ],
Convert,
FSUSING[ ComponentPositions, Delete, EnumerateForInfo, EnumerateForNames, Error, ExpandName, InfoProc, NameProc],
IOUSING[ int, PutF, PutFR, PutRope, rope, STREAM, text, time ],
List USING[ Reverse, Sort ],
Menus,
RefText,
Rope,
RPC,
UserCredentials USING[ Get ],
ViewerClasses USING[ Viewer ],
ViewerTools USING[ GetContents, SetContents],
YodelData;
YodelUserImpl1: CEDAR MONITOR LOCKS d USING d: MyData
IMPORTS AlpDirectory, AlpineInterimDirectory, AlpFile, AlpInstance, AlpTransaction, Buttons, CedarProcess, Convert, FS, IO, List, RefText, Rope, UserCredentials, ViewerTools, YodelData
EXPORTS YodelData
=
BEGIN OPEN AE: AlpineEnvironment, YodelData;
PageCount: TYPE = AE.PageCount;
PropertyValuePair: TYPE = AE.PropertyValuePair;
UniversalFile: TYPE = AE.UniversalFile;
EnumProc: TYPE = PROC [ fileName: ROPE, universalFile: AE.UniversalFile, trans: AlpTransaction.Handle, flushState: BOOL ] RETURNS [ didCommitAndContinue: BOOL, quit: BOOL, nowTrans: AlpTransaction.Handle];
EnumeratePattern: PROC [ transHandle: AlpTransaction.Handle, commitALot: BOOL, transServer: ROPE, caller: AlpineEnvironment.Principal, key: RPC.EncryptionKey, assertWheel: BOOL, pattern: ROPE, enumProc: EnumProc] RETURNS [myTrans: AlpTransaction.Handle ← NIL]= {
lastCommitedFullPathName: ROPENIL;
firstErrorThisXAct: BOOLFALSE;
doFlush: BOOLTRUE;
file: AE.UniversalFile;
fullPathName, link: ROPE;
tryRecovery: PROC RETURNS [doRetry: BOOLFALSE] = {
IF ~firstErrorThisXAct THEN {
inst: AlpInstance.Handle ← NIL;
firstErrorThisXAct ← TRUE;
inst ← AlpInstance.Create[fileStore: transServer, caller: caller, key: key];
myTrans ← AlpTransaction.Create[inst];
IF assertWheel THEN myTrans.AssertAlpineWheel[TRUE];
fullPathName ← lastCommitedFullPathName;
doFlush ← TRUE;
doRetry ← TRUE;
};
};
myTrans ← transHandle;
{
ENABLE BEGIN
AlpInstance.Unknown =>{
IF tryRecovery[] THEN RETRY; -- should retry the whole block
};
AlpDirectory.Error => {
IF type # transAborted THEN REJECT;
IF tryRecovery[] THEN RETRY; -- should retry the whole block
};
END;
{
DO
didCC: BOOL;
pathName: ROPE;
quit: BOOL;
[file, fullPathName, link] ← AlpDirectory.Enumerate[ transHandle: myTrans, pattern: pattern, previousFile: fullPathName];
IF fullPathName # NIL THEN {
IF Rope.Find[fullPathName, "$$$.btree"] > 0 THEN LOOP;
};
[quit: quit, didCommitAndContinue: didCC, nowTrans: myTrans] ← enumProc[ fullPathName, file, myTrans, doFlush ];
IF quit THEN EXIT;
doFlush ← FALSE;
IF didCC THEN {
firstErrorThisXAct ← FALSE;
lastCommitedFullPathName ← fullPathName;
};
pathName ← fullPathName;
IF pathName.IsEmpty[] THEN EXIT;
ENDLOOP;
};
};
};
ROPE: TYPE = Rope.ROPE;
CompareProc: SAFE PROC [ref1, ref2: REF ANY]
RETURNS [Basics.Comparison] = CHECKED {
rope1: ROPE = NARROW[ref1];
rope2: ROPE = NARROW[ref2];
IF ~rope1.IsEmpty[] AND ~rope2.IsEmpty[] THEN {
IF rope1.InlineFetch[0] = '- THEN {
IF ~rope2.InlineFetch[0] = '- THEN RETURN[greater];
}
ELSE IF rope2.InlineFetch[0] = '- THEN RETURN[less];
};
RETURN [Rope.Compare[rope1, rope2, FALSE]];
};
ParseSArgs: PUBLIC PROC [ d: MyData] RETURNS [user, password, srcServer, srcDir, srcFile: ROPENIL, parseError: BOOLFALSE, errorExplanation: ROPENIL] = {
srcFileTemp: ROPE ;
fullFName: ROPE;
cp: FS.ComponentPositions;
dirOmitted: BOOL;
srcFileTemp ← ViewerTools.GetContents[d.src];
[fullFName: fullFName, cp: cp, dirOmitted: dirOmitted] ← FS.ExpandName[name: srcFileTemp, wDir: "[]<>" ! FS.Error => {
parseError ← TRUE;
errorExplanation ← error.explanation;
CONTINUE ;
};
];
IF parseError THEN {
[fullFName: fullFName, cp: cp, dirOmitted: dirOmitted] ← FS.ExpandName[name: (srcFileTemp ← srcFileTemp.Concat["*"]), wDir: "[]<>" ! FS.Error => {
GOTO FSError ;
};
];
};
parseError ← FALSE;
[user, password] ← UserCredentials.Get[];
srcServer ← fullFName.Substr[cp.server.start, cp.server.length];
IF dirOmitted THEN srcFile ← ""
ELSE srcFile ← fullFName.Substr[cp.dir.start-1];
[srcDir] ← DecomposePattern[server: srcServer, pattern: srcFile, user: user];
EXITS
FSError => {};
};
listFiles: PROC [trans: AlpTransaction.Handle, server: ROPE, pattern: ROPE, user: ROPENIL, password: ROPENIL , caller: AlpineEnvironment.Principal, key: RPC.EncryptionKey, displayProperties: AlpineFile.PropertySet ← AlpineFile.allProperties, d: MyData] RETURNS [LIST OF REF ANY] = {
directory, restOfPattern: ROPE;
resultList: LIST OF REF ANYNIL;
noFiles: INT ← 0;
noBytes: INT ← 0 ;
AlpineListEnumProc: EnumProc = {
PROC [ fileName: ROPE, universalFile: AE.UniversalFile, trans: AlpTransaction.Handle, flushState: BOOL ] RETURNS [ didCommitAndContinue: BOOL, quit: BOOL, nowTrans: AlpTransaction.Handle ];
intendReadLockOption: AlpFile.LockOption = [intendRead , IF d.breakLocks THEN wait ELSE fail];
readLockOption: AlpFile.LockOption = [read, IF d.breakLocks THEN wait ELSE fail];
IF fileName.IsEmpty[] THEN RETURN;
nowTrans ← trans ;
IF d.stopFlag THEN {
resultList ← CONS[first: NARROW[" ** List aborted **", ROPE], rest: NIL];
noFiles ← 0;
RETURN [quit: TRUE, didCommitAndContinue: TRUE, nowTrans: nowTrans];
};
quit ← FALSE;
{
file: AlpFile.Handle;
fileID: AlpFile.FileID;
properties: LIST OF AlpineEnvironment.PropertyValuePair;
byteLength: INT;
resultRope: ROPE ;
resultRope ← IO.PutFR["%g\t", IO.rope[fileName]];
IF universalFile # AE.nullUniversalFile THEN {
[file, fileID] ← AlpFile.Open[transHandle: nowTrans, universalFile: universalFile, lock: intendReadLockOption
! AlpInstance.LockFailed => GOTO lockError;
AlpInstance.AccessFailed =>
IF missingAccess = AlpineEnvironment.NeededAccess.fileRead
THEN GOTO skipFile
ELSE REJECT;
];
properties ← AlpFile.ReadProperties[handle: file, lock: readLockOption
! AlpInstance.LockFailed => GOTO lockError;
AlpInstance.AccessFailed =>
IF missingAccess = AlpineEnvironment.NeededAccess.fileRead THEN GOTO skipFile
ELSE REJECT;
];
byteLength ← NARROW[properties.first,
AlpineEnvironment.PropertyValuePair.byteLength].byteLength;
loop through properties and print those enabled
UNTIL properties = NIL DO
property: AlpineEnvironment.PropertyValuePair ← properties.first ;
properties ← properties.rest;
IF displayProperties[property.property] THEN {
SELECT property.property FROM
 byteLength
byteLength => {
byteLength: INTNARROW[property, AlpineEnvironment.PropertyValuePair.byteLength].byteLength;
resultRope ← Rope.Concat[resultRope, IO.PutFR["%g bytes ", IO.int[byteLength]]];
};
createTime
createTime => {
createTime: BasicTime.GMTNARROW[property, AlpineEnvironment.PropertyValuePair.createTime].createTime;
resultRope ← Rope.Concat[resultRope, IO.PutFR["%g ", IO.time[createTime]]];
};
highWaterMark
highWaterMark => {
highWaterMark: AlpineEnvironment.PageCount ← NARROW[property, AlpineEnvironment.PropertyValuePair.highWaterMark].highWaterMark;
resultRope ← Rope.Concat[resultRope, IO.PutFR["HWM: %g ", IO.int[highWaterMark]]];
};
modifyAccess
modifyAccess => {
modifyAccess: AlpineEnvironment.AccessList ← NARROW[property, AlpineEnvironment.PropertyValuePair.modifyAccess].modifyAccess;
resultRope ← Rope.Concat[resultRope,"modify access: ("];
IF modifyAccess = NIL THEN resultRope ← Rope.Concat[resultRope,"*none*"] ELSE {
UNTIL modifyAccess = NIL DO
accessItem: AlpineEnvironment.RName ← modifyAccess.first;
resultRope ← Rope.Concat[resultRope,accessItem];
modifyAccess ← modifyAccess.rest;
IF modifyAccess # NIL THEN resultRope ← Rope.Concat[resultRope,", "];
ENDLOOP;
} ;
resultRope ← Rope.Concat[resultRope,") "];
};
owner
owner => {
owner: AlpineEnvironment.OwnerName ← NARROW[property, AlpineEnvironment.PropertyValuePair.owner].owner;
resultRope ← Rope.Concat[resultRope, IO.PutFR["owner: %g ", IO.rope[owner]]];
};
readAccess
readAccess => {
readAccess: AlpineEnvironment.AccessList ← NARROW[property, AlpineEnvironment.PropertyValuePair.readAccess].readAccess;
resultRope ← Rope.Concat[resultRope,"read access: ("];
IF readAccess = NIL THEN resultRope ← Rope.Concat[resultRope,"*none*"] ELSE {
UNTIL readAccess = NIL DO
accessItem: AlpineEnvironment.RName ← readAccess.first;
resultRope ← Rope.Concat[resultRope,accessItem];
readAccess ← readAccess.rest;
IF readAccess # NIL THEN resultRope ← Rope.Concat[resultRope,", "];
ENDLOOP;
} ;
resultRope ← Rope.Concat[resultRope,") "];
};
stringName
stringName => {
stringName: ROPENARROW[property, AlpineEnvironment.PropertyValuePair.stringName].stringName;
resultRope ← Rope.Concat[resultRope, IO.PutFR["stringName: %g ", IO.rope[stringName]]];
};
version
version => {
version: LONG INTEGERNARROW[property, AlpineEnvironment.PropertyValuePair.version].version;
resultRope ← Rope.Concat[resultRope, IO.PutFR["version: %g ", IO.int[version]]];
};
ENDCASE;
};
ENDLOOP;
};
noFiles ← noFiles + 1;
noBytes ← noBytes + byteLength;
resultList ← CONS[first: resultRope, rest: resultList];
EXITS
lockError => {
resultRope ← IO.PutFR["%g has lock set", IO.rope[fileName]];
resultList ← CONS[first: resultRope, rest: resultList];
};
skipFile => {};
};
RETURN [quit: FALSE, didCommitAndContinue: FALSE, nowTrans: nowTrans];
};
FSListEnumProc: FS.InfoProc = {
PROC [fullFName, attachedTo: ROPE, created: BasicTime.GMT, bytes: INT,
keep: CARDINAL] RETURNS [continue: BOOLEAN];
resultRope: ROPE ;
fName: ROPE ;
componentPositions: FS.ComponentPositions ;
IF d.stopFlag THEN {
resultList ← CONS[first: NARROW[" ** List aborted **", ROPE], rest: NIL];
noFiles ← 0;
RETURN [continue: FALSE];
};
[fullFName: fName, cp: componentPositions] ← FS.ExpandName[name: fullFName];
resultRope ← IO.PutFR["%g\t", IO.rope[fName]];
resultRope ← Rope.Concat[resultRope,
IO.PutFR["%g bytes ", IO.int[bytes]]];
resultRope ← Rope.Concat[resultRope,
IO.PutFR["\t%g ", IO.time[created]]];
noFiles ← noFiles + 1;
noBytes ← noBytes + bytes;
resultList ← CONS[first: resultRope, rest: resultList];
RETURN [continue: TRUE];
};
resultRope: ROPENIL;
outcome: AlpTransaction.Outcome;
failureName: ROPE ← "" ;
{
[directory, restOfPattern] ← DecomposePattern[server, pattern, user];
IF trans # NIL THEN {
 alpine files to list
IF d.assertWheel THEN trans.AssertAlpineWheel[TRUE];
[] ← EnumeratePattern[
transHandle: trans,
transServer: server,
caller: caller,
key: key,
assertWheel: d.assertWheel,
commitALot: FALSE,
pattern: Rope.Concat["[",Rope.Concat[server,Rope.Concat["]",directory]]].Concat[restOfPattern],
enumProc: AlpineListEnumProc];
outcome ← AlpTransaction.Finish[trans, commit];
}
ELSE {
 FS or IFS files to list
pattern: ROPE;
pattern ← Rope.Concat[Rope.Concat[Rope.Concat[Rope.Concat ["[", server], "]"], directory], restOfPattern];
FS.EnumerateForInfo[pattern: pattern, proc: FSListEnumProc ];
};
};
IF noFiles > 0 THEN resultList ← CONS[first: IO.PutFR["-- %g files, %g total bytes", IO.int[noFiles], IO.int[noBytes]], rest: resultList];
resultList ← List.Sort[resultList, CompareProc];
RETURN [resultList];
};
DecomposePattern: PUBLIC PROC [server: ROPE, pattern: ROPE, user: ROPE, addStar: BOOL ← TRUE] RETURNS [directory, restOfPattern: ROPE] = {
rightAngleBracket: INT;
dir: ROPE ;
isAlpine: BOOL ;
IF Rope.Match[pattern: "*.alpine", object: server, case: FALSE] THEN {
isAlpine ← TRUE;
dir ← user;
}
ELSE {
isAlpine ← FALSE;
IF server.IsEmpty[] THEN dir ← NIL
ELSE {
IF Rope.Match[pattern: "*.*", object: user, case: FALSE] THEN dir ← user.Substr[start: 0, len: user.SkipTo[1, "."]]
ELSE dir ← user;
};
};
IF NOT Rope.Match[pattern: "<*>*", object: pattern, case: FALSE] THEN {
directory ← Rope.Cat["<", dir, ">"] ;
restOfPattern ← pattern ;
IF restOfPattern.IsEmpty[] THEN restOfPattern ← "*";
}
ELSE {
rightAngleBracket ← pattern.SkipTo[1, ">"];
IF rightAngleBracket > 1
THEN directory ← pattern.Substr[start: 0, len: rightAngleBracket+1]
ELSE directory ← Rope.Cat["<", dir, ">"] ;
restOfPattern ← pattern.Substr[start: rightAngleBracket+1];
IF restOfPattern.IsEmpty[] AND addStar THEN restOfPattern ← "*";
};
};
ListFilesProc: PUBLIC Buttons.ButtonProc = {
resultList: LIST OF REF ANYNIL;
d: MyData = NARROW[clientData];
server, user, file, password: ROPE;
directory, restOfPattern: ROPE;
printedSomething: BOOLFALSE;
parseError: BOOL;
errorExplanation: ROPE;
callList: YodelData.PerformProc = {
RETURN[listFiles[trans, server, file, user, password, caller, key, d.displayProperties, d]];
};
typeParseError: PROC [] = {
d.out.PutF["\nBad pattern in List because %g\n", IO.rope[errorExplanation]];
};
d.stopFlag ← FALSE;
IF d.background THEN CedarProcess.SetPriority[background];
[user, password, server, directory, file, parseError, errorExplanation] ← ParseSArgs[d];
IF parseError THEN { typeParseError[]; GOTO badParse;};
[directory, restOfPattern] ← DecomposePattern[server: server, pattern: file, user: user];
d.out.PutF["\nList of [%g]%g%g\n", IO.rope[server], IO.rope[directory], IO.rope[restOfPattern]];
resultList ← PerformOp[performProc: callList, server: server, user: user, password: password];
printedSomething ← PrintResult[d.out, resultList];
IF NOT printedSomething THEN d.out.PutRope[" ** no files match the pattern **\n"];
EXITS
badParse => {};
};
fileDelete: PROC [trans: AlpTransaction.Handle, server: ROPE, file: ROPE, user: ROPENIL, password: ROPENIL, caller: AlpineEnvironment.Principal, key: RPC.EncryptionKey, d: MyData] RETURNS [resultList: LIST OF REF ANYNIL, countDeleted: INT ← 0] = {
directory, restOfPattern, directoryName: ROPE;
firstNewNameLessVersion: ROPENIL;
topVersion: INT ← 0;
allFullNamesSeen: LIST OF ROPENIL;
resultListBeforeCommit: LIST OF REF ANYNIL;
countDeletedBeforeCommit: INT ← 0 ;
fileCounter: INT ← 0;
AlpineDeleteEnumProc: EnumProc = {
PROC [ fileName: ROPE, universalFile: AE.UniversalFile, trans: AlpTransaction.Handle, flushState: BOOL ] RETURNS [ didCommitAndContinue: BOOL, quit: BOOL, nowTrans: AlpTransaction.Handle ];
fullFNameLessVersion: ROPE;
bangPos: INT;
thisVersion: INT;
lastOne: BOOLFALSE;
tryToDelete: PROC [ fullNameToDelete: ROPE] RETURNS [quit: BOOLFALSE] = {
deleteIt: BOOLTRUE;
IF ~d.AutoDelete AND ~ lastOne THEN {
answer: YesNoAll;
nowQuit: BOOL;
ViewerTools.SetContents[d.oDeleteConfirm, fullNameToDelete, TRUE];
enableAnswer[d];
[nowQuit, answer] ← getAnswer[d];
disableAnswer[d];
ViewerTools.SetContents[d.oDeleteConfirm, ""];
IF nowQuit THEN {
resultList ← CONS[first: NARROW[">> ** Delete aborted **", ROPE], rest: resultListBeforeCommit];
RETURN [quit: TRUE];
};
IF answer = no THEN deleteIt ← FALSE;
};
IF deleteIt THEN {
IF ~lastOne THEN [] ← AlpDirectory.DeleteFile[transHandle: nowTrans, name: fullNameToDelete];
IF d.AutoDelete THEN {
IF ~lastOne THEN {
countDeletedBeforeCommit ← countDeletedBeforeCommit + 1;
resultRope ← IO.PutFR["%g deleted", IO.rope[fullNameToDelete]];
resultListBeforeCommit ← CONS[first: resultRope, rest: resultListBeforeCommit];
};
IF fileCounter > 10 OR lastOne THEN {
outcome: AlpTransaction.Outcome;
outcome ← AlpTransaction.Finish[handle: nowTrans, requestedOutcome: commit, continue: TRUE];
IF outcome # commit THEN ERROR AlpInstance.Unknown[transID]; -- raise ERROR to be caught by EnumeratePattern (our caller) for retry
FOR rList: LIST OF REF ANY ← List.Reverse[resultListBeforeCommit], rList.rest UNTIL rList = NIL DO
res: ROPE;
res ← NARROW[IF rList.first = NIL THEN NIL ELSE rList.first];
IF res = NIL THEN EXIT;
d.out.PutF[" %g\n", IO.rope[res]];
ENDLOOP;
IF d.assertWheel THEN nowTrans.AssertAlpineWheel[TRUE]; -- wheelness does not survied commit and continue
resultListBeforeCommit ← NIL;
didCommitAndContinue ← TRUE;
countDeleted ← countDeleted + countDeletedBeforeCommit;
countDeletedBeforeCommit ← 0;
fileCounter ← 0;
};
}
ELSE {
outcome: AlpTransaction.Outcome;
IF ~lastOne THEN {
outcome ← AlpTransaction.Finish[handle: nowTrans, requestedOutcome: commit, continue: TRUE];
IF outcome # commit THEN {
inst: AlpInstance.Handle ← NIL;
inst ← AlpInstance.Create[fileStore: server, caller: caller, key: key];
nowTrans ← AlpTransaction.Create[inst];
IF d.assertWheel THEN nowTrans.AssertAlpineWheel[TRUE];
[] ← AlpDirectory.DeleteFile[transHandle: nowTrans, name: fullNameToDelete];
outcome ← AlpTransaction.Finish[handle: nowTrans, requestedOutcome: commit, continue: TRUE];
IF outcome # commit THEN {
d.out.PutF["Alpine transaction aborted -- %g could NOT be deleted-- delete stopped\n", IO.rope[fullNameToDelete]];
quit ← TRUE;
RETURN;
};
};
IF d.assertWheel THEN nowTrans.AssertAlpineWheel[TRUE]; -- wheelness does not survied commit and continue
countDeleted ← countDeleted + 1;
d.out.PutF["%g deleted\n", IO.rope[fullNameToDelete]];
didCommitAndContinue← TRUE;
};
};
};
};
didCommitAndContinue ← FALSE;
quit ← FALSE;
nowTrans ← trans;
IF d.stopFlag THEN {
resultList ← CONS[first: NARROW[" ** Delete aborted **", ROPE], rest: NIL];
RETURN [quit: TRUE, didCommitAndContinue: FALSE, nowTrans: nowTrans];
};
IF flushState THEN {
firstNewNameLessVersion ← NIL;
topVersion ← 0;
allFullNamesSeen ← NIL;
resultListBeforeCommit ← NIL;
countDeletedBeforeCommit ← 0;
fileCounter ← 0 ;
};
fileCounter ← fileCounter + 1;
IF fileName.IsEmpty[] THEN {
fileName ← "$$$Junk!1";
};
IF bangStarAndNonzeroDelVer THEN {
bangPos ← fileName.Find["!"];
fullFNameLessVersion ← fileName.Substr[0, bangPos-1];
thisVersion ← Convert.IntFromRope[fileName.Substr[bangPos+1]];
IF Rope.Equal[firstNewNameLessVersion, fullFNameLessVersion, FALSE] THEN {
allFullNamesSeen ← CONS[fileName, allFullNamesSeen];
IF thisVersion > topVersion THEN topVersion ← thisVersion;
}
ELSE {
IF allFullNamesSeen # NIL THEN {
FOR nowNameList: LIST OF ROPE ← allFullNamesSeen, nowNameList.rest UNTIL nowNameList = NIL DO
nowBangPos: INT = nowNameList.first.Find["!"];
nowVersion: INT ← Convert.IntFromRope[nowNameList.first.Substr[nowBangPos+1]];
IF (nowVersion + d.DelVerCount) <= topVersion THEN quit ← tryToDelete[nowNameList.first];
IF quit THEN EXIT;
ENDLOOP;
};
IF fileName.Equal["$$$Junk!1"] THEN {
lastOne ← TRUE;
[] ← tryToDelete[fileName];
};
topVersion ← thisVersion;
firstNewNameLessVersion ← fullFNameLessVersion;
allFullNamesSeen ← CONS[fileName, NIL];
};
}
ELSE {
IF fileName.Equal["$$$Junk!1"] THEN lastOne ← TRUE;
quit ← tryToDelete[fileName];
};
RETURN;
};
FSDeleteEnumProc: FS.NameProc = {
PROC [fullFName: ROPE] RETURNS [continue: BOOL];
inner: PROC [fullFName: ROPE] RETURNS [continue: BOOL] = {
deleteIt: BOOLTRUE;
fullFNameLessVersion: ROPE;
bangPos: INT;
thisVersion: INT;
IF d.stopFlag THEN {
resultList ← CONS[first: NARROW[">> ** Delete aborted **", ROPE], rest: resultList];
RETURN [continue: FALSE];
};
bangPos ← fullFName.Find["!"];
fullFNameLessVersion ← fullFName.Substr[0, bangPos-1];
thisVersion ← Convert.IntFromRope[fullFName.Substr[bangPos+1]];
IF bangStarAndNonzeroDelVer THEN {
IF (thisVersion + d.DelVerCount) > topVersion THEN deleteIt ← FALSE;
};
IF deleteIt AND ~d.AutoDelete THEN {
answer: YesNoAll;
nowQuit: BOOL;
ViewerTools.SetContents[d.oDeleteConfirm, fullFName, TRUE];
enableAnswer[d];
[nowQuit, answer] ← getAnswer[d];
disableAnswer[d];
ViewerTools.SetContents[d.oDeleteConfirm, ""];
IF nowQuit THEN {
resultList ← CONS[first: NARROW[">> ** Delete aborted **", ROPE], rest: resultList];
RETURN [continue: FALSE];
};
IF answer = no THEN deleteIt ← FALSE;
};
IF deleteIt THEN {
FS.Delete[ name: fullFName];
countDeleted ← countDeleted + 1;
d.out.PutF["%g deleted\n", IO.rope[fullFName]];
};
RETURN [continue: TRUE];
};
continue ← TRUE;
IF bangStarAndNonzeroDelVer THEN {
innerPattern: ROPE;
thisBangPos: INT;
outerContinue: BOOL ;
anotherEnum: FS.NameProc = {
continue ← inner[fullFName];
outerContinue ← continue;
};
thisBangPos ← fullFName.Find["!"];
topVersion ← Convert.IntFromRope[fullFName.Substr[thisBangPos+1]];
innerPattern ← Rope.Concat[fullFName.Substr[0, thisBangPos+1], versionPattern];
FS.EnumerateForNames[pattern: innerPattern, proc: anotherEnum ];
continue ← outerContinue;
}
ELSE {
continue ← inner[fullFName];
};
};
resultRope: ROPENIL;
outcome: AlpTransaction.Outcome;
failureName: ROPE ← "" ;
restBang: INT;
versionPattern: ROPE;
bangStarAndNonzeroDelVer: BOOLFALSE;
patternForFS: ROPE;
[directory, restOfPattern] ← DecomposePattern[server, file, user];
d.deleteConfirm ← wait;
IF (restBang ← Rope.Find[restOfPattern,"!"]) > 0 THEN {
versionPattern ← restOfPattern.Substr[restBang+1];
IF versionPattern.Equal["*"] AND d.DelVerCount # 0 THEN bangStarAndNonzeroDelVer ← TRUE;
}
ELSE versionPattern ← "L";
IF trans # NIL THEN {
alpine files to delete
IF d.assertWheel THEN trans.AssertAlpineWheel[TRUE];
IF restBang < 0 THEN {
restOfPattern ← restOfPattern.Concat["!L"];
};
directoryName ← Rope.Concat["[",Rope.Concat[server,Rope.Concat["]",directory]]];
trans ← EnumeratePattern[
transHandle: trans,
transServer: server,
caller: caller,
key: key,
commitALot: TRUE,
assertWheel: d.assertWheel,
pattern: directoryName.Concat[restOfPattern],
enumProc: AlpineDeleteEnumProc
];
outcome ← AlpTransaction.Finish[trans, commit];
IF outcome # commit THEN {
resultList ← CONS[
first: Rope.Concat["Alpine transaction aborted -- deletes NOT done",
IF resultList = NIL THEN NIL ELSE "; log before abort is:"],
rest: resultList];
};
}
ELSE {
 FS or IFS files to delete
anotherPattern: ROPE;
anotherBang: INT;
patternForFS ← Rope.Concat[Rope.Concat[Rope.Concat[Rope.Concat["[", server], "]"], directory], restOfPattern];
IF (anotherBang ← patternForFS.Find["!"]) < 0 THEN patternForFS ← Rope.Concat[patternForFS, "!l"];
anotherPattern ← patternForFS;
IF bangStarAndNonzeroDelVer THEN anotherPattern ← Rope.Concat[patternForFS.Substr[0, anotherBang], "!h"]; -- have to enumerate highest version, so we can do the delete versions
FS.EnumerateForNames[pattern: anotherPattern, proc: FSDeleteEnumProc ];
};
d.stopFlag ← TRUE;
setAnswer[d, wait];
resultList ← List.Sort[resultList, CompareProc];
};
DeleteFilesProc: PUBLIC Buttons.ButtonProc = {
d: MyData = NARROW[clientData];
saveDelVerCount: INT ← d.DelVerCount;
resultList: LIST OF REF ANYNIL;
srcServer, srcFile, srcDir: ROPE;
user, password: ROPE;
directory, restOfPattern: ROPE;
keepMsg: ROPE;
printedSomething: BOOLFALSE;
parseError: BOOL;
errorExplanation: ROPE;
deleteNum: INT ← 0;
callDelete: YodelData.PerformProc = {
lora: LIST OF REF ANY;
[lora, deleteNum] ← fileDelete[trans: trans, server: srcServer, file: srcFile, user: user, password: password, caller: caller, key: key, d: d];
RETURN[lora];
};
typeParseError: PROC [] = {
d.out.PutF["\nBad pattern in Delete because %g\n", 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[]; GOTO badParse;};
[directory, restOfPattern] ← DecomposePattern[server: srcServer, pattern: srcFile, user: user];
IF Rope.IsEmpty[srcFile] THEN {
d.out.PutF["\nDelete of [%g]%g%g NOT done -- MUST have explicit pattern\n", IO.rope[srcServer], IO.rope[srcDir], IO.rope[restOfPattern]];
RETURN;
};
IF d.DelVerCount # 0 THEN {
bangPos: INT ← Rope.Find[restOfPattern, "!"];
vers: ROPE"";
IF bangPos >= 0 THEN {
vers ← restOfPattern.Substr[bangPos+1];
}
ELSE restOfPattern ← restOfPattern.Cat["!L"];
IF ~vers.Equal["*"] THEN {
d.DelVerCount ← 0;
keepMsg ← NIL;
}
ELSE keepMsg ← IO.PutFR["with keep %g ", IO.int[d.DelVerCount]];
};
d.out.PutF["\nDelete [%g]%g%g %g\n", IO.rope[srcServer], IO.rope[srcDir], IO.rope[restOfPattern], IO.rope[keepMsg]];
resultList ← PerformOp[performProc: callDelete, server: srcServer, user: user, password: password];
d.DelVerCount ← saveDelVerCount;
printedSomething ← PrintResult[d.out, resultList];
IF NOT printedSomething AND deleteNum = 0 THEN d.out.PutRope[" ** no files deleted **\n"];
IF deleteNum # 0 THEN d.out.PutF[" ** %g files deleted **\n", IO.int[deleteNum]];
EXITS
badParse => {};
};
OptionsProc: PUBLIC Buttons.ButtonProc = {
d: MyData = NARROW[clientData];
p: ViewerClasses.Viewer = NARROW[parent];
d.displayOptions ← NOT d.displayOptions ;
CreateButtons[d, p.parent.parent];
};
ChangeOptionsProc: PUBLIC Buttons.ButtonProc = {
d: MyData = NARROW[clientData];
p: ViewerClasses.Viewer = NARROW[parent];
count: INT ;
option: AlpineEnvironment.Property;
loop through all the properties and find the one selected
comparison is by Rope equivalent to option!
FOR count IN [0..NumberOfAlpineProperties) DO
propertyName: ROPE ;
[option, propertyName] ← PropertySetToRopeArray[count];
IF Rope.Equal[propertyName, p.name] THEN {
d.displayProperties[option] ← NOT d.displayProperties[option] ;
IF d.displayProperties[option]
THEN Buttons.SetDisplayStyle[p, $WhiteOnBlack]
ELSE Buttons.SetDisplayStyle[p, $BlackOnWhite];
EXIT;
};
ENDLOOP;
};
ChangeAutoDeleteProc: PUBLIC Buttons.ButtonProc = {
d: MyData = NARROW[clientData];
p: ViewerClasses.Viewer = NARROW[parent];
d.AutoDelete ← NOT d.AutoDelete ;
IF d.AutoDelete
THEN Buttons.SetDisplayStyle[p, $WhiteOnBlack]
ELSE Buttons.SetDisplayStyle[p, $BlackOnWhite];
d.deleteConfirm ← yes;
CreateButtons[d, p.parent.parent]
};
YesProc: PUBLIC Buttons.ButtonProc = {
d: MyData = NARROW[clientData];
p: ViewerClasses.Viewer = NARROW[parent];
setAnswer[d, yes];
};
NoProc: PUBLIC Buttons.ButtonProc = {
d: MyData = NARROW[clientData];
p: ViewerClasses.Viewer = NARROW[parent];
setAnswer[d, no];
};
enableAnswer: ENTRY PROC [d: MyData] = {
Buttons.ReLabel[d.yesButton, "Yes", TRUE];
Buttons.ReLabel[d.noButton, "No", TRUE];
d.deleteConfirm ← wait;
};
disableAnswer: ENTRY PROC [d: MyData] = {
Buttons.ReLabel[d.yesButton, "", TRUE];
Buttons.ReLabel[d.noButton, "", TRUE];
d.deleteConfirm ← wait;
};
setAnswer: ENTRY PROC [d: MyData, answer: YesNoAll] = {
DO
IF d.stopFlag THEN { NOTIFY d.condition; RETURN;};
IF d.deleteConfirm # wait THEN WAIT d.condition
ELSE {
d.deleteConfirm ← answer;
NOTIFY d.condition;
RETURN;
};
ENDLOOP;
};
getAnswer: ENTRY PROC [d: MyData] RETURNS [stop: BOOLFALSE, answer: YesNoAll] = {
DO
IF d.stopFlag THEN { NOTIFY d.condition; RETURN[TRUE, wait];};
IF d.deleteConfirm = wait THEN WAIT d.condition
ELSE {
answer ← d.deleteConfirm;
d.deleteConfirm ← wait;
NOTIFY d.condition;
RETURN;
};
ENDLOOP;
};
ChangeDelVerProc: PUBLIC Buttons.ButtonProc = {
parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL
d: MyData = NARROW[clientData];
p: ViewerClasses.Viewer = NARROW[parent];
propertyName: ROPE ;
SELECT mouseButton FROM
red => {  -- increase number of versions to keep
IF d.DelVerCount # INT.LAST THEN d.DelVerCount ← d.DelVerCount + 1;
};
yellow => {  -- keep 1
d.DelVerCount ← 1;
};
blue => {   -- decrease number of versions to keep
IF d.DelVerCount > 0 THEN d.DelVerCount ← d.DelVerCount - 1;
};
ENDCASE => ERROR;
propertyName ← Rope.Cat["Keep ", IO.PutFR["%g", IO.int[d.DelVerCount]], " versions"];
ViewerTools.SetContents[p, propertyName];
};
ChangePriorityProc: PUBLIC Buttons.ButtonProc = {
parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL
d: MyData = NARROW[clientData];
p: ViewerClasses.Viewer = NARROW[parent];
d.background ← NOT d.background;
IF d.background
THEN Buttons.SetDisplayStyle[p, $WhiteOnBlack]
ELSE Buttons.SetDisplayStyle[p, $BlackOnWhite];
};
InterimlistFiles: PROC [trans: AlpTransaction.Handle, server: ROPE, pattern: ROPE, user: ROPENIL, password: ROPENIL , displayProperties: AlpineFile.PropertySet ← AlpineFile.allProperties, d: MyData] RETURNS [LIST OF REF ANY] = {
directory, restOfPattern: ROPE;
resultList: LIST OF REF ANYNIL;
AlpineEnumProc: PROC [fileName: REF TEXT, universalFile: AlpineEnvironment.UniversalFile] RETURNS [quit: BOOL] = {
intendReadLockOption: AlpFile.LockOption = [intendRead, IF d.breakLocks THEN wait ELSE fail];
readLockOption: AlpFile.LockOption = [read, IF d.breakLocks THEN wait ELSE fail];
IF d.stopFlag THEN {
resultList ← CONS[first: NARROW[" ** List aborted **", ROPE], rest: NIL];
RETURN [quit: TRUE];
};
IF Rope.Match[pattern: restOfPattern, object: RefText.TrustTextAsRope[fileName], case: FALSE] THEN {
file: AlpFile.Handle;
fileID: AlpFile.FileID;
properties: LIST OF AlpineEnvironment.PropertyValuePair;
byteLength: INT;
resultRope: ROPE ;
[file, fileID] ← AlpFile.Open[transHandle: trans, universalFile: universalFile, lock: intendReadLockOption
! AlpInstance.LockFailed => GOTO lockError;
AlpInstance.AccessFailed => IF missingAccess = AlpineEnvironment.NeededAccess.fileRead
THEN GOTO skipFile
ELSE REJECT;
];
properties ← AlpFile.ReadProperties[handle: file, lock: readLockOption
! AlpInstance.LockFailed => GOTO lockError;
AlpInstance.AccessFailed =>
IF missingAccess = AlpineEnvironment.NeededAccess.fileRead THEN GOTO skipFile
ELSE REJECT;
];
byteLength ← NARROW[properties.first, AlpineEnvironment.PropertyValuePair.byteLength].byteLength;
resultRope ← IO.PutFR["%g\t", IO.text[fileName]];
loop through properties and print those enabled
UNTIL properties = NIL DO
property: AlpineEnvironment.PropertyValuePair ← properties.first ;
properties ← properties.rest;
IF displayProperties[property.property] THEN BEGIN
SELECT property.property FROM
 byteLength
byteLength => {
byteLength: INTNARROW[property, AlpineEnvironment.PropertyValuePair.byteLength].byteLength;
resultRope ← Rope.Concat[resultRope, IO.PutFR["%g bytes ", IO.int[byteLength]]];
};
createTime
createTime => {
createTime: BasicTime.GMTNARROW[property, AlpineEnvironment.PropertyValuePair.createTime].createTime;
resultRope ← Rope.Concat[resultRope, IO.PutFR["%g ", IO.time[createTime]]];
};
highWaterMark
highWaterMark => {
highWaterMark: AlpineEnvironment.PageCount ← NARROW[property, AlpineEnvironment.PropertyValuePair.highWaterMark].highWaterMark;
resultRope ← Rope.Concat[resultRope, IO.PutFR["HWM: %g ", IO.int[highWaterMark]]];
};
modifyAccess
modifyAccess => {
modifyAccess: AlpineEnvironment.AccessList ← NARROW[property, AlpineEnvironment.PropertyValuePair.modifyAccess].modifyAccess;
resultRope ← Rope.Concat[resultRope,"modify access: ("];
IF modifyAccess = NIL THEN resultRope ← Rope.Concat[resultRope,"*none*"]
ELSE {
UNTIL modifyAccess = NIL DO
accessItem: AlpineEnvironment.RName ← modifyAccess.first;
resultRope ← Rope.Concat[resultRope,accessItem];
modifyAccess ← modifyAccess.rest;
IF modifyAccess # NIL THEN resultRope ← Rope.Concat[resultRope,", "];
ENDLOOP;
} ;
resultRope ← Rope.Concat[resultRope,") "];
};
owner
owner => {
owner: AlpineEnvironment.OwnerName ← NARROW[property, AlpineEnvironment.PropertyValuePair.owner].owner;
resultRope ← Rope.Concat[resultRope, IO.PutFR["owner: %g ", IO.rope[owner]]];
};
readAccess
readAccess => {
readAccess: AlpineEnvironment.AccessList ← NARROW[property, AlpineEnvironment.PropertyValuePair.readAccess].readAccess;
resultRope ← Rope.Concat[resultRope,"read access: ("];
IF readAccess = NIL THEN resultRope ← Rope.Concat[resultRope,"*none*"]
ELSE {
UNTIL readAccess = NIL DO
accessItem: AlpineEnvironment.RName ← readAccess.first;
resultRope ← Rope.Concat[resultRope,accessItem];
readAccess ← readAccess.rest;
IF readAccess # NIL THEN resultRope ← Rope.Concat[resultRope,", "];
ENDLOOP;
} ;
resultRope ← Rope.Concat[resultRope,") "];
};
stringName
stringName => {
stringName: ROPENARROW[property, AlpineEnvironment.PropertyValuePair.stringName].stringName;
resultRope ← Rope.Concat[resultRope, IO.PutFR["stringName: %g ", IO.rope[stringName]]];
};
version
version => {
version: LONG INTEGERNARROW[property, AlpineEnvironment.PropertyValuePair.version].version;
resultRope ← Rope.Concat[resultRope, IO.PutFR["version: %g ", IO.int[version]]];
};
ENDCASE;
END;
ENDLOOP;
resultList ← CONS[first: resultRope, rest: resultList];
EXITS
lockError => {
resultRope ← IO.PutFR["%g has lock set", IO.text[fileName]];
resultList ← CONS[first: resultRope, rest: resultList];
};
skipFile => {};
};
RETURN [quit: FALSE];
};
resultRope: ROPENIL;
outcome: AlpTransaction.Outcome;
failureName: ROPE ← "" ;
{
[directory, restOfPattern] ← DecomposePattern[server, pattern, user];
IF trans # NIL THEN {
alpine files to list
bangPos: INT ;
IF d.assertWheel THEN trans.AssertAlpineWheel[TRUE];
IF (bangPos ← Rope.Find[restOfPattern,"!"]) >= 0 THEN {
IF bangPos = 0
THEN restOfPattern ← "*"
ELSE restOfPattern ← Rope.Substr[base: restOfPattern, len: bangPos];
};
AlpineInterimDirectory.EnumerateDirectoryUnderTrans[
transHandle: trans,
directoryName: Rope.Concat["[",Rope.Concat[server,Rope.Concat["]",directory]]],
enumProc: AlpineEnumProc];
outcome ← AlpTransaction.Finish[trans, commit];
}
ELSE {
resultList ← CONS[first: NARROW["Use the List button for FS and IFS Files", ROPE], rest: resultList];
};
};
resultList ← List.Sort[resultList, CompareProc];
RETURN [resultList];
};
ListInterimProc: PUBLIC Buttons.ButtonProc = {
resultList: LIST OF REF ANYNIL;
d: MyData = NARROW[clientData];
server, user, file, password: ROPE;
directory, restOfPattern: ROPE;
printedSomething: BOOLFALSE;
parseError: BOOL;
errorExplanation: ROPE;
callList: YodelData.PerformProc = {
RETURN[InterimlistFiles[trans, server, file, user, password, d.displayProperties, d]];
};
typeParseError: PROC [] = {
d.out.PutF["\nBad pattern in List because %g\n", IO.rope[errorExplanation]];
};
d.stopFlag ← FALSE;
[user, password, server, directory, file, parseError, errorExplanation] ← ParseSArgs[d];
IF parseError THEN { typeParseError[]; GOTO badParse;};
[directory, restOfPattern] ← DecomposePattern[server: server, pattern: file, user: user];
d.out.PutF["\nList Interim of [%g]%g%g\n",
IO.rope[server], IO.rope[directory], IO.rope[restOfPattern]];
resultList ← PerformOp[performProc: callList,
server: server, user: user, password: password];
DO
nowRope: ROPENARROW[IF resultList = NIL THEN NIL ELSE resultList.first];
IF resultList = NIL THEN EXIT;
resultList ← resultList.rest;
d.out.PutF[" %g\n", IO.rope[nowRope]];
printedSomething ← TRUE ;
ENDLOOP;
IF NOT printedSomething THEN d.out.PutRope[" ** no files match the pattern **\n"];
EXITS
badParse => {};
};
END.
Bob Hagmann June 11, 1985 9:11:52 am PDT
reformatted
Bob Hagmann June 12, 1985 8:10:17 am PDT
split from YodelUserImpl
Bob Hagmann June 12, 1985 9:16:23 am PDT
changes to: delete for delver and confirmantion

Bob Hagmann July 9, 1985
changes to: copy - allow multiple file copy and other bug fixes
Bob Hagmann July 26, 1985 9:51:26 am PDT
changes to: tryToDelete (local of AlpineDeleteEnumProc, local of fileDelete)
Bob Hagmann July 26, 1985 2:20:23 pm PDT
changes to: tryToDelete (local of AlpineDeleteEnumProc, local of fileDelete)
Bob Hagmann August 9, 1985 2:39:57 pm PDT
changes to: tryToDelete (local of AlpineDeleteEnumProc, local of fileDelete)
Bob Hagmann January 6, 1986 4:48:38 pm PST
changes to: AlpineDeleteEnumProc (local of fileDelete)
Bob Hagmann January 27, 1986 9:16:20 am PST
Fix up deletes of $$$.btree and $$$Junk!1 (during delete with 0 keep)
changes to: EnumeratePattern, AlpineDeleteEnumProc (local of fileDelete)