1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
#!/usr/bin/perl -w
package TrapStdin;
sub TIEHANDLE {
my $class = shift;
my $fh;
bless \$fh, $class
}
sub READLINE {
my (undef, $file, $line) = caller;
warn "Irssi scripts can't read from STDIN or ARGV at $file line $line.\n";
return undef;
}
sub READ {
goto &READLINE;
}
package main;
tie *TRAP, 'TrapStdin';
*ARGV = *STDIN = *TRAP;
#### rest of this file is documentation. the doc is a lot longer than
#### the code, sorry about that.
=pod
=encoding utf8
=head1 NAME
trap_stdin.pl - catch attempts to read from standard input in an irssi perl script.
=head1 SYNOPSIS
# from shell:
cp trap_stdin.pl ~/.irssi/scripts/autorun/
# if irssi was already running, load manually:
/script load trap_stdin
=head1 DESCRIPTION
This script is intended for script developers.
The Perl scripting interface to irssi doesn't support reading from
B<STDIN> (or from B<ARGV> via the B<E<lt>E<gt>> operator). Attempts
to do so cause irssi to freeze: it stops responding to either
the keyboard or incoming IRC traffic. Eventually, the IRC server
will disconnect the client, since it's no longer responding to
PING messages, but the user will have to kill the client manually
(e.g. using C<kill> from another terminal), and probably also have to
blindly type C<reset> to fix the terminal afterwards.
The problem is, the script is trying to read from the terminal, but
the terminal has already been "taken over" by irssi itself. The read
blocks, waiting for input it will never receive.
It's easy to avoid this: just don't try to read from standard input in
your scripts. Problem solved? Well, it would be, but...
The author of this script has an unfortunate tendency to forget the
filehandle when using B<E<lt>E<gt>>. Example:
open my $fh, "<", $filename;
my $content = <>;
The second line was I<intended> to be:
my $content = <$fh>;
...but due to PEBKAC, the C<$fh> was left out (passive voice sucks:
I<I> screwed up and left it out). Attempting to run the broken code
causes irssi to lock up, with no indication what the problem is.
Although the code is wrong, the penalty for this trivial mistake is
harsh.
With B<trap_stdin.pl> loaded, the erroneous code will instead cause a
warning, and irssi will continue executing normally. The warning looks
like:
Irssi scripts can't read from STDIN or ARGV at *FILE* line *LINE*.
=head1 NOTES
All loaded scripts are affected by this. It shouldn't have any effect
on scripts that already work correctly, since they already don't try
to read from standard input (or else they wouldn't work correctly).
B<trap_stdin.pl> doesn't actually B<use Irssi>. It also doesn't stay
isolated to its own package namespace: it works by reassigning the
global C<*STDIN> and C<*ARGV> globs to a tied filehandle.
Unlike a normal irssi script, the changes made by this one will stay
active even if the script is unloaded.
If we didn't care about getting an irssi-specific warning, the script
could have just been:
undef *ARGV;
undef *STDIN;
=head1 AUTHOR
B. Watson (urchlay@slackware.uk), aka Urchlay on Libera.
=head1 LICENSE
Licensed under the WTFPL. See http://www.wtfpl.net/txt/copying/ for details.
|