Thanks for being interested. Lets walk through some of your code.
Code:
# search /proc/net/wireless for available devices
$c = 0;
open (DEVICES, "cat /proc/net/wireless |");
while (<DEVICES>){
if ($c > "1"){
$lin = $_;
$linf = index($lin, ":");
$length = $linf - "2";
$device = substr($lin,"2", $length);
@devices = ($device, @devices);
}
$c++
}
#number of devices available
$numdev = $c - "2";
Firstly, we don't need [tt]cat[/tt]. /proc/net/wireless behaves like a file so we can just open it and read it with
Code:
open (DEVICES, '/proc/net/wireless')
or die "$0: /proc/net/wireless: $!";
You'll note that I've added a bit of error checking.
Secondly, [tt]@devices = ($device, @devices);[/tt] adds $device to the beginning of the @devices array. The [tt]push()[/tt] function does this much more efficiently. We simply say
(We could have used [tt]unshift()[/tt] to add to the other end of the array. [tt]pop()[/tt] and [tt]shift()[/tt] remove items from either end).
Now we want to read this file and grab the device names into an array. The file contains something like
Code:
Inter-|sta| Quality | Discarded packets
face |tus|link level noise| nwid crypt misc
eth2: f0 15. 24. 4. 181 0 0
You're using [tt]$c[/tt] for two purposes - losing the first two lines ([tt]if ($c<1){[/tt]) and providing a count. The count is redundent: it's just the number of items in [tt]@devices[/tt]. Throwing away the first two lines would perhaps be more explicit with two null reads, as in
Code:
<DEVICES>;<DEVICES>; # discard two lines
but, as we'll see, there's a better way.
In the body of the loop, you're finding the first colon, subtracting two and extracting the required number of characters from the input string, all with associated variables. This is pretty much how you'd have to do it in, say, 'c' but Perl provides much cleaner text parsing idioms.
There are several neat ways to do this but I'm going to show a solution using regular expressions. They are perhaps slightly over-used in perl and there are sometimes more efficient solutions, but they are not inefficient in themselves and can produce some very concise, legible code.
We're interested in those input lines which have some optional leading spaces, followed by some word-characters, followed by a colon. In regex-speak, this is
The ^ anchors the expression to the beginning of the line. The \s stands for any space character and the * modifier means none or more. The \w stands for any word character (A-Z, a-z, 0-9 or _) and the + modifier means one or more. The colon stands for itself. We can extract the word by parenthesising it - it is returned as $1 - like this
In fact, we don't even need $1 if we assign
from the regex in array context. We can say
Code:
my($device) = /^\s*(\w+):/;
This assignment will fail if the input line does not match the pattern, so we can be very slick and say
Code:
while(<DEVICES>){
next unless my($device) = /^\s*(\w+):/;
push @devices, $device;
}
Using [tt]next[/tt] in a two-line loop should ring alarm bells, and I'd probably end up with the minimal (but still very readable)
Code:
while(<DEVICES>){
push @devices, $1 if /^\s*(\w+):/;
}
We could now say
but, as we're not using it, I'll omit that. It's rare to need the length of an array in perl as they tend to be processed using [tt]foreach[/tt], [tt]map[/tt] or [tt]grep[/tt] rather than using an explicit iterator. You may want something like
Code:
warn_no_connection() unless @devices;
to explicitly handle the case where there are no networks.
Now we've got our array of network names, we need to create the buttons. A [tt]foreach[/tt] loop is the natural way:
Code:
my $table = new Gtk::Table( 1, 2, $true );
foreach my $dev (@devices) {
my $button = new Gtk::Button( $dev );
$button->signal_connect( 'clicked', \&ButtonClicked, $dev );
$table->attach_defaults( $button, 0, 1, 0, 1 );
$button->show();
}
You'll note I've used lexical variables (introduced by [tt]my[/tt]) rather than globals. They only exist within the enclosing textual block so, in combination with [tt]use strict[/tt] (which will warn if you don't use lexicals), they will protect your variables from the rest of your code and vice versa. It's a habit well worth getting into which will save you hours of debugging in the future. Promise.
Somewhat pathologically, I've changed
to
. "" is an operator that constructs a new, anonymous, read-only string variable, possibly with interpolation. It parses it's contents looking for variables to interpolate. '' simply introduces a string constant, which is what we want here.
Here's the fragment.
Code:
my @devices;
while(<DEVICES>){
push @devices, $1 if /^\s*(\w+):/;
}
my $table = new Gtk::Table( 1, 2, $true );
foreach my $dev (@devices) {
my $button = new Gtk::Button( $dev );
$button->signal_connect( 'clicked', \&ButtonClicked, $dev );
$table->attach_defaults( $button, 0, 1, 0, 1 );
$button->show();
}
I hope that this has been helpful. Let me know how you get on with the project.
Yours,
fish
["]As soon as we started programming, we found to our surprise that it wasn't as easy to get programs right as we had thought. Debugging had to be discovered. I can remember the exact instant when I realized that a large part of my life from then on was going to be spent in finding mistakes in my own programs.["]
--Maur