-- File: WFileToolDir.mesa -- last edit, Stewart, July 4, 1982 5:29 pm. DIRECTORY Directory USING [ DeleteFile, Error, GetNext, ignore, Lookup, PropertyType, PutProperty, RemoveFile, VolumeError], DirectoryExtras USING [ForgetVolumes], File USING [Capability, LimitPermissions, Unknown], IO USING [PutF, string], KernelFile USING [MakeTemporary], RTFiles USING [IsFileInUse], Segments USING [DestroyFile, FHandle, FP, FPHandle, InsertFile, LockFile, NewFile, NullFP, ReleasableFile, ReleaseFile, SetFileTimes, UnlockFile, Write], Space USING [Handle, Create, Kill, LongPointer, Map, virtualMemory], STP USING [defaultOptions], Stream USING [ Block, CompletionCode, EndOfStream, GetProcedure, Handle, SubSequenceType], Streams USING [ CreateStream, Destroy, Handle, PutBlock, PutByte], String USING [AppendChar, AppendString, UpperCase], Time USING [Packed], VFTOps USING [ FreeBufferPages, GetBufferPages, Handle, InvertIndicator, PostComment, Task], Volume USING [ Close, ID, InsufficientSpace, nullID], VolumeExtras USING [OpenVolume]; WFileToolDir: PROGRAM IMPORTS Directory, DirectoryExtras, File, IO, KernelFile, RTFiles, Segments, Space, Stream, Streams, String, VFTOps, Volume, VolumeExtras EXPORTS VFTOps SHARES Segments = BEGIN -- Also in VFileSupport PageSize: CARDINAL = 256; Byte: TYPE = [0..377B]; bufPages: CARDINAL = 6; -- also in VFileSUpportB wordsInBuffer: CARDINAL = bufPages*PageSize; bytesInBuffer: CARDINAL = wordsInBuffer*2; Buffer: TYPE = PACKED ARRAY [0..bytesInBuffer) OF Byte; WordBuffer: TYPE = PACKED ARRAY [0..wordsInBuffer) OF WORD; -- by convention, the remote name is where the file was --retrieved FROM or stored ONTO RemoteNameProperty: Directory.PropertyType = LOOPHOLE[213B]; wildString: CHARACTER = '*; wildChar: CHARACTER = '#; WriteDiskForRetrieve: PUBLIC PROCEDURE [ self: VFTOps.Handle, from: Stream.Handle, name: STRING, fpHint: Segments.FPHandle, create: Time.Packed, remoteName: LONG STRING, t: VFTOps.Task] RETURNS [bytes: LONG CARDINAL] = BEGIN buffer: LONG POINTER TO Buffer = VFTOps.GetBufferPages[self, bufPages]; block: Stream.Block _ [buffer, 0, bytesInBuffer]; bytesTransferred: CARDINAL; why: Stream.CompletionCode; fh: Segments.FHandle; savedSST: Stream.SubSequenceType; to: Streams.Handle _ NIL; remotePropertySet: BOOL _ FALSE; bytes _ 0; DO ENABLE UNWIND => { VFTOps.FreeBufferPages[self, buffer]; IF to # NIL THEN {OPEN Segments; LockFile[fh]; Streams.Destroy[to]; UnlockFile[fh]; DestroyFile[fh]}}; block.startIndex _ 0; block.stopIndexPlusOne _ bytesInBuffer; [bytesTransferred, why, savedSST] _ from.get[ from, block, STP.defaultOptions ! Stream.EndOfStream => { why _ endOfStream; bytesTransferred _ nextIndex; CONTINUE}]; block.stopIndexPlusOne _ bytesTransferred; bytes _ bytes + bytesTransferred; VFTOps.InvertIndicator[self]; IF to = NIL THEN {OPEN Segments; fh _ IF fpHint^ = NullFP THEN NewFile[name, Write] ELSE InsertFile[fpHint]; to _ Streams.CreateStream[fh, Write]}; IF NOT remotePropertySet THEN { cap: File.Capability _ File.LimitPermissions[originalFC: fh.cap, maxPermissions: Directory.ignore]; remotePath: STRING _ [125]; remotePropertySet _ TRUE; String.AppendChar[s: remotePath, c: '[]; String.AppendString[to: remotePath, from: t.host]; String.AppendChar[s: remotePath, c: ']]; FOR i: NAT IN [0..remoteName.length) DO String.AppendChar[s: remotePath, c: remoteName[i]]; ENDLOOP; -- debugging --self.log.PutF["rfp: %s\n", IO.string[remotePath]]; -- WARNING THIS CHANGES THE CREATE DATE if the capability's permissions --are not Directory.ignore -- remotename should be of the form [ivy]name!vers Directory.PutProperty[cap, RemoteNameProperty, DESCRIPTOR[remotePath, SIZE[StringBody[remotePath.maxlength]]], TRUE]; }; [] _ Streams.PutBlock[to, buffer, bytesTransferred/2]; IF bytesTransferred MOD 2 # 0 THEN Streams.PutByte[to, buffer[block.startIndex+bytesTransferred-1]]; IF why = endOfStream THEN EXIT; ENDLOOP; Segments.LockFile[fh]; Streams.Destroy[to]; Segments.SetFileTimes[file: fh, create: create]; Segments.UnlockFile[fh]; IF Segments.ReleasableFile[fh] THEN Segments.ReleaseFile[fh]; VFTOps.FreeBufferPages[self, buffer]; RETURN END; NoMoreRoom: PUBLIC PROC RETURNS [ERROR] = {RETURN[Volume.InsufficientSpace]}; DestroyFile: PUBLIC PROC [self: VFTOps.Handle, fp: Segments.FPHandle, name: STRING] = { badFile: File.Capability; failed: BOOLEAN _ FALSE; IF RTFiles.IsFileInUse[fp^] THEN { self.log.PutF["%s is in use. Removing from directory and making temporary.\r", IO.string[name]]; Directory.RemoveFile[fileName: name, file: fp^]; KernelFile.MakeTemporary[fp^]; } ELSE Directory.DeleteFile[name ! File.Unknown => {badFile _ file; failed _ TRUE; CONTINUE}]; IF failed THEN { self.log.PutF["File.Unknown; will remove directory entry... \n"]; Directory.RemoveFile[name, badFile]; }; }; GetBufferPages: PUBLIC PROC [self: VFTOps.Handle, pages: CARDINAL] RETURNS [buf: LONG POINTER] = { IF self.bufAllocated THEN ERROR; IF self.buffer = NIL THEN { OPEN Space; self.bufSpace _ Create[size: self.bufSize _ pages, parent: virtualMemory]; Map[self.bufSpace]; self.buffer _ LongPointer[self.bufSpace]; }; IF pages # self.bufSize THEN ERROR; self.bufAllocated _ TRUE; buf _ self.buffer }; FreeBufferPages: PUBLIC PROC [self: VFTOps.Handle, buf: LONG POINTER] = { IF ~self.bufAllocated OR buf # self.buffer THEN ERROR; self.bufAllocated _ FALSE; Space.Kill[self.bufSpace]; }; FileExists: PUBLIC PROC [self: VFTOps.Handle, name: STRING, cap: Segments.FPHandle] RETURNS [yes: BOOLEAN] = BEGIN openedVolume: Volume.ID _ Volume.nullID; Fail: PROC = {yes _ FALSE; cap^ _ Segments.NullFP}; yes _ TRUE; cap^ _ Directory.Lookup[fileName: name, permissions: Directory.ignore ! Directory.Error => SELECT type FROM fileNotFound, volumeNotFound => {Fail[]; CONTINUE}; ENDCASE; Directory.VolumeError => SELECT type FROM directoryNeedsScavenging => { VFTOps.PostComment[self, "Directory Needs Scavenging"]; Fail[]; CONTINUE}; rootNotFound => { VFTOps.PostComment[self, "Root Not Found"]; Fail[]; CONTINUE}; volumeNotOpen => {VolumeExtras.OpenVolume[volume: volume, readOnly: TRUE]; openedVolume _ volume; RETRY}; ENDCASE]; IF openedVolume # Volume.nullID THEN {Volume.Close[openedVolume]; DirectoryExtras.ForgetVolumes[]}; END; Enumerate: PUBLIC PROC [ self: VFTOps.Handle, files: STRING, proc: PROC [fp: Segments.FPHandle, file: STRING] RETURNS [BOOLEAN], wantWholeName: BOOLEAN ] = BEGIN PreProcessFile: PROC [fp: Segments.FPHandle, file: STRING] RETURNS [BOOLEAN] = { IF MaskFilename[file, 0, files, 0] THEN RETURN[proc[fp, file]]; RETURN[FALSE]}; MaskFilename: PROC [file: STRING, fileIndex: CARDINAL, mask: STRING, maskIndex: CARDINAL] RETURNS [outcome: BOOLEAN] = BEGIN -- local variables i, j: CARDINAL; -- process each character in mask FOR i IN [maskIndex..mask.length) DO SELECT mask[i] FROM wildString => -- matches any string of zero or more characters BEGIN FOR j IN [fileIndex..file.length] DO IF MaskFilename[file, j, mask, i+1] THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; END; wildChar => -- matches any single character IF fileIndex = file.length THEN RETURN[FALSE] ELSE fileIndex _ fileIndex + 1; ENDCASE => IF fileIndex = file.length OR String.UpperCase[file[fileIndex]] # String.UpperCase[mask[i]] THEN RETURN[FALSE] ELSE fileIndex _ fileIndex + 1; ENDLOOP; -- filename passes mask if entire filename has been consumed outcome _ fileIndex = file.length; END; curName: STRING _ [100]; nextName: STRING _ [100]; dirName: STRING _ [100]; filePart: STRING _ [40]; passedOutName: STRING _ [40]; fp: Segments.FP; FOR i: CARDINAL IN [0..files.length) DO IF files[i] = '> THEN { String.AppendString[dirName, filePart]; String.AppendChar[dirName, '>]; filePart.length _ 0} ELSE String.AppendChar[filePart, files[i]]; ENDLOOP; IF filePart.length = 0 THEN String.AppendChar[filePart, '*]; FOR i: CARDINAL IN [0..filePart.length) DO IF filePart[i] = wildString OR filePart[i] = wildChar THEN EXIT REPEAT FINISHED => { IF FileExists[self, files, @fp] THEN [] _ proc[@fp, IF wantWholeName THEN files ELSE filePart]; RETURN}; ENDLOOP; IF dirName.length = 0 THEN String.AppendChar[dirName, '*]; DO fp _ Directory.GetNext[dirName, curName, nextName ! Directory.Error => CONTINUE]; IF nextName.length = 0 THEN EXIT; passedOutName.length _ 0; FOR i: CARDINAL IN [0..nextName.length) DO IF nextName[i] = '> THEN passedOutName.length _ 0 ELSE String.AppendChar[passedOutName, nextName[i]]; ENDLOOP; IF MaskFilename[passedOutName, 0, filePart, 0] THEN { IF proc[@fp, IF wantWholeName THEN nextName ELSE passedOutName] THEN RETURN}; curName.length _ 0; String.AppendString[curName, nextName]; nextName.length _ 0; ENDLOOP; END; END. -- of FileToolDir -- Smokey: Oct 13, 1980 12:39 PM -- Karlton: 11-Dec-80 15:38:39 -- Mark: 12-Mar-81 19:50:44 16-Jan-82 15:57:24, Stewart, created from FileToolDir.mesa 25-Jan-82 23:17:49, Stewart, objects 27-Jan-82 19:12:59 Stewart, removed SetWorkingDir March 28, 1982 4:21 pm, Stewart, Cedar 3.0 cleanup July 4, 1982 5:29 pm, Stewart, RTFiles Κ– "Mesa" style˜IprocšθΟcJœΟk œ žœ~žœžœ,žœžœžœžœžœžœ~žœ<žœžœžœažœCžœ/žœžœbžœžœ-žœ žœžœ'žœdžœ žœžœœ žœžœžœœžœ'žœžœžœžœžœžœžœžœžœžœ8œ œ0žœž œž œ Οnœžœž œ7žœ>žœžœžœ žœžœžœ žœžœžœ}žœ{žœžœžœžœžœžœ.žœžœžœžœΰžœ_žœ‡žœžœžœžœžœžœžœCžœžœžœ|žœ&žœ‹žœžœžœžœ;žœ œ5œHœœ>œ:ž œ žœ%žœQžœžœžœJžœžœžœžœŒžœžœIžœžœŸ œžœžœžœžœžœ"Ÿ œžœžœ4žœ0žœžœžœΓžœžœžœžœŸœžœžœžœžœžœžœ žœžœžœžœžœžœ žœ°žœžœžœžœ&Ÿœžœžœžœžœ žœžœžœžœžœ-Ÿ œžœžœžœžœžœžœžœŸœžœ žœ%žœižœžœ-žœžœ"žœžœgžœQžœOžœžœžœžœžœJžœŸ œžœžœ'žœ žœžœžœžœžœ žœŸœžœžœžœžœžœ!žœžœžœžœ Ÿ œžœžœ žœžœžœžœ žœžœœ žœ"œžœžœžœžœ žœ1œžœžœžœžœžœ"žœžœžœžœžœžœžœ œžœžœžœžœžœ#žœžœžœCžœžœžœžœ!žœ=œ'žœžœžœžœžœžœžœžœžœžœžœžœžœižœ.žœžœžœ&žœžœžœžœžœžœžœžœžœžœžœžœžœžœžœ žœ žœžœžœ%žœNžœ žœžœžœ(žœžœžœžœžœžœžœ1žœžœ-žœžœ žœžœ žœžœžœlžœžœžœsœλ˜ŒM—…—&Ž+£