Forum: Ruby pipe in cmd-line text but then reset STDIN for menu

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Andrew S. (Guest)
on 2008-11-25 06:37
Hello,
I'm trying to make a customized pager utility that can process
file-contents piped in from the command line, equally well as a list of
provided file names.
e.g.  cat testfile | my_util.rb    or   my_util.rb  testfile

I'm having trouble using the pipe to get the file contents and still be
able to use STDIN in a non-blocking way for a shell-style interactive
menu.

For the menu, I'm using Highline::SystemExtensions:  e.g.:
while (mychar = get_character) && (mychar.chr != "q")
  if (mychar.chr == "x") then
    filter_all_but_selected
  end
end

... and that works only for the case where there is no input pipe,
instead filename(s) I get from ARGV.

Here's what I've tried to get the pipe read before begining the menu,
but thid hammers the rest of my script and my highline menu doesn't
work:

def fill_pipe
  # capture piped input
  flags = STDIN.fcntl(Fcntl::F_GETFL, 0)
  flags |= Fcntl::O_NONBLOCK
  STDIN.fcntl(Fcntl::F_SETFL, flags)
  begin
    input_pipe = STDIN.readlines
    $working_pipe.concat(input_pipe)
#    puts "Input pipe: #{$input_pipe}"
  rescue Errno::EAGAIN
    puts "ERROR: no input piped nor file name provided"
    display_help
  end  # end what kind of input
end # end fill_pipe

STDIN.close_read  doesn't do the trick to reset things -- ideas how I
can have it both ways?  Thanks!  Andrew.
Brian C. (Guest)
on 2008-11-25 10:51
Andrew Seidel wrote:
> I'm having trouble using the pipe to get the file contents and still be
> able to use STDIN in a non-blocking way for a shell-style interactive
> menu.

I think what you're asking is: how can I write a program which does the
same as this?

    cat /usr/share/file/magic | less

(i.e. less reads its input from stdin, but can also read the terminal).

This is more a Unix question than a Ruby one. Probably the easiest way
to find out will be to read the source code of 'less'.

The less manpage directs you to http://www.greenwoodsoftware.com/less/
from where you can download the source tarball.

The relevant bit:

        /*
         * Try /dev/tty.
         * If that doesn't work, use file descriptor 2,
         * which in Unix is usually attached to the screen,
         * but also usually lets you read from the keyboard.
         */
#if OS2
        /* The __open() system call translates "/dev/tty" to "con". */
        tty = __open("/dev/tty", OPEN_READ);
#else
        tty = open("/dev/tty", OPEN_READ);
#endif
        if (tty < 0)
                tty = 2;

That is: it opens /dev/tty for keyboard input, and falls back to fd 2 if
that fails. In Ruby that would be something like

  mystdin = IO.open("/dev/tty") rescue IO.for_fd(2)

or if your user library can only talk to STDIN, then perhaps

  STDIN.reopen("/dev/tty")

All untested.
Andrew S. (Guest)
on 2008-11-25 23:26
Brian,
Thanks!  I'll try some things out a let the forum know iff I produce
something of interest.  Cheers, Andrew.
This topic is locked and can not be replied to.