Pipe in cmd-line text but then reset STDIN for menu

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.

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 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.

Brian,
Thanks! I’ll try some things out a let the forum know iff I produce
something of interest. Cheers, Andrew.