DIRECTORY Basics USING [charsPerWord], RefText USING [AppendChar, AppendRope, New], Rope USING [Length, ROPE], SymTab USING [Create, Delete, EachPairAction, Fetch, Insert, Pairs, Ref], YggDummyProcess USING [MsecToTicks, Pause, SetTimeout], YggEnvironment USING [bytesPerPage], YggFile USING [FileHandleRep, PageCount, PageNumber], YggInternal USING [FileHandle]; YggUnixFileImpl: CEDAR MONITOR IMPORTS RefText, Rope, SymTab, YggDummyProcess, YggEnvironment EXPORTS YggInternal, YggFile ~ BEGIN ROPE: TYPE ~ Rope.ROPE; OpenFiles: SymTab.Ref _ NIL; FileHandle: PUBLIC TYPE ~ YggInternal.FileHandle; FileHandleRep: PUBLIC TYPE ~ YggFile.FileHandleRep; AllNulls: PACKED ARRAY [0..Basics.charsPerWord) OF CHAR _ ALL[0C]; Open: PUBLIC PROC [name: Rope.ROPE] RETURNS [FileHandle] = { entry: FileHandle _ NIL; entry _ RememberOpenFile[name]; RETURN[entry]; }; Create: PUBLIC PROC [name: Rope.ROPE, size: YggFile.PageCount ] RETURNS [FileHandle _ NIL] = { unixName: REF TEXT; entry: FileHandle _ NIL; fd: INT _ 0; entry _ RememberOpenFile[name]; allocateFD[entry, FALSE]; -- try (somewhat) to make sure that there are enough fd's unixName _ UnixStringFromRope[name]; fd _ UCreat[LOOPHOLE[unixName, CARD]+UNITS[TEXT[0]]]; entry.fd _ fd; deAllocateFD[entry]; RETURN[entry]; }; Delete: PUBLIC PROC [file: FileHandle] = { RemoveOpenFile[file.name] }; Info: PUBLIC PROC [file: FileHandle] RETURNS [size: YggFile.PageCount _ 0, name: Rope.ROPE _ NIL] = { stat: StatRecord; result: INT; allocateFD[file]; TRUSTED {result _ UFStat[file.fd, @stat];}; deAllocateFD[file]; IF result < 0 THEN ERROR; RETURN[stat.size, file.name]; }; SetSize: PUBLIC PROC [file: FileHandle, size: YggFile.PageCount] = { stat: StatRecord; result: INT; allocateFD[file]; TRUSTED {result _ UFStat[file.fd, @stat];}; IF result >= 0 THEN { SELECT stat.size FROM > size => { -- truncate [] _ UFtruncate[file.fd, size]; deAllocateFD[file]; }; < size => { -- expand bytesWritten: INT; [] _ ULSeek[fd: file.fd, offset: size-1, whence: set]; TRUSTED {bytesWritten _ UWrite[fd: file.fd, buf: @AllNulls, nbytes: 1];}; deAllocateFD[file]; IF bytesWritten # 1 THEN ERROR; }; ENDCASE => {deAllocateFD[file];}; } ELSE deAllocateFD[file]; IF result < 0 THEN ERROR; }; Read: PUBLIC UNSAFE PROC [file: FileHandle, from: YggFile.PageNumber, nPages: YggFile.PageCount, to: LONG POINTER] = { bytesRead: INT; bytesNeeded: INT; allocateFD[file]; [] _ ULSeek[fd: file.fd, offset: from*YggEnvironment.bytesPerPage, whence: set]; bytesNeeded _ nPages*YggEnvironment.bytesPerPage; bytesRead _ URead[fd: file.fd, buf: to, nbytes: bytesNeeded]; deAllocateFD[file]; IF bytesRead = 0 THEN ERROR; IF bytesRead # bytesNeeded THEN ERROR; }; Write: PUBLIC PROC [file: FileHandle, to: YggFile.PageNumber, nPages: YggFile.PageCount, from: LONG POINTER] = { bytesWritten: INT; bytesToWrite: INT; allocateFD[file]; [] _ ULSeek[fd: file.fd, offset: to*YggEnvironment.bytesPerPage, whence: set]; bytesToWrite _ nPages*YggEnvironment.bytesPerPage; bytesWritten _ UWrite[fd: file.fd, buf: from, nbytes: bytesToWrite]; IF bytesWritten = 0 THEN ERROR; IF bytesWritten # bytesToWrite THEN ERROR; deAllocateFD[file]; }; numberOfOpenFiles: INT _ 0; maxNumberOfOpenFiles: INT _ 20; myCondition: CONDITION; BROADCASTmyCondition: ENTRY PROC ~ { BROADCAST myCondition; }; allocateFD: PROC [file: FileHandle, openIt: BOOL _ TRUE] ~ { unixName: REF TEXT; firstPart: ENTRY PROC ~ { DO IF file.locked THEN {WAIT myCondition; LOOP;}; IF file.fd > 0 THEN file.fdLockCount _ file.fdLockCount + 1 ELSE { IF file.fdLockCount # 0 THEN ERROR; file.fdLockCount _ 1; unixName _ UnixStringFromRope[file.name]; file.locked _ TRUE; numberOfOpenFiles _ numberOfOpenFiles + 1; WHILE numberOfOpenFiles > maxNumberOfOpenFiles DO eachPairAction: SymTab.EachPairAction = { ce: REF FileHandleRep; quit _ FALSE; ce _ NARROW[val]; IF ce.users = 0 AND ce.fd > 0 AND ~ce.locked THEN { UClose[ce.fd]; ce.fd _ 0; numberOfOpenFiles _ numberOfOpenFiles - 1; IF numberOfOpenFiles < maxNumberOfOpenFiles -2 THEN quit _ TRUE; }; }; [] _ SymTab.Pairs[x: OpenFiles, action: eachPairAction]; IF numberOfOpenFiles > maxNumberOfOpenFiles THEN YggDummyProcess.Pause[YggDummyProcess.MsecToTicks[73]]; ENDLOOP; }; ENDLOOP; }; IF file.fd <= 0 THEN { file.fd _ UOpen[LOOPHOLE[unixName, CARD]+UNITS[TEXT[0]]]; IF file.fd <= 0 THEN ERROR; }; file.locked _ FALSE; BROADCASTmyCondition[]; }; deAllocateFD: ENTRY PROC [file: FileHandle] ~ { IF file.fdLockCount <= 0 THEN ERROR; file.fdLockCount _ file.fdLockCount - 1; }; LookupNameInOpenFiles: ENTRY PROC [name: Rope.ROPE] RETURNS [entry: REF FileHandleRep _ NIL] ~ { found: BOOL; val: REF; [found, val] _ SymTab.Fetch[x: OpenFiles, key: name]; IF found THEN { entry _ NARROW[val]; }; }; RememberOpenFile: ENTRY PROC [name: Rope.ROPE] RETURNS [entry: REF FileHandleRep _ NIL] ~ { found: BOOL; val: REF; [found, val] _ SymTab.Fetch[x: OpenFiles, key: name]; IF found THEN { entry _ NARROW[val]; entry.users _ entry.users + 1; RETURN; } ELSE { IF ~SymTab.Insert[x: OpenFiles, key: name, val: NEW[FileHandleRep _ [name: name, users: 1]]] THEN ERROR; }; }; RemoveOpenFile: ENTRY PROC [name: Rope.ROPE] ~ { found: BOOL; val: REF; [found, val] _ SymTab.Fetch[x: OpenFiles, key: name]; IF found THEN { entry: REF FileHandleRep; entry _ NARROW[val]; [] _ SymTab.Delete[x: OpenFiles, key: name]; }; }; UnixStringFromRope: PROC [rope: Rope.ROPE] RETURNS [REF TEXT] ~ { len: INT _ rope.Length[]; rtn: REF TEXT _ RefText.New[len+1]; [] _ RefText.AppendRope[to: rtn, from: rope]; [] _ RefText.AppendChar[to: rtn, from: '\000]; RETURN[rtn]; }; UOpen: PROC [name: CARD, flags: CARD _ 2, mode: CARD _ 644B] RETURNS [INT] ~ TRUSTED MACHINE CODE { "open" }; UCreat: PROC [name: CARD, mode: CARD _ 644B] RETURNS [INT] ~ TRUSTED MACHINE CODE { "creat" }; URead: PROC [fd: INT, buf: POINTER, nbytes: INT] RETURNS [INT] ~ TRUSTED MACHINE CODE { "read" }; UWrite: PROC [fd: INT, buf: POINTER, nbytes: INT] RETURNS [INT] ~ TRUSTED MACHINE CODE { "write" }; Whence: TYPE ~ MACHINE DEPENDENT {set(0), incr(1), xtnd(2), (CARD.LAST)}; ULSeek: PROC [fd: INT, offset: INT, whence: Whence] RETURNS [INT] ~ TRUSTED MACHINE CODE { "lseek" }; UFSync: PROC [fd: INT] RETURNS [INT] ~ TRUSTED MACHINE CODE { "fsync" }; UFtruncate: PROC [fd: INT, length: INT] RETURNS [INT] ~ TRUSTED MACHINE CODE { "ftruncate" }; Short: TYPE ~ INT16; UShort: TYPE ~ CARD16; Long: TYPE ~ INT32; ULong: TYPE ~ CARD32; Int: TYPE ~ INT32; DevT: TYPE ~ Short; InoT: TYPE ~ ULong; TimeT: TYPE ~ Long; OffT: TYPE ~ Int; StatRecord: TYPE ~ MACHINE DEPENDENT RECORD [ dev: DevT, -- device inode resides on ino: InoT, -- this inode's number mode: UShort, -- protection nlink: Short, -- number of hard links to the file uid: Short, -- user ID of owner gid: Short, -- group ID of owner rdev: DevT, -- the device type, for inode that is device size: OffT, -- total size of file, in bytes atime: TimeT, -- file last access time spare1: Int, mtime: TimeT, -- file last modify time spare2: Int, ctime: TimeT, -- file last status change time spare3: Int, blksize: Long, -- optimal blocksize for file system i/o ops blocks: Long, -- actual number of blocks allocated spare4: ARRAY [0..2) OF Long ]; UFStat: PROC [fd: INT, buf: POINTER TO StatRecord] RETURNS [INT] ~ TRUSTED MACHINE CODE { "fstat" }; UClose: PROC [fd: INT] ~ TRUSTED MACHINE CODE { "close" }; OpenFiles _ SymTab.Create[mod: 129, case: TRUE]; TRUSTED { YggDummyProcess.SetTimeout[condition: @myCondition, ticks: YggDummyProcess.MsecToTicks[133]]; }; END. @YggUnixFileImpl.mesa Copyright Σ 1988 by Xerox Corporation. All rights reserved. Bob Hagmann May 9, 1988 9:48:40 am PDT This module converts documents from stable to volatile forms. Exported procedures File descriptor management PROC [key: Key, val: Val] RETURNS [quit: BOOL _ FALSE]; Open file symbol table management Given a name, return the object for it. Add this document to the cache for the did. Remove this did from the cache. Talking to UNIX from /usr/include/sys/file.h from /usr/include/sys/types.h from /usr/include/sys/stat.h Initialization Κ ˜code•Mark outsideHeaderšœ™Kšœ<™Kšœ˜šœ˜K˜Kšœœœ˜K˜Kšœœ˜K˜Kšœ  œ˜1Kšœœœ˜3K˜Kš Οnœœœœœœ˜B—head™š  œœœ œœ˜