Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

bidirectional challange

by my_nihilist (Sexton)
on Mar 13, 2008 at 22:00 UTC ( #674081=perlquestion: print w/ replies, xml ) Need Help??
my_nihilist has asked for the wisdom of the Perl Monks concerning the following question:

here is THCB.pl. It is a candified "viewer" for telneting into the ChatterBox which does away with line endings but adds some color, a timer, and a log.

I say "viewer" because this can log you in and greet the CB if you enter a valid username and password. Otherwise, it will start telnet anyway and you can watch in anonymity.

My Question: THCB uses IPC w/autoflush because i would like to somehow pipe STDIN->TNOUT but can't figure out how to do that without interrupting the while loop.
#!/usr/bin/perl use warnings; use strict; use Term::ANSIColor; use IO::Handle; use IPC::Open2; $|=1; print "User: "; my $User=<>; print "Pass: "; my $Pass=<>; open2(*TNIN, *TNOUT, "telnet desert-island.dynodns.net 4040"); TNOUT->autoflush(1); open(LOG, ">THCB.log"); LOG->autoflush(1); print TNOUT "auth ]$User [ $Pass"; my @c=("red","green","yellow","magenta"); my $n=0; alarm 120; my $m=2; while (<TNIN>) { $SIG{ALRM}=sub{rearm()}; print LOG "$_"; if ($_ eq "Found you!\n") {print TNOUT "<i>exhaling...</i>\n"} my @line = split / /; my $bit = shift @line; chomp @line; if ($bit =~ /:$/) { print color("blue on_cyan"), "$bit"; print color("reset"); } else {print color("$c[$n]"), "$bit "} print color("$c[$n]"), "@line "; if ($n eq 3) {$n=0} else {$n++} } sub rearm { print color("white on_red"), "$m"; print "m"; print color("reset"); alarm 120; $m++; $m++; }
NOTE: THCB does not yet log you out because you can't log out through telnet. You can log in repeatedly, but i recommend against this; eventually im2 sends little scoldings back.

Update: THCB is probably a *nix event

Comment on bidirectional challange
Download Code
Re: bidirectional challange
by ikegami (Pope) on Mar 13, 2008 at 22:26 UTC
    You want to read from STDIN and from TNIN without starving either, so that points to IO::Select.

      Based on CB chat, there's some confusion with my solution, so I'll elaborate.

      It's my understanding that you want to do something along the lines of

      while (<STDIN> or <TNIN>) { if (we_read_from_STDIN()) { print TNOUT do_this($_); } else { print STDOUT do_that($_); } }

      Of course, that's not valid Perl. That where IO::Select comes in.

      use IO::Select qw( ); my $sel = IO::Select->new(\*STDIN, \*TNIN); MAIN_LOOP: while (my @ready = $sel->can_read()) { for my $fh (@ready) { my $bytes_read = sysread($fh, my $data='', 4096) or last MAIN_LOOP; if ($fh == \*STDIN) { print TNOUT do_this($data); } else { print STDOUT do_that($data); } } }

      We can't use <$fh> since that would block when only part of a line has arrived. If you want to do line-based IO, you'll have to do your own line building:

      use IO::Select qw( ); my $sel = IO::Select->new(\*STDIN, \*TNIN); my %bufs; MAIN_LOOP: while (my @ready = $sel->can_read()) { for my $fh (@ready) { our $buf; local *buf = \$bufs{$fh}; # Alias my $bytes_read = sysread($fh, $buf, 4096, length($buf)) or last MAIN_LOOP; my $line_end = index($buf, $/); next if $line_end < 0; my $line = substr($buf, 0, $line_end+1, ''); if ($fh == \*STDIN) { print TNOUT do_this($line); } else { print STDOUT do_that($line); } } }

      Update:

      • I'm not sure about the $fh == \*STDIN part. fileno($fh) == fileno(\*STDIN) should definitely work.
      • Note: %bufs may still contain data when last MAIN_LOOP; is executed.
      • Bonus: Since can_read accepts a timeout argument, you can probably do away with your alarm!
      • Won't work on Windows.
Re: bidirectional challange
by halfcountplus (Hermit) on Mar 14, 2008 at 03:48 UTC
    I couldn't figure a way to channel STDIN->TNOUT, but you could use $SIG{INT} as something to "listen for" along with the alarm, so the user can interrupt and input a line. That means you need a new way to conviently kill the whole thing (input DONE). Since TNIN is triggered initially by im2 starting the timer:
    $SIG{ALRM}=sub{rearm()};
    insert this line after that:
    $SIG{INT}=sub{process()};
    and for the sub:
    sub process { print color("black on_white"), ">"; print color("reset"); my $input=<>; if ($input eq "DONE\n") {exit 0} else {print TNOUT "$input"} }
    now, when you press ctrl-c, instead of death you get a little ">" prompt so you can talk.

    I just tried it and it werx dandy. ROLL ON
Re: bidirectional challange
by my_nihilist (Sexton) on Mar 14, 2008 at 14:25 UTC
    I posted a working version of THCB in the Code Catacombs, which uses halfcountplus's suggestion w/ the Ctrl-C interrupt. I am still surprised there is no/no one provided a simple way to just "channel STDIN->TNOUT". ikegami also suggested
    open2(*TNIN, '<&STDIN', "telnet desert-island. dynodns.net 4040");
    Which actually works, except that STDIN then seems to become divorced from keyboard input at a certain point. Frankly, after much tooling around with IO::Select and what have you, i am suspicous of ikegami's ideas here in general. But if anyone can demonstrate them working in THCB, I'd love to see it!

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://674081]
Approved by ikegami
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others examining the Monastery: (9)
As of 2015-07-05 08:56 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The top three priorities of my open tasks are (in descending order of likelihood to be worked on) ...









    Results (61 votes), past polls