<> <> <> <> <> <> <> 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, FS USING[ ComponentPositions, Delete, EnumerateForInfo, EnumerateForNames, Error, ExpandName, InfoProc, NameProc], IO USING[ 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: ROPE _ NIL; firstErrorThisXAct: BOOL _ FALSE; doFlush: BOOL _ TRUE; file: AE.UniversalFile; fullPathName, link: ROPE; tryRecovery: PROC RETURNS [doRetry: BOOL _ FALSE] = { 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: ROPE _ NIL, parseError: BOOL _ FALSE, errorExplanation: ROPE _ NIL] = { 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: ROPE _ NIL, password: ROPE _ NIL , 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 ANY _ NIL; noFiles: INT _ 0; noBytes: INT _ 0 ; AlpineListEnumProc: EnumProc = { <> 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; <<>> <> UNTIL properties = NIL DO property: AlpineEnvironment.PropertyValuePair _ properties.first ; properties _ properties.rest; IF displayProperties[property.property] THEN { SELECT property.property FROM << byteLength>> byteLength => { byteLength: INT _ NARROW[property, AlpineEnvironment.PropertyValuePair.byteLength].byteLength; resultRope _ Rope.Concat[resultRope, IO.PutFR["%g bytes ", IO.int[byteLength]]]; }; <> createTime => { createTime: BasicTime.GMT _ NARROW[property, AlpineEnvironment.PropertyValuePair.createTime].createTime; resultRope _ Rope.Concat[resultRope, IO.PutFR["%g ", IO.time[createTime]]]; }; <> highWaterMark => { highWaterMark: AlpineEnvironment.PageCount _ NARROW[property, AlpineEnvironment.PropertyValuePair.highWaterMark].highWaterMark; resultRope _ Rope.Concat[resultRope, IO.PutFR["HWM: %g ", IO.int[highWaterMark]]]; }; <> 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: AlpineEnvironment.OwnerName _ NARROW[property, AlpineEnvironment.PropertyValuePair.owner].owner; resultRope _ Rope.Concat[resultRope, IO.PutFR["owner: %g ", IO.rope[owner]]]; }; <> 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: ROPE _ NARROW[property, AlpineEnvironment.PropertyValuePair.stringName].stringName; resultRope _ Rope.Concat[resultRope, IO.PutFR["stringName: %g ", IO.rope[stringName]]]; }; <> version => { version: LONG INTEGER _ NARROW[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 = { <> <> 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: ROPE _ NIL; 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 ANY _ NIL; d: MyData = NARROW[clientData]; server, user, file, password: ROPE; directory, restOfPattern: ROPE; printedSomething: BOOL _ FALSE; 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: ROPE _ NIL, password: ROPE _ NIL, caller: AlpineEnvironment.Principal, key: RPC.EncryptionKey, d: MyData] RETURNS [resultList: LIST OF REF ANY _ NIL, countDeleted: INT _ 0] = { directory, restOfPattern, directoryName: ROPE; firstNewNameLessVersion: ROPE _ NIL; topVersion: INT _ 0; allFullNamesSeen: LIST OF ROPE _ NIL; resultListBeforeCommit: LIST OF REF ANY _ NIL; countDeletedBeforeCommit: INT _ 0 ; fileCounter: INT _ 0; AlpineDeleteEnumProc: EnumProc = { <> fullFNameLessVersion: ROPE; bangPos: INT; thisVersion: INT; lastOne: BOOL _ FALSE; tryToDelete: PROC [ fullNameToDelete: ROPE] RETURNS [quit: BOOL _ FALSE] = { deleteIt: BOOL _ TRUE; 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 = { <> inner: PROC [fullFName: ROPE] RETURNS [continue: BOOL] = { deleteIt: BOOL _ TRUE; 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: ROPE _ NIL; outcome: AlpTransaction.Outcome; failureName: ROPE _ "" ; restBang: INT; versionPattern: ROPE; bangStarAndNonzeroDelVer: BOOL _ FALSE; 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 { <> 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 ANY _ NIL; srcServer, srcFile, srcDir: ROPE; user, password: ROPE; directory, restOfPattern: ROPE; keepMsg: ROPE; printedSomething: BOOL _ FALSE; 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; <> <> 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: BOOL _ FALSE, 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 = { <> 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 = { <> 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: ROPE _ NIL, password: ROPE _ NIL , displayProperties: AlpineFile.PropertySet _ AlpineFile.allProperties, d: MyData] RETURNS [LIST OF REF ANY] = { directory, restOfPattern: ROPE; resultList: LIST OF REF ANY _ NIL; 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]]; <> 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: INT _ NARROW[property, AlpineEnvironment.PropertyValuePair.byteLength].byteLength; resultRope _ Rope.Concat[resultRope, IO.PutFR["%g bytes ", IO.int[byteLength]]]; }; <> createTime => { createTime: BasicTime.GMT _ NARROW[property, AlpineEnvironment.PropertyValuePair.createTime].createTime; resultRope _ Rope.Concat[resultRope, IO.PutFR["%g ", IO.time[createTime]]]; }; <> highWaterMark => { highWaterMark: AlpineEnvironment.PageCount _ NARROW[property, AlpineEnvironment.PropertyValuePair.highWaterMark].highWaterMark; resultRope _ Rope.Concat[resultRope, IO.PutFR["HWM: %g ", IO.int[highWaterMark]]]; }; <> 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: AlpineEnvironment.OwnerName _ NARROW[property, AlpineEnvironment.PropertyValuePair.owner].owner; resultRope _ Rope.Concat[resultRope, IO.PutFR["owner: %g ", IO.rope[owner]]]; }; <> 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: ROPE _ NARROW[property, AlpineEnvironment.PropertyValuePair.stringName].stringName; resultRope _ Rope.Concat[resultRope, IO.PutFR["stringName: %g ", IO.rope[stringName]]]; }; <> version => { version: LONG INTEGER _ NARROW[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: ROPE _ NIL; outcome: AlpTransaction.Outcome; failureName: ROPE _ "" ; { [directory, restOfPattern] _ DecomposePattern[server, pattern, user]; IF trans # NIL THEN { <> 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 ANY _ NIL; d: MyData = NARROW[clientData]; server, user, file, password: ROPE; directory, restOfPattern: ROPE; printedSomething: BOOL _ FALSE; 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: ROPE _ NARROW[IF resultList = NIL THEN NIL ELSE resultList.first]; IF resultList = NIL THEN EXIT; resultList _ resultList.rest; d.out.PutF[" %g\n", IO.rope[nowRope]]; printedSomething _ TRUE ; ENDLOOP; IF NOT printedSomething THEN d.out.PutRope[" ** no files match the pattern **\n"]; EXITS badParse => {}; }; END. <<>> <> <> <<>> <> <> <<>> <> <> << Bob Hagmann July 9, 1985 >> <> <> <> <> <> <> <> <> <> <> <> <> <<>>