title: Running vi from within haskell program (dealing with ptys)
body: <p>I'm trying to write a logging shell; e.g. one that captures data about commands that are being run in a structured format. To do this, I'm using <code>readline</code> to read in commands and then executing them in a subshell whilst capturing things such as the time taken, the environment, the exit status and so on. </p>
  
  <p>So far so good. However, initial attempts to run things such as <code>vi</code> or <code>less</code> from within this logging shell failed. Investigation suggested that the thing to do was to establish a pseudo-tty and connect the subshell to that rather than to a normal pipe. This stops vi complaining about not being connected to a terminal, but still fails - I get some nonsense printed to the screen and commands print as characters in the editor - e.g. 'ESC' just displays <code>^[</code>.</p>
  
  <p>I <em>thought</em> that what I needed to do was put the pty in raw mode. To do this, I tried the following:</p>
  
  <pre><code>  pty &lt;- do
      parentTerminal &lt;- getControllingTerminalName &gt;&gt;= 
                        \a -&gt; openFd a ReadWrite Nothing defaultFileFlags
      sttyp &lt;- getTerminalAttributes parentTerminal
      (a, b) &lt;- openPseudoTerminal
      let rawModes = [ProcessInput, KeyboardInterrupts, ExtendedFunctions, 
                      EnableEcho, InterruptOnBreak, MapCRtoLF, IgnoreBreak, 
                      IgnoreCR, MapLFtoCR, CheckParity, StripHighBit, 
                      StartStopOutput, MarkParityErrors, ProcessOutput]
          sttym = withoutModes rawModes sttyp
          withoutModes modes tty = foldl withoutMode tty modes
      setTerminalAttributes b sttym Immediately
      setTerminalAttributes a sttym Immediately
      a' &lt;- fdToHandle a
      b' &lt;- fdToHandle b
      return (a',b')
  </code></pre>
  
  <p>E.g. we get the parent terminal's attributes, remove the various flags that I think correspond to setting the tty into raw mode (based on <a href="http://www.cs.uleth.ca/~holzmann/C/system/ttyraw.c" rel="noreferrer">this code</a> and <a href="http://hackage.haskell.org/package/unix-2.5.1.0/docs/src/System-Posix-Terminal-Common.html#TerminalMode" rel="noreferrer">the haddock for <code>System.Posix.Terminal</code></a>), and then set these on both sides of the pty.</p>
  
  <p>I then start up a process within a shell using <code>createProcess</code> and use <code>waitForProcess</code> to connect to it, giving the slave side of the pty for the stdin and stdout handles on the child process:</p>
  
  <pre><code>eval :: (Handle, Handle) -&gt; String -&gt; IO ()
  eval pty command = do
      let (ptym, ptys) = pty
      (_, _, hErr, ph) &lt;- createProcess $ (shell command) { 
            delegate_ctlc = True
          , std_err = CreatePipe
          , std_out = UseHandle ptys
          , std_in = UseHandle ptys
        }
      snipOut &lt;- tee ptym stdout
      snipErr &lt;- sequence $ fmap (\h -&gt; tee h stderr) hErr
      exitCode &lt;- waitForProcess ph
      return ()
    where tee :: Handle -&gt; Handle -&gt; IO B.ByteString
          tee from to = DCB.sourceHandle from
              $= DCB.conduitHandle to -- Sink contents to out Handle
              $$ DCB.take 256 -- Pull off the start of the stream
  </code></pre>
  
  <p>This definitely changes terminal settings (confirmed with <code>stty</code>), but doesn't fix the problem. Am I missing something? Is there some other device I need to set attributes on?</p>
  
  <p>Edit: The full runnable code is available at <a href="https://github.com/nc6/tabula" rel="noreferrer">https://github.com/nc6/tabula</a> - I've simplified a few things for this post.</p>