-- SubrImpl.Mesa, last edit March 28, 1983 4:29 pm -- General purpose procedures -- the Defs file is Subr.Mesa DIRECTORY Ascii: TYPE USING [ControlZ], CWF: TYPE USING [WFC, WFCR, WF0, WF1, WF2], DCSFileTypes: TYPE USING [tLeaderPage], Directory: TYPE USING [CreateFile, Error, GetNext, GetProperty, Handle, ignore, Lookup, PropertyType, PutProperty, UpdateDates], Environment: TYPE USING [wordsPerPage], File: TYPE USING [Capability, Permissions, SetSize], FileStream: TYPE USING [Create, GetLeaderPropertiesForCapability, Subtype], Heap: TYPE USING [systemZone], Inline: TYPE USING [BytePair, LongCOPY, LowHalf], IO: TYPE USING[CreateEditedStream, GetChar, GetSequence, Handle, Signal, UserAbort], LongString: TYPE USING [AppendChar], Rope: TYPE USING[Concat, FromChar, ROPE, Text], RopeInline: TYPE USING[InlineFlatten], Runtime: TYPE USING [GetBcdTime], Space: TYPE USING [CopyIn, Create, CreateUniformSwapUnits, Delete, Error, ForceOut, GetAttributes, GetHandle, Handle, Kill, LongPointer, Map, nullHandle, PageFromLongPointer, Unmap, virtualMemory], Stream: TYPE USING [EndOfStream, GetChar, Handle], Subr: TYPE USING [FileErrorType, NameType, PackedTime, Read, ReadWrite, TTYProcs, TTYProcsRecord, Write], System: TYPE USING [GreenwichMeanTime], UnsafeStorage: TYPE USING[FreeUZone, NewUZone], UserCredentialsUnsafe: TYPE USING[GetUserCredentials, SetUserCredentials]; SubrImpl: PROGRAM IMPORTS CWF, Directory, File, FileStream, Heap, Inline, IO, LongString, Rope, RopeInline, Runtime, Space, Stream, UnsafeStorage, UserCredentialsUnsafe EXPORTS Subr = { -- MDS USAGE !!! errorflg: PUBLIC BOOL ← FALSE; debugflg: PUBLIC BOOL ← FALSE; numberofleaders: PUBLIC CARDINAL ← 0; ncharsallocated: INT ← 0; -- zone stuff longzone: UNCOUNTED ZONE ← NIL; -- default long zone hugezone: UNCOUNTED ZONE ← NIL; -- default huge zone szprocs: UZProcs ← [alloc: SZAlloc, dealloc: SZDeAlloc]; szobject: UZObject ← [procs: @szprocs, data: NIL]; spacezonehandle: UZHandle ← @szobject; -- endof MDS USAGE !!! -- SIGNALS AbortMyself: PUBLIC SIGNAL = CODE; -- raised usually by something that calls -- Exec.CheckForAbort[] or TypeScript.UserAbort[] -- PROCS MakeTTYProcs: PUBLIC PROC[in, out: IO.Handle, data: REF ANY, Confirm: PROC[in, out: IO.Handle, data: REF ANY, msg: Rope.ROPE, dch: CHAR] RETURNS[CHAR]] RETURNS[h: Subr.TTYProcs] = { RETURN[NEW[Subr.TTYProcsRecord ← [in, out, data, Confirm]]]; }; -- long strings AllocateString: PUBLIC PROC[nchars: CARDINAL, zone: UNCOUNTED ZONE] RETURNS[s: LONG STRING] = { IF zone = NIL THEN { IF longzone = NIL THEN ERROR; zone ← longzone; }; IF zone = longzone THEN ncharsallocated ← ncharsallocated + nchars; s ← zone.NEW[StringBody[nchars] ← StringBody[length: 0, maxlength: nchars, text:]]; }; FreeString: PUBLIC PROC[str: LONG STRING, zone: UNCOUNTED ZONE] = { IF str = NIL THEN RETURN; IF zone = NIL THEN { IF longzone = NIL THEN ERROR; zone ← longzone; }; IF zone = longzone THEN ncharsallocated ← ncharsallocated - str.maxlength; zone.FREE[@str]; }; AllocateWords: PUBLIC PROC[nwords: CARDINAL] RETURNS[ptr: LONG POINTER] = { seq: TYPE = RECORD[ body: SEQUENCE maxsize: CARDINAL OF WORD ]; IF longzone = NIL THEN ERROR; IF nwords > 32000 THEN ERROR; ptr ← longzone.NEW[seq[nwords]]; }; FreeWords: PUBLIC PROC[ptr: LONG POINTER] = { IF longzone = NIL THEN ERROR; IF ptr = NIL THEN RETURN; longzone.FREE[@ptr]; }; CheckVitalSigns: PROC = { IF ncharsallocated ~= 0 THEN CWF.WF1["Schmidt's debugging: Some memory not freed (%ld bytes).\n"L, @ncharsallocated]; ncharsallocated ← 0; }; -- like Storage.CopyString CopyString: PUBLIC PROC[sold: LONG STRING, zone: UNCOUNTED ZONE] RETURNS[snew: LONG STRING] = { nchars: CARDINAL; IF sold = NIL THEN ERROR; nchars ← sold.length; IF zone = NIL THEN { IF longzone = NIL THEN ERROR; zone ← longzone; }; IF zone = longzone THEN ncharsallocated ← ncharsallocated + nchars; snew ← zone.NEW[StringBody[nchars] ← StringBody[length: nchars, maxlength: nchars, text:]]; Inline.LongCOPY[from: sold+2, nwords: (nchars+1)/2, to: snew+2]; }; strcpy: PUBLIC PROC[sto, sfrom: LONG STRING] = { i : CARDINAL; i ← MIN[sfrom.length, sto.maxlength]; Inline.LongCOPY[from: sfrom+2, nwords: (i+1)/2, to: sto+2]; sto.length ← i; }; -- can also be used if sto and sfrom point to the same StringBody SubStrCopy: PUBLIC PROC[sto, sfrom: LONG STRING, sfinx: CARDINAL] ={ i: CARDINAL ← 0; WHILE sfinx < sfrom.length AND i < sto.maxlength DO sto[i] ← sfrom[sfinx]; i ← i + 1; sfinx ← sfinx + 1; ENDLOOP; sto.length ← i; }; -- from CharIO.GetID GetID: PUBLIC PROC [in: Stream.Handle, s: LONG STRING] = { c: CHAR ← ' ; DO c ← Stream.GetChar[in ! ANY => EXIT]; IF c # ' THEN EXIT; ENDLOOP; DO IF c = ' OR c = '\n THEN EXIT; LongString.AppendChar[s, c]; c ← GetChar[in ! ANY => EXIT]; ENDLOOP; }; StripLeadingBlanks: PUBLIC PROC[str: LONG STRING] = { i: CARDINAL ← 0; WHILE i < str.length AND (str[i] = ' OR str[i] = '\t) DO i ← i + 1; ENDLOOP; SubStrCopy[str,str,i]; }; Any: PUBLIC PROC[str: LONG STRING, ch: CHAR] RETURNS[BOOL] = { FOR i: CARDINAL IN [0..str.length) DO IF str[i] = ch THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; }; EndsIn: PUBLIC PROC[str: LONG STRING, suf: LONG STRING] RETURNS[BOOL] = { FOR i: CARDINAL IN [0 .. str.length) DO { IF LowerCase[str[i]] = LowerCase[suf[0]] THEN { FOR j: CARDINAL IN [0 ..suf.length) DO IF i+j >= str.length OR LowerCase[str[i+j]] ~= LowerCase[suf[j]] THEN GOTO outer; ENDLOOP; IF i+suf.length < str.length THEN GOTO outer; RETURN[TRUE]; }; EXITS outer => NULL; }; ENDLOOP; RETURN[FALSE]; }; ControlChars: PUBLIC PROC[str: LONG STRING] RETURNS[BOOL] = { j: CARDINAL; FOR i: CARDINAL IN [0..str.length) DO j ← LOOPHOLE[str[i], CARDINAL]; IF j IN [0..32) OR j >= 177B THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; }; Prefix: PUBLIC PROC[str,pref: LONG STRING] RETURNS[BOOL] = { i: CARDINAL ← 0; WHILE i < str.length AND i <pref.length AND LowerCase[str[i]] = LowerCase[pref[i]] DO i ← i + 1; ENDLOOP; RETURN[i = pref.length]; }; -- from [Indigo]<APilot>ComSoft>Private>StringsImplB.Mesa -- only difference: INLINE LowerCase: PROC [c: CHAR] RETURNS [CHAR] = INLINE BEGIN IF c IN ['A..'Z] THEN c ← c + ('a - 'A); RETURN[c] END; PrintGreeting: PUBLIC PROC[str: LONG STRING] = { date: Subr.PackedTime ← Runtime.GetBcdTime[]; CWF.WF2["%s, Version Of %lt.\n"L, str, @date]; }; FindMappedSpace: PUBLIC PROC [space: Space.Handle] RETURNS [Space.Handle] = BEGIN mapped: BOOL; parent: Space.Handle; DO [mapped: mapped, parent: parent] ← Space.GetAttributes[space]; IF mapped THEN RETURN[space]; space ← parent; ENDLOOP; END; -- does not put a trailing CR into string "line" -- if line ends with \ then omit \ and the following CR -- append next line to last line -- returns TRUE if NOT end-of-file, FALSE if eof has occured GetLine: PUBLIC PROC[sh: Stream.Handle, line: LONG STRING] RETURNS[noteof: BOOL] = { i: CARDINAL ← 0; ch: CHAR; DO IF i >= line.maxlength THEN EXIT; ch ← GetChar[sh]; line[i] ← ch; IF ch = 0C OR ch = '\n THEN EXIT; i ← i + 1; ENDLOOP; line.length ← i; IF line.length >= line.maxlength THEN { line.length ← line.maxlength; CWF.WF1["GetLine-- line '%s' is too long.\n"L, line]; }; RETURN[NOT (ch = 0C AND line.length = 0)]; }; -- return next char in input, return 0C if EOF GetChar: PUBLIC PROC[sh: Stream.Handle] RETURNS[ch: CHAR] = { { ch ← Stream.GetChar[sh ! Stream.EndOfStream => { ch ← 0C; GOTO out } ]; IF ch = Ascii.ControlZ THEN DO -- strip bravo trailer ch ← Stream.GetChar[sh ! Stream.EndOfStream => { ch ← 0C; GOTO out } ]; IF ch = '\n OR ch = 0C THEN EXIT; ENDLOOP; EXITS out => NULL; }; RETURN[ch]; }; -- parses line, beginning at line[inx], into sub -- considers a string to be a sequence of characters -- begun by anything except blank, :, =, tab, and [ -- and terminated by blank, :, =, tab, and [ -- leading and trailing blanks are ignored -- TAB is not considered a blank GetString: PUBLIC PROC[line: LONG STRING, sub: LONG STRING, inx: CARDINAL] RETURNS[CARDINAL] = { i: CARDINAL; sub.length ← 0; WHILE inx < line.length AND line[inx] = ' DO inx ← inx + 1; ENDLOOP; IF inx >= line.length THEN RETURN[inx]; i ← 0; DO sub[i] ← line[inx]; inx ← inx + 1; i ← i + 1; IF sub[i-1] = '= OR sub[i-1] = ': THEN EXIT; -- tests for termination chars IF inx < line.length AND i < sub.maxlength AND line[inx] ~= 0C AND line[inx] ~= ' AND line[inx] ~= '\t AND line[inx] ~= ': AND line[inx] ~= '= AND line[inx] ~= '[ THEN LOOP; -- tests for begin chars EXIT; ENDLOOP; sub.length ← i; IF sub.length >= sub.maxlength THEN CWF.WF1["Substring %s not long enough\n"L, sub]; RETURN[inx]; }; -- heap management INITHEAP: CARDINAL = 30; -- # pages initially allocated to the heap DEFPAGES: CARDINAL = 256; -- size of long heap default, in pages SubrInit: PUBLIC PROC[npages: CARDINAL] = { -- space: Space.Handle; IF longzone ~= NIL THEN { CWF.WF0["Schmidt's debugging: SubrInit called twice.\n"L]; RETURN; }; errorflg ← FALSE; numberofleaders ← 0; ncharsallocated ← 0; -- space ← Space.Create[size: npages, parent: Space.virtualMemory -- ! Space.InsufficientSpace => { -- CWF.WF1["ERROR Space.InsufficientSpace[%u].\n"L, @available]; -- CWF.WF1["This program asked Pilot for %u pages, but the largest\n"L, @npages]; -- CWF.WF1["space available was %u pages. You should rollback or boot\n"L, @available]; -- CWF.WF0["and try again.\n"L]; -- SIGNAL AbortMyself; -- }]; -- longzone ← Heap.Create[initial: INITHEAP, parent: space, increment: 20]; longzone ← UnsafeStorage.NewUZone[initialSize: INITHEAP*Environment.wordsPerPage, sr: prefixed]; }; SubrStop: PUBLIC PROC = { deleteAgain: BOOL ← FALSE; IF hugezone ~= NIL THEN hugezone ← FreeHugeZone[hugezone]; IF longzone = NIL THEN RETURN; -- [parent: space] ← Heap.GetAttributes[longzone ! Heap.Error => CONTINUE]; CheckVitalSigns[]; -- Heap.Delete[z: longzone, checkEmpty: TRUE -- ! Heap.Error => { -- IF type = invalidHeap THEN { -- deleteAgain ← TRUE; -- CWF.WF0["Schmidt's debugging: Heap not empty.\n"L]; -- }; -- CONTINUE -- } -- ]; -- IF deleteAgain THEN -- Heap.Delete[z: longzone, checkEmpty: FALSE -- ! Heap.Error => CONTINUE]; -- Space.Delete[space: space ! Space.Error => CONTINUE]; UnsafeStorage.FreeUZone[longzone]; longzone ← NIL; }; LongZone: PUBLIC PROC RETURNS[UNCOUNTED ZONE] = { IF longzone = NIL THEN SubrInit[DEFPAGES]; RETURN[longzone] }; -- first the TYPEs UZHandle: TYPE = LONG POINTER TO UZObject; UZObject: TYPE = MACHINE DEPENDENT RECORD [ procs: LONG POINTER TO UZProcs, data: LONG POINTER ]; UZProcs: TYPE = MACHINE DEPENDENT RECORD [ alloc: PROC[zone: UZHandle, size: CARDINAL] RETURNS[LONG POINTER], dealloc: PROC[zone: UZHandle, object: LONG POINTER] ]; -- the HugeZone stuff HZHeader: TYPE = LONG POINTER TO HZHeaderRecord; HZHeaderRecord: TYPE = RECORD[ next: HZHeader ← NIL, -- next in list space: Space.Handle ← Space.nullHandle, -- this space current: LONG POINTER ← NIL, -- next free spot in this segment ending: LONG POINTER ← NIL, -- last valid address in this segment + 1 npages: CARDINAL ← 0 -- number of pages in this segment ]; -- now the procedures HZAlloc: PROC[zone: UZHandle, size: CARDINAL] RETURNS[lp: LONG POINTER] = { RETURN[WordsFromHugeZone[LOOPHOLE[zone], size]]; }; HZDeAlloc: PROC[zone: UZHandle, object: LONG POINTER] = { -- currently do nothing }; pagesPerChunk: CARDINAL = 32; WordsFromHugeZone: PUBLIC PROC[zone: UNCOUNTED ZONE, nwords: LONG CARDINAL] RETURNS[lp: LONG POINTER] = { hzheader: HZHeader ← LOOPHOLE[zone, UZHandle].data; IF LOOPHOLE[hzheader.current, LONG CARDINAL] + nwords > LOOPHOLE[hzheader.ending, LONG CARDINAL] THEN { oldheader: HZHeader ← hzheader; Space.ForceOut[oldheader.space]; -- avoids running out of Pilot Swap Buffers hzheader ← AllocHeader[MAX[pagesPerChunk, Inline.LowHalf[(nwords + SIZE[HZHeaderRecord])/Environment.wordsPerPage + 1]]]; hzheader.next ← oldheader; LOOPHOLE[zone, UZHandle].data ← hzheader; }; lp ← hzheader.current; hzheader.current ← hzheader.current + nwords; RETURN[lp]; }; PagesUsedInHugeZone: PUBLIC PROC[zone: UNCOUNTED ZONE] RETURNS[npages: CARDINAL] = { header: HZHeader ← LOOPHOLE[zone, UZHandle].data; npages ← 0; WHILE header ~= NIL DO npages ← npages + header.npages; header ← header.next; ENDLOOP; }; -- now the procedure that will give you one of these HugeZone: PUBLIC PROC RETURNS[UNCOUNTED ZONE] = { hzprocs: LONG POINTER TO UZProcs; hzheader: HZHeader; hugezonehandle: UZHandle; IF hugezone ~= NIL THEN RETURN[hugezone]; hzheader ← AllocHeader[1]; hzprocs ← Heap.systemZone.NEW[UZProcs ← [alloc: HZAlloc, dealloc: HZDeAlloc]]; hugezonehandle ← Heap.systemZone.NEW[UZObject ← [procs: hzprocs, data: hzheader]]; hugezone ← LOOPHOLE[hugezonehandle]; RETURN[hugezone]; }; AllocHeader: PROC[npages: CARDINAL] RETURNS[header: HZHeader] = { space: Space.Handle; nwords: LONG CARDINAL ← LONG[npages] * Environment.wordsPerPage; -- CWF.WF1["Allocating %u huge pages.\n"L, @npages]; space ← Space.Create[size: npages, parent: Space.virtualMemory]; Space.Map[space]; header ← Space.LongPointer[space]; IF npages > 32 THEN Space.CreateUniformSwapUnits[8, space]; header↑ ← [next: NIL, space: space, current: header + SIZE[HZHeaderRecord], ending: header + nwords, npages: npages]; }; -- to free the huge zone -- always returns nil FreeHugeZone: PUBLIC PROC[zone: UNCOUNTED ZONE] RETURNS[UNCOUNTED ZONE]= { space: Space.Handle; uzhandle: UZHandle ← LOOPHOLE[zone]; header: HZHeader ← uzhandle.data; uzhandle.data ← NIL; WHILE header ~= NIL DO space ← header.space; header ← header.next; Space.Delete[space: space ! Space.Error => { CWF.WF0["Schmidt's Debugging: Error freeing hugespace.\n"L]; CONTINUE; }]; ENDLOOP; Heap.systemZone.FREE[@uzhandle.procs]; Heap.systemZone.FREE[@uzhandle]; -- kludge until multiple hugezones exist hugezone ← NIL; RETURN[NIL]; }; pairsPerLine: CARDINAL = 40; -- debugging procedure DebugPrintZone: PUBLIC PROC[h: Subr.TTYProcs] = { i, j, n1, n2, nwords: CARDINAL; addr, lp: LONG POINTER TO CARDINAL; header: HZHeader ← LOOPHOLE[hugezone, UZHandle].data; WHILE header ~= NIL DO lp ← LOOPHOLE[header + SIZE[HZHeaderRecord]]; nwords ← (header.npages * Environment.wordsPerPage) - SIZE[HZHeaderRecord]; i ← 0; WHILE i < nwords DO addr ← lp + i; CWF.WF1["%06u "L, @addr]; FOR j IN [0 .. MIN[pairsPerLine, nwords - i]) DO [n1, n2] ← LOOPHOLE[(addr + j)↑, Inline.BytePair]; CWF.WFC[IF n1 < 040B OR n1 >= 0177B THEN ' ELSE LOOPHOLE[n1, CHAR]]; CWF.WFC[IF n2 < 040B OR n2 >= 0177B THEN ' ELSE LOOPHOLE[n2, CHAR]]; ENDLOOP; CWF.WFCR[]; i ← i + pairsPerLine; IF h.in.UserAbort[] THEN SIGNAL AbortMyself; ENDLOOP; header ← header.next; ENDLOOP; }; -- Space Zone stuff -- now the procedures SZAlloc: PROC[zone: UZHandle, size: CARDINAL] RETURNS[lp: LONG POINTER] = { space: Space.Handle; npages: CARDINAL ← (size/Environment.wordsPerPage) + 1; space ← Space.Create[npages, Space.virtualMemory]; Space.Map[space]; lp ← Space.LongPointer[space]; IF npages > 20 THEN Space.CreateUniformSwapUnits[10, space]; }; SZDeAlloc: PROC[zone: UZHandle, object: LONG POINTER] = { Space.Delete[FindMappedSpace[Space.GetHandle[ Space.PageFromLongPointer[object]]]]; }; -- now the procedure that will give you one of these SpaceZone: PUBLIC PROC RETURNS[UNCOUNTED ZONE] = { RETURN[LOOPHOLE[spacezonehandle]]; }; -- end of heap stuff -- if nt = login, sets Profile.userName and userPassword -- if nt = connect, sets name2, password2 GetNameandPassword: PUBLIC PROC[nt: Subr.NameType, name2, password2: LONG STRING, h: Subr.TTYProcs] = { n: STRING ← [40]; p: STRING ← [40]; IF nt = login THEN UserCredentialsUnsafe.GetUserCredentials[name: n, password: NIL] ELSE IF nt = connect THEN strcpy[n, name2]; -- CWF.WF0["Name: "L]; GetIDProc[h: h, s: n, echo: TRUE]; -- CWF.WF0[" Password: "L]; p.length ← 0; GetIDProc[h: h, s: p, echo: FALSE]; -- IF nt = login THEN UserCredentialsUnsafe.SetUserCredentials[name: n, password: p] ELSE { strcpy[name2, n]; strcpy[password2, p]; }; }; GetIDProc: PROC[h: Subr.TTYProcs, s: LONG STRING, echo: BOOL] = { input: IO.Handle ← h.in; r: Rope.ROPE; f: Rope.Text; IF NOT echo THEN input ← h.in.backingStream; IF input = NIL THEN -- in case it was not an edited stream input ← IO.CreateEditedStream[in: h.in, echoTo: h.out]; -- r ← SpecialGetLine[input]; r ← input.GetSequence[]; IF r ~= NIL THEN { f ← RopeInline.InlineFlatten[r]; strcpy[s, LOOPHOLE[f]]; }; }; SpecialGetLine: PROC[in: IO.Handle] RETURNS[r: Rope.ROPE] = { ch: CHAR; DO ch ← in.GetChar[! IO.Signal => TRUSTED{EXIT}]; IF ch = '\n THEN EXIT; r ← r.Concat[Rope.FromChar[ch]]; ENDLOOP; }; -- here because I wanted to remove reliance on PilotSSImpl and DummyFileCache FileError: PUBLIC ERROR[error: Subr.FileErrorType] = CODE; -- npages should not include the leader page; I'll add +1 NewFile: PUBLIC PROC [name: LONG STRING, access: File.Permissions, npages: CARDINAL] RETURNS [cap: File.Capability] = { old: BOOL ← FALSE; IF access ~= Subr.Read THEN cap ← Directory.CreateFile[name, DCSFileTypes.tLeaderPage, npages + 1 ! Directory.Error => { IF type = fileAlreadyExists THEN old ← TRUE ELSE ERROR FileError[notFound]; CONTINUE }] ELSE old ← TRUE; IF old THEN cap ← Directory.Lookup[fileName: name, permissions: Directory.ignore ! Directory.Error => ERROR FileError[notFound]]; cap ← Directory.UpdateDates[cap, access]; IF old AND npages > 0 AND (access = Subr.Write OR access = Subr.ReadWrite) THEN File.SetSize[cap, npages + 1]; }; NewStream: PUBLIC PROC [name: LONG STRING, access: File.Permissions] RETURNS [Stream.Handle] = { RETURN[FileStream.Create[NewFile[name, access, 0]]]; }; EnumerateDirectory: PUBLIC PROC [proc: PROC [File.Capability, STRING] RETURNS [BOOL]] = BEGIN next: STRING ← [100]; name: STRING ← [100]; cap: File.Capability; DO cap ← Directory.GetNext[pathName: "WorkDir>*"L, currentName: next, nextName: next]; StripPathName[name, next]; IF name.length = 0 THEN EXIT; IF proc[cap, name] THEN EXIT; ENDLOOP; END; StripPathName: PROC [noPath, withPath: STRING] = { pos, len: CARDINAL ← withPath.length; WHILE pos > 0 AND withPath[pos-1] # '> DO pos ← pos - 1; ENDLOOP; len ← len - pos; FOR i: CARDINAL IN [0..len) DO noPath[i] ← withPath[pos+i] ENDLOOP; noPath.length ← len; }; -- by convention, the remote name is where the file was retrieved FROM or stored ONTO RemoteNameProperty: Directory.PropertyType = LOOPHOLE[213B]; -- the maxlength of remotename MUST be 125 SetRemoteFilenameProp: PUBLIC PROC[cap: File.Capability, remotename: LONG STRING] = { -- remotename should be of the form [ivy]<directory>name!vers IF remotename.maxlength ~= 125 THEN ERROR; Directory.PutProperty[cap, RemoteNameProperty, DESCRIPTOR[remotename, SIZE[StringBody[remotename.maxlength]]], TRUE]; }; -- the maxlength of remotename MUST be 125 GetRemoteFilenameProp: PUBLIC PROC[cap: File.Capability, remotename: LONG STRING] = { mlen: CARDINAL ← remotename.maxlength; -- this is erroneous, because reading the property will set the maxlength IF mlen ~= 125 THEN ERROR; Directory.GetProperty[cap, RemoteNameProperty, DESCRIPTOR[remotename, SIZE[StringBody[mlen]]] ! Directory.Error => IF type = invalidProperty THEN GOTO leave]; LOOPHOLE[remotename+1, LONG POINTER TO CARDINAL]↑ ← mlen; -- reset the maxlength!!! EXITS leave => remotename.length ← 0; }; -- if window is NIL then gets the window that Exec.w is in CursorInWindow: PUBLIC PROC[h: Subr.TTYProcs] RETURNS[BOOL] = {RETURN[FALSE]}; -- if window is NIL, will use Exec.w CheckForModify: PUBLIC PROC[file: LONG STRING, h: Subr.TTYProcs] RETURNS[oktomodify: BOOL] = { -- short: STRING ← [100]; -- strcpy[short, file]; -- IF NOT Segments.ModifyFile[short] THEN { -- CWF.WF1["%s cannot be modified.\n"L, file]; -- CWF.WF0["Shall I go ahead and modify it"L]; -- IF Confirm['n, h] = 'y THEN { -- CWF.WF0["Yes.\n"L]; -- RETURN[TRUE]; -- } -- ELSE { -- CWF.WF0["No.\n"L]; -- RETURN[FALSE]; -- }; -- }; RETURN[TRUE]; }; GetCreateDate: PUBLIC PROC[cap: File.Capability] RETURNS[create: LONG CARDINAL] = { [create: create] ← FileStream.GetLeaderPropertiesForCapability[cap]; numberofleaders ← numberofleaders + 1; }; -- leader page -- (copied straight from [Idun]<APilot60>ComSoft>Private>FileStreamImpl.Mesa) maxLeaderNameCharacters: CARDINAL = 40; LeaderPage: TYPE = MACHINE DEPENDENT RECORD [ versionID: CARDINAL, dataType: FileStream.Subtype, create, write, read: System.GreenwichMeanTime, length: LONG CARDINAL, nameLength: CARDINAL, name: PACKED ARRAY [0..maxLeaderNameCharacters) OF CHAR ]; -- experiments with SModel Nov 81, Rubicon: -- using CopyIn is 2-5% faster than using Map -- using Map is 2-5% faster than using FileStream -- (these times include other things like parsing the DF file, -- writing the DF file, etc.) -- space is a 1-page space, and must BE MAPPED (anonymously) GetCreateDateWithSpace: PUBLIC PROC[cap: File.Capability, space: Space.Handle] RETURNS[create: LONG CARDINAL] = { leader: LONG POINTER TO LeaderPage; Space.CopyIn[space, [cap, 0]]; leader ← Space.LongPointer[space]; create ← leader.create; numberofleaders ← numberofleaders + 1; Space.Kill[space]; RETURN[create]; }; -- space is a 1-page space, and must NOT BE MAPPED OldGetCreateDateWithSpace: PUBLIC PROC[cap: File.Capability, space: Space.Handle] RETURNS[create: LONG CARDINAL] = { leader: LONG POINTER TO LeaderPage; Space.Map[space, [cap, 0]]; leader ← Space.LongPointer[space]; create ← leader.create; numberofleaders ← numberofleaders + 1; Space.Unmap[space]; RETURN[create]; }; }. Confirm: PUBLIC PROC[dch: CHAR, h: Subr.TTYProcs] RETURNS[CHAR] = { ch: CHAR; DO { ENABLE h.rubOut => LOOP; CWF.FWF0[h.putChar, "? "L]; ch ← h.getChar[]; IF ch = '\n THEN ch ← dch; ch ← LowerCase[ch]; RETURN[ch]; }; ENDLOOP; };