-- File: ClientFileTool.mesa -- Last edit by Russ Atkinson: January 19, 1983 9:44 pm -- Schmidt: 25-Jul-81 12:09:21 -- Andrew Birrell: March 9, 1983 3:09 pm -- Paul Rovner: April 12, 1983 10:36 pm -- NOTE: change the create date message when changing this beast DIRECTORY Boot USING[ LVBootFiles ], Buttons USING[ ButtonProc, Create, Destroy ], Commander USING[ CommandProc, Register ], Containers USING[ ChildXBound, ChildYBound, Create ], ConvertUnsafe USING[ AppendRope ], Directory USING [CreateFile, DeleteFile, Error, GetDefaultContext, GetNext, GetProps, GetWD, Handle, Lookup, LookupUnlimited, ModifyContext, RemoveFile], DirectoryExtras USING [ForgetVolumes], File USING [ Capability, GetAttributes, GetSize, nullCapability, nullID, PageCount, Permissions, read, SetSize], HeapString USING [AppendChar, AppendString], Inline USING [LowHalf], IO USING [PutChar, PutF, STREAM], KernelFile USING [MakeTemporary], Rope USING [Length, ROPE], RTFiles USING [IsFileInUse], Rules USING[ Create ], Space USING [ CopyOut, Create, Delete, Handle, Map, Unmap, virtualMemory], SpecialVolume USING[ GetLogicalVolumeBootFiles ], Storage USING [FreeString, String], String USING [AppendLongDecimal, AppendString], System USING [GreenwichMeanTime], TypeScript USING [Create], ViewerClasses USING [Viewer], ViewerIO USING [CreateViewerStreams], ViewerTools USING [GetContents, MakeNewTextViewer, SetContents, SetSelection], Volume USING [ Close, GetAttributes, GetLabelString, GetNext, GetType, ID, maxNameLength, nullID, PageCount, Type, TypeSet, systemID], VolumeExtras USING [OpenVolume]; ClientFileTool: CEDAR MONITOR IMPORTS Buttons, Commander, Containers, ConvertUnsafe, Directory, DirectoryExtras, File, HeapString, Inline, IO, KernelFile, Rope, RTFiles, Rules, Space, SpecialVolume, Storage, String, TypeScript, ViewerIO, ViewerTools, Volume, VolumeExtras = BEGIN OPEN HeapString; CommandType: TYPE = {get, list, delete, name, getboth}; volumeOpened: BOOLEAN _ FALSE; -- some slight protection here -- Commands Invoked from Menu GetFileFromClientVolume: Buttons.ButtonProc = { DoCommand[clientData, get]}; DeleteFilesFromClientVolume: Buttons.ButtonProc = { DoCommand[clientData, delete]}; ListFilesOnClientVolume: Buttons.ButtonProc = { DoCommand[clientData, list]}; NameFilesOnClientVolume: Buttons.ButtonProc = { DoCommand[clientData, name]}; GetBothFilesOnClientVolume: Buttons.ButtonProc = { DoCommand[clientData, getboth]}; Stop: Buttons.ButtonProc = { data: MyData = NARROW[clientData]; data.stopWanted _ TRUE }; DoCommand: PROC [clientData: REF ANY, command: CommandType] = TRUSTED { OPEN HeapString; data: MyData = NARROW[clientData]; cpText: Rope.ROPE = ViewerTools.GetContents[data.cpText]; localText: Rope.ROPE = ViewerTools.GetContents[data.localText]; cpName: STRING _ Storage.String[cpText.Length[]]; localName: STRING _ Storage.String[localText.Length[]]; DisplayMsg: PROCEDURE [s1, s2: STRING _ NIL] = TRUSTED { data.out.PutF["\n%g%g", [string[s1]], [string[s2]] ] }; volumeLabel: STRING _ "Client"L; fileName, pathName, workingDir, scratch: STRING _ NIL; oldContext: Directory.Handle _ Directory.GetDefaultContext[]; fileCount, pageCount: LONG CARDINAL _ 0; useLocal: BOOL _ FALSE; ConvertUnsafe.AppendRope[cpName, cpText]; ConvertUnsafe.AppendRope[localName, localText]; IF volumeOpened THEN {DisplayMsg["Client volume already opened!"L]; RETURN}; FindClientVolume[]; IF clientVolume = Volume.nullID THEN {DisplayMsg["Client volume not found!"L]; RETURN}; fileName _ Storage.String[60]; pathName _ Storage.String[60]; workingDir _ Storage.String[60]; scratch _ Storage.String[60]; Directory.GetWD[oldContext, workingDir]; AppendChar[@pathName, '<]; AppendString[@pathName, clientLabel]; AppendString[@pathName, ">SysDir>"L]; IF cpName # NIL THEN AppendString[@fileName, cpName]; IF command = get AND localName # NIL AND localName.length > 0 THEN useLocal _ TRUE; {single: BOOL _ SingleFilePattern[fileName]; nextName: STRING _ [80]; pathNameLen: NAT _ pathName.length; triedBcd: BOOL _ FALSE; triedMesa: BOOL _ FALSE; IF useLocal THEN {IF NOT single THEN {DisplayMsg["Sorry, client patterns require blank local name."L]; GO TO out}; IF NOT SingleFilePattern[localName] THEN {DisplayMsg["Sorry, patterns not allowed in local name."L]; GO TO out}}; VolumeExtras.OpenVolume [volume: clientVolume, readOnly: command # delete ! ANY => {DisplayMsg["Client volume could not be opened!"L]; GO TO out}]; volumeOpened _ TRUE; Directory.ModifyContext[oldContext, pathName]; DisplayMsg[""L]; {root: File.Capability; volSize,freePages: Volume.PageCount; [volSize, freePages, root] _ Volume.GetAttributes[clientVolume]; data.out.PutF["\nClient volume has %g total pages, %g free pages", [cardinal[volSize]], [cardinal[freePages]] ]}; IF fileName.length = 0 THEN {DisplayMsg["No client file name."L]; GO TO out}; DO localFile: File.Capability _ File.nullCapability; pages: File.PageCount _ 0; cap: File.Capability _ File.nullCapability; lagName: STRING _ [80]; IF data.stopWanted THEN { data.stopWanted _ FALSE; DisplayMsg["!!Aborted!!"]; GO TO out; }; IF command = delete THEN String.AppendString[lagName, nextName]; IF single THEN {nextName.length _ 0; String.AppendString[nextName, pathName]; String.AppendString[nextName, fileName]; SELECT FALSE FROM command = getboth => {}; triedBcd => {String.AppendString[nextName, ".bcd"]; triedBcd _ TRUE}; triedMesa => {String.AppendString[nextName, ".mesa"]; triedMesa _ TRUE}; ENDCASE => EXIT; cap _ Directory.Lookup [fileName: nextName, permissions: File.read ! Directory.Error --[type]-- => {IF type # fileNotFound THEN DisplayMsg[nextName, " has a problem!"L] ELSE DisplayMsg[nextName, " could not be found!"L]; EXIT}]} ELSE cap _ Directory.GetNext [pathName, nextName, nextName ! ANY => CONTINUE]; IF nextName.length = 0 OR cap = File.nullCapability THEN EXIT; IF NOT Match[fileName, 0, nextName, pathNameLen, command = getboth] THEN LOOP; pages _ File.GetSize[cap]; SELECT command FROM get, getboth => {scratch.length _ 0; AppendString[@scratch, workingDir]; IF pages > 8191 THEN {DisplayMsg[nextName, " is too big!"L]; LOOP}; IF NOT useLocal THEN {-- skip over path name to get local name pos: NAT _ pathNameLen; localName.length _ 0; FOR pos: NAT IN [pathNameLen..nextName.length) DO AppendChar[@localName, nextName[pos]]; ENDLOOP}; AppendString[@scratch, localName]; localFile _ Directory.LookupUnlimited [fileName: scratch ! Directory.Error --[type]-- => {IF type # fileNotFound THEN {DisplayMsg[localName, " has a problem"L]; LOOP}; CONTINUE}]; IF localFile # File.nullCapability AND RTFiles.IsFileInUse[localFile] THEN BEGIN DisplayMsg[scratch, " was in use: making old version temporary"L]; Directory.RemoveFile[scratch, localFile]; KernelFile.MakeTemporary[localFile]; localFile _ File.nullCapability; END; IF localFile = File.nullCapability THEN localFile _ Directory.CreateFile [fileName: scratch, fileType: File.GetAttributes[cap].type, size: pages ! ANY => {DisplayMsg[fileName, " can't be created locally"L]; LOOP}] ELSE File.SetSize[localFile, pages]; DisplayMsg["Copy "L, nextName]; {chunk: CARDINAL _ Inline.LowHalf[pages]; space: Space.Handle; base: File.PageCount _ 0; IF chunk > 64 THEN chunk _ 64; IF chunk > 0 THEN space _ Space.Create [chunk, Space.virtualMemory ! ANY => {DisplayMsg ["Could not create VM space for "L, nextName]; LOOP}]; WHILE base < pages DO -- loop to transfer file (avoid LARGE spaces) Space.Map[space, [cap, base]]; Space.CopyOut[space, [localFile, base]]; Space.Unmap[space]; base _ base + chunk; ENDLOOP; Space.Delete[space]}; DisplayMsg[" to "L, scratch]; }; delete => {Directory.DeleteFile [nextName ! ANY => {DisplayMsg[nextName, " could not be deleted!"L]; LOOP}]; DisplayMsg[nextName, " deleted."L]; nextName.length _ 0; String.AppendString[nextName, lagName]; }; list => {readDate, writeDate, createDate: System.GreenwichMeanTime; byteLength: LONG CARDINAL; parent: File.Capability; [readDate, writeDate, createDate, byteLength, parent] _ Directory.GetProps [cap, lagName ! ANY => {DisplayMsg[nextName, " ???"L]; LOOP}]; DisplayMsg[lagName, " "L]; {size: STRING _ [12]; lim: NAT _ lagName.length + 1; String.AppendLongDecimal[size, byteLength]; lim _ lim + size.length; THROUGH (lim..36] DO data.out.PutChar[' ] ENDLOOP; data.out.PutF["%g", [string[size]]]; size.length _ 0; String.AppendLongDecimal[size, pages]; THROUGH (size.length..5] DO data.out.PutChar[' ] ENDLOOP; data.out.PutF["%g", [string[size]]]; }; data.out.PutF["p %g", [time[createDate]] ]; }; name => {IF fileCount = 0 THEN data.out.PutChar['\n]; data.out.PutChar[' ]; FOR i: NAT IN [pathNameLen..nextName.length) DO data.out.PutChar[nextName[i]]; ENDLOOP; }; ENDCASE => ERROR; fileCount _ fileCount + 1; pageCount _ pageCount + pages; IF single AND command # getboth THEN EXIT; ENDLOOP; data.stopWanted _ FALSE; EXITS out => { data.stopWanted _ FALSE}}; IF volumeOpened THEN { IF workingDir # NIL AND workingDir.length > 0 THEN { Directory.ModifyContext[oldContext, workingDir]}; DirectoryExtras.ForgetVolumes[]; Volume.Close[clientVolume]; volumeOpened _ FALSE}; Storage.FreeString[fileName]; Storage.FreeString[pathName]; Storage.FreeString[localName]; Storage.FreeString[workingDir]; Storage.FreeString[scratch]; data.out.PutF["\n%g %g files, %g pages", [rope[SELECT command FROM get, getboth => "Copied", delete => "Deleted", list => "Listed", name => "Named", ENDCASE => ERROR]], [cardinal[fileCount]], [cardinal[pageCount]] ]; }; SingleFilePattern: PROC [pattern: STRING] RETURNS [BOOLEAN] = TRUSTED { patlen: NAT = IF pattern = NIL THEN 0 ELSE pattern.length; pos: NAT _ 0; FOR pos IN [pos..patlen) DO c: CHAR = pattern[pos]; IF c = '* OR c = '# OR c = ' THEN RETURN [FALSE]; ENDLOOP; RETURN [TRUE]}; Match: PROC [pattern: STRING, patpos: NAT, object: STRING, objpos: NAT, isGetBoth: BOOL] RETURNS [BOOL] = TRUSTED { -- return TRUE if the object matches the pattern, -- FALSE if it does not match. The pattern may contain -- * characters which will match 0 or more characters -- in the object. Case does not matter. -- If isGetBoth, then .bcd and .mesa extensions also match. -- Blanks in the pattern separate alternate matches submatch: PROC [i1: NAT, len1: INTEGER, i2: NAT, len2: INTEGER, isGetBoth: BOOLEAN] RETURNS [BOOLEAN] = TRUSTED { DO c1: CHAR _ IF len1 > 0 THEN Lower[pattern[i1]] ELSE ' ; IF c1 = ' THEN {IF len2 = 0 THEN RETURN [TRUE]; IF NOT isGetBoth THEN RETURN [FALSE]; RETURN [Match[".mesa"L, 0, object, i2, FALSE] OR Match[".bcd"L, 0, object, i2, FALSE]]}; IF c1 = '* THEN { -- quick kill for * at end of pattern IF len1 = 1 THEN RETURN [TRUE]; -- else must take all combinations {j1: NAT _ i1 + 1; nlen1: INTEGER _ len1 - 1; j2: NAT _ i2; nlen2: INTEGER _ len2; WHILE nlen2 >= 0 DO IF submatch[j1, nlen1, j2, nlen2, isGetBoth] THEN RETURN [TRUE]; j2 _ j2 + 1; nlen2 _ nlen2 - 1; ENDLOOP}; RETURN [FALSE]; }; IF len2 <= 0 THEN RETURN [FALSE]; -- at this point demand an exact match in both strings {c2: CHAR _ Lower[object[i2]]; IF c1 # c2 AND c1 # '# THEN -- mismatch, so reject this alternative RETURN [FALSE]; }; i1 _ i1 + 1; len1 _ len1 - 1; i2 _ i2 + 1; len2 _ len2 - 1; ENDLOOP; }; -- submatch patlen: CARDINAL = IF pattern = NIL THEN 0 ELSE pattern.length; objlen: CARDINAL = IF object = NIL THEN 0 ELSE object.length; DO -- cycle through all of the alternatives in the pattern WHILE patpos < patlen AND pattern[patpos] = ' DO -- skip over blanks patpos _ patpos + 1; ENDLOOP; IF patpos >= patlen THEN RETURN [FALSE]; IF submatch[patpos, patlen-patpos, objpos, objlen-objpos, isGetBoth] THEN RETURN [TRUE]; WHILE patpos < patlen AND pattern[patpos] # ' DO -- skip over non-blanks patpos _ patpos + 1; ENDLOOP; ENDLOOP; }; Lower: PROC [c: CHAR] RETURNS [CHAR] = INLINE { IF c IN ['A..'Z] THEN RETURN['a + (c-'A)] ELSE RETURN[c]}; -- Initialization and Window Management MyData: TYPE = REF MyDataRec; MyDataRec: TYPE = RECORD[ out: IO.STREAM, cpText, localText: ViewerClasses.Viewer, stopWanted: BOOL _ FALSE]; Create: Commander.CommandProc = TRUSTED { --PROC [cmd: Handle]; maxW: INTEGER; v: ViewerClasses.Viewer = Containers.Create[ info: [name: "ClientFileTool", column: right, scrollable: FALSE, iconic: FALSE]]; data: MyData = NEW[MyDataRec]; child: ViewerClasses.Viewer _ NIL; x: INTEGER _ 2; y: INTEGER _ 0; CommandButton: PROC [name: Rope.ROPE, proc: Buttons.ButtonProc, newline: BOOL] = TRUSTED { child _ Buttons.Create[ info: [name: name, parent: v, border: TRUE, wy: y, wx: x, ww: maxW], proc: proc, clientData: data, fork: TRUE, paint: TRUE]; x _ IF newline THEN 2 ELSE child.wx + maxW + 2; y _ IF newline THEN child.wy + child.wh + 2 ELSE child.wy; }; LabelText: PROC[name, data: Rope.ROPE] RETURNS[ViewerClasses.Viewer] = TRUSTED { child _ ViewerTools.MakeNewTextViewer[ info: [parent: v, ww: 999, wh: child.wh, scrollable: FALSE, data: data, border: FALSE, wx: x + maxW + 2, wy: y], paint: TRUE ]; Containers.ChildXBound[v, child]; [] _ Buttons.Create[ info: [name: name, parent: v, border: FALSE, wx: x, wy: y], proc: TextLabelProc, clientData: child, fork: FALSE, paint: TRUE]; x _ 2; y _ child.wy + child.wh + 2; RETURN[child] }; Rule: PROC = TRUSTED { child _ Rules.Create[ info: [parent: v, border: FALSE, wy: y, wx: 0, ww: v.ww, wh: 1], paint: TRUE ]; Containers.ChildXBound[v, child]; x _ 2; y _ child.wy + child.wh + 2; }; { -- kludge to find max button size! -- temp: ViewerClasses.Viewer = Buttons.Create[ info: [name: "ClientFile:", parent: v, border: FALSE, wx: 0, wy: 0], proc: NIL, clientData: NIL, fork: FALSE, paint: FALSE]; maxW _ temp.ww; Buttons.Destroy[temp]; }; CommandButton["Stop", Stop, FALSE]; CommandButton["Get", GetFileFromClientVolume, FALSE]; CommandButton["GetBoth", GetBothFilesOnClientVolume, FALSE]; CommandButton["List", ListFilesOnClientVolume, FALSE]; CommandButton["Names", NameFilesOnClientVolume, FALSE]; CommandButton["Delete", DeleteFilesFromClientVolume, TRUE]; data.cpText _ LabelText["ClientFile:", NIL]; data.localText _ LabelText["LocalFile:", NIL]; Rule[]; child _ TypeScript.Create[ info: [parent: v, wh: v.ch - y, ww: v.cw, border: FALSE, wy: y, wx: x] ]; data.out _ ViewerIO.CreateViewerStreams[NIL, child].out; Containers.ChildXBound[v, child]; Containers.ChildYBound[v, child]; }; TextLabelProc: Buttons.ButtonProc = -- parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL BEGIN text: ViewerClasses.Viewer = NARROW[clientData]; SELECT mouseButton FROM red => ViewerTools.SetSelection[text, NIL]; blue => { ViewerTools.SetContents[text, NIL]; ViewerTools.SetSelection[text, NIL] }; yellow => NULL; ENDCASE => ERROR; END; -- copied for now (will get exported from CascadeExec) clientVolume: Volume.ID _ Volume.nullID; clientLabel: STRING = [Volume.maxNameLength]; FindClientVolume: ENTRY PROCEDURE = TRUSTED { myType: Volume.Type = Volume.GetType[Volume.systemID]; clientType: Volume.TypeSet _ []; IF clientVolume # Volume.nullID THEN RETURN; IF myType = FIRST[Volume.Type] THEN RETURN; clientType[PRED[myType]] _ TRUE; UNTIL (clientVolume _ Volume.GetNext[clientVolume, clientType]) = Volume.nullID DO bootFiles: Boot.LVBootFiles; SpecialVolume.GetLogicalVolumeBootFiles[clientVolume, @bootFiles]; IF bootFiles[pilot].fID ~= File.nullID AND Volume.GetAttributes[clientVolume].volumeSize > 1000 -- to ignore othello, booter! -- THEN { Volume.GetLabelString[clientVolume, clientLabel]; EXIT }; ENDLOOP; }; -- Initialization Commander.Register[key: "ClientFileTool", proc: Create, doc: "Access to files on the client volume"]; END. ÊŘJšÄÏc³œÏk œžœžœ-žœ(žœ6žœžœ¦žœžœ€žœ&žœžœžœžœžœžœ žœ žœžœžœTžœ)žœ žœ-žœ#žœžœžœ&žœGžœ?žœQžœ"žœžœžœjžœŒžœžœžœ:žœžœœœéžœ žœÏn œžœžœžœžœžœ žœžœ=žœ8žœ3žœ+Ÿ œž œ žœžœžœRžœ;žœžœ^žœžœžœžœhžœžœ7žœžœžœ2žœ¶žœ žœžœ&žœžœ žœžœžœ žœžœ/žœžœ#žœžœžœžœžœ žœ žœžœžœ\žœžœžœžœžœVžœžœhžœ8žœžœžœîžœžœ/žœžœ žœ£žœžœžœ žœ3žœžœžœžœ1žœžœ žœžœžœµžœ}žœžœžœ† œžœ,žœAžœDžœžœgžœžœ žœžœžœžœ žœžœ>žœžœ6žœ žœzžœžœ9žœžœžœžœ)œžœRžœžœžœ žœ\žœ½ œžœžœDžœžœžœ!žœ-žœžœŽžœžœ1žœÑžœQžœžœcžœsžœ žœžœ žœlžœ}žœžœžœ.œÁžœÃžœIžœžœžœÉžœ(žœ@žœžœ†žœ žœžœ—žœžœžœ®žœžœIžœžœžœ žœ>žœžœžœUžœžœžœžœ žœžœžœ$žœžœžœ žœžœžœžœœžœážœ žœ{žœžœJŸœžœ žœžœžœžœžœžœ žœžœžœžœ žœžœžœ žœžœžœžœžœžœžœ žœžœžœ Ÿœžœ žœ žœ žœ žœžœžœžœžœ2œ8œ6œ)œ<œ4œžœžœžœžœžœ žœ žœžœžœ žœ žœžœ žœžœ žœ žœ žœ žœžœžœžœžœ žœžœžœžœ!žœžœžœ žœ žœ&œ žœ žœžœžœ#œžœžœžœžœžœ žœžœ+žœžœžœPžœžœžœžœ žœžœžœ 7œ žœžœ žœ žœ (œ žœžœsžœ œ žœžœ žœžœžœžœžœ žœžœžœžœ8œžœžœžœ œ%žœžœžœžœžœ žœLžœžœžœ žœžœžœ œ%žœžœ Ÿœžœžœžœžœžœžœžœžœžœžœžœ (œ žœžœžœžœ žœžœ?žœžœ*žœœ žœtžœ žœžœ/žœ žœ žœ Ÿ œžœžœ%žœžœOžœfžœžœ žœ žœžœ žœ žœžœŸ œžœžœžœžœnžœ2žœ:žœˆžœIžœ žœ9žœŸœžœžœAžœ>žœbžœžœ%œmžœ2žœžœžœ žœ:žœ"žœ5žœ<žœ6žœ7žœ<žœ.žœ0žœwžœNžœdÏb œXœžœ žœžœ žœ+žœ/žœ"žœžœžœžœžœ8œžœ!žœŸœžœž œžœgžœžœžœžœ žœžœžœžœ žœžœKžœsžœ+žœ6 œžœ5žœ žœ œužœ˜›˜—…—LSé