<<>> <> <> <> <> <<>> DIRECTORY Ascii, IO, PseudoTerminal, Rope, SymTab, UnixSysCalls, UnixTypes, UXStrings; PseudoTerminalImpl: CEDAR PROGRAM IMPORTS IO, Rope, SymTab, UnixSysCalls, UXStrings EXPORTS PseudoTerminal ~ BEGIN OPEN PseudoTerminal; ROPE: TYPE ~ Rope.ROPE; Error: PUBLIC ERROR [code: ATOM, msg: ROPE] ~ CODE; <> inUse: SymTab.Ref ~ SymTab.Create[]; -- Don't try things that are probably in use anyway Allocate: PUBLIC PROCEDURE [] RETURNS [PseudoTerminal] ~ { pseudoTerminal: PseudoTerminal ¬ NEW[PseudoTerminalRep ¬ []]; ptyGroupNames: ROPE ~ "pqrs"; ptyLineNames: ROPE ~ "0123456789abcdef"; ptyGroups: NAT ~ ptyGroupNames.Length[]; ptyLines: NAT ~ ptyLineNames.Length[]; ptyCount: NAT ~ ptyGroups * ptyLines; PTYName: PROCEDURE [nth: NAT, controller: BOOLEAN] RETURNS [ROPE] ~ { group: NAT ~ nth / ptyLines MOD ptyGroups; line: NAT ~ nth MOD ptyLines; name: ROPE ~ IO.PutFR["/dev/%gty%g%g", [character[IF controller THEN 'p ELSE 't]], [character[ptyGroupNames.Fetch[group]]], [character[ptyLineNames.Fetch[line]]]]; RETURN [name]; }; TryOpening: PROCEDURE [name: ROPE] RETURNS [UnixTypes.FileDescriptor] ~ { IF SymTab.Insert[inUse, name, $TRUE] THEN { nameString: UXStrings.CString ~ UXStrings.Create[from: name]; descriptor: UnixTypes.FileDescriptor ~ UnixSysCalls.Open[ path: nameString, flags: [excl: true, access: RDWR], mode: []]; RETURN [descriptor]; } ELSE RETURN [UnixTypes.FileDescriptor.error] }; THROUGH [0..2) DO FOR pty: NAT IN [0 .. ptyCount) DO pseudoTerminal.controllerName ¬ PTYName[nth: pty, controller: TRUE]; pseudoTerminal.controllerFD ¬ TryOpening[name: pseudoTerminal.controllerName]; IF pseudoTerminal.controllerFD # UnixTypes.FileDescriptor.error THEN { pseudoTerminal.slaveName ¬ PTYName[nth: pty, controller: FALSE]; EXIT; } ENDLOOP; IF pseudoTerminal.controllerFD = UnixTypes.FileDescriptor.error THEN SymTab.Erase[inUse] -- Retry - some may have come free ELSE EXIT; ENDLOOP; IF pseudoTerminal.controllerFD = UnixTypes.FileDescriptor.error THEN { ERROR Error[ code: $CantAllocatePseudoTerminal, msg: "unable to allocate pseudo-terminal (/dev/pty* all in use)"]; }; RETURN [pseudoTerminal]; }; Close: PUBLIC PROCEDURE [pty: PseudoTerminal] ~ { IF pty.controllerFD # UnixTypes.FileDescriptor.error THEN { [] ¬ UnixSysCalls.Close[fd: pty.controllerFD]; pty.controllerFD ¬ UnixTypes.FileDescriptor.error; [] ¬ SymTab.Delete[inUse, pty.controllerName]; }; }; END.