Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery
 
PerlMonks  

bidirectional challange

by my_nihilist (Sexton)
on Mar 13, 2008 at 22:00 UTC ( [id://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

Replies are listed 'Best First'.
Re: bidirectional challange
by ikegami (Patriarch) 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
Domain Nodelet?
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?Last hourOther CB clients
Other Users?
Others surveying the Monastery: (4)
As of 2024-03-29 09:07 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found