Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How are YOU using perl?

128 views
Skip to first unread message

Kurt L. Reisler

unread,
Apr 5, 1988, 4:23:00 PM4/5/88
to
Which would be an appropriate news group for the discussion of uses of
Larry Wall's outstanding "perl" program? I would be intersted in
learning what uses you are putting it to, and what kind of tricks have
been developed. I am also interested in seeing how the "format"
statement has been used.

Thanks in advance,
Kurt

tch...@convex.uucp

unread,
Apr 6, 1988, 1:39:00 PM4/6/88
to

/* Written 3:23 pm Apr 5, 1988 by k...@hadron.Sun.COM in convex:comp.sources.d */
/* ---------- "How are YOU using perl?" ---------- */

Thanks in advance,
Kurt
/* End of text from convex:comp.sources.d */

I can't help with the format statement, but I do use perl quite a bit.
I'll supply two potentially useful scripts after this note.
The first is a script I wrote to get symbolic wait channels out of a ps
that doesn't supply such. You will want to init $syms to be the name
of a file containing "nm -n /vmunix" output. This script uses evals,
subroutines, and both associative and indexed arrays. Also, when
have you ever seen a script do a binary search before?

The second will tell give you the synopsis section of all man pages
matching it argument. It also spots missing cat and/or man pages.
It's also useful in telling whether you've got a local version (manl)
around.

Would anyone who gets this please reply to me by email? My news
feed (killer) has died, so I'm forced to rely upon notes (ick) and I
suspect that they don't get out to the real world.

thanks,

--tom


Tom Christiansen {ihnp4,uiucdcs,ut-sally,sun}!convex!tchrist
Convex Computer Corporation convex!tch...@sally.utexas.edu

====================== perl script #1 ================================
#!/usr/local/bin/perl -s
#
# script to convert wait channels from long ps output
#
# usage: $0 [-opt] [ var=val ] [ psopts ]
#
# recognized options are:
# -verbose print values for ps and symbols file, also symcounts
# -long print sym+offset, rather than just sym+
# -nm run nm on vmunix, don't use default place
#
# useful vars to set are:
# ps="ps l /vmunix.old |"
# ps=psfile
# syms=../lib/oldsyms
# syms="nm -n /vmunix.old |"
#
# useful ps opts are:
# x - all my procs
# ax - all procs
# axtp4 - all procs on ttyp4

# recognize short versions of options; these were set by the perl -s flag
$verbose |= $v;
$long |= $l;
$nm |= $n;

# process variable assignments
eval '$'.$1.'$2;' while $ARGV[0] =~ /^([A-Za-z_]+=)(.*)/ && shift;

# open files
$syms = "/tac/tac/lib/vmsyms" unless $syms;
$vmsyms = $nm ? "nm -n /vmunix|" : $syms;
$ps = "ps wwl" . join(' ',@ARGV) . " |" unless $ps;

if ( $verbose ) {
printf stderr "ps is \"%s\"\n", $ps;
printf stderr "vmsyms is \"%s\"\n", $vmsyms;
}

$sub = 's/^(' . ("." x ($cols ? $cols : 80)) . ').*$/$1/;';


die "$0: can't open $vmsyms: $!\n" unless open(symbols,$vmsyms);
die "$0: can't open $ps: $!\n" unless open(ps,$ps);

$| = 1; # flush
printf stderr "gathering vm symbols from ``%s''...", $vmsyms if $verbose;

for($ncount = 0;<symbols>;$ncount++) {
chop;split;
$names[$ncount] = $_[2];
$values[$ncount] = hex($_[0]);
}

printf stderr "found %d of them.\n",$ncount if $verbose;


$_ = <ps>;
s/^........//;
s/(STAT)/ $1/;
s/ (TIME)/$1/;
print;

while(<ps>) {
s/^........//;
split(' ');
if ($_[8] !~ /^[a-f0-9]*$/) {
s/(..........:)/ $1/;
eval $sub;
print;
next;
}
chop;
$front = $_;
$back = $_;
eval sprintf ('$front =~ s/%s.*//;',$_[8]);
$front =~ s/ *$/ /;
eval sprintf ('$back =~ s/.*%s//;',$_[8]);
$addr = do get_name (hex($_[8]));
$_ = sprintf( "%s%-10s%s", $front,$addr,$back);
eval $sub;
printf "%s\n",$_;
}

# do binary search on values[] array to find name.
# store hits in hits{} array for quicker subsequent lookups
sub get_name {
$it = $_[0];
if ( $hits{$it} ) {
$hits{$it};
} else {
$left = 0;
$right = $ncount-1;
do {
$targ = int(($left+$right)/2);
if ( $it < $values[$targ] ) {
$right = $targ-1;
} else {
$left = $targ + 1;
}
} until ( $it == $values[$targ] || $left > $right );
if ( $it != $values[$targ] ) {
sprintf(($long||$l) ? "%s+%x" : "%s+",
$names[$right],$it-$values[$right]);
} else {
$hits{$it} = $names[$targ];
}
}
}
====================== perl script #2 ================================
#!/usr/local/bin/perl

die "usage: $0 manpage ..." unless $#ARGV > -1;

while ( $arg = shift ) {
@manfiles = split(' ',`ls /usr/man/man?/$arg.* 2> /dev/null`);
if ($?) {
print stderr "no manpages for $arg\n";
next;
}
@catfiles = split(' ',`ls /usr/man/cat?/$arg.* 2> /dev/null`);
if ($?) {
print stderr "no catpages for $arg\n";
next;
}


if ( $#manfiles > $#catfiles ) {
print stderr
"$0: WARNING! missing cat pages for `$arg' from sections: ";
@tman = @manfiles;
while ($shouldbe = shift(@tman)) {
$found = 0;
$shouldbe =~ s/^.*\///;
@tcat = @catfiles;
while ($reallyis = shift(@tcat)) {
$reallyis =~ s/^.*\///;
$found = 1 if ( $shouldbe eq $reallyis );
}
if ( ! $found ) {
$shouldbe =~ s/.*\.//;
print stderr "$shouldbe ";
}
}
print stderr "\n";
}


while ( $file = shift(@catfiles) ) {
if ( ! open(page,$file) ) {
print stderr "couldn't open $file for read\n";
next;
}
if ( ! $opened ) {
$opened++;
die "can't open ul" unless open (ul, "|ul");
select(ul);
$| = 1;
}
$base = $file;
$base =~ s/.*\.//;
print ",$arg($base):";

$gotname = 0;
while ( ($_ = <page>) !~ /SYNOPSIS/ ) {
if (/NAME/) {
$snarfed = 0;
while (( $_ = <page> ) !~ /^\n/) {
chop;
chop if /-$/;
if (! $snarfed ) {
s/[^-]*-//;
$snarfed ++;
} else {
s/^[ ]*//;
}
printf "%s", $_;
}
}
}
print "\n+";

while ( ($_ = <page>) !~ /^[A-Z]/ ) {
next if /INTRODUCTION/;
last if /DESCRIPTION/;
print;
}
close page;
}
}
close ul if $opened;

John Stanley

unread,
Apr 8, 1988, 10:25:19 AM4/8/88
to

Sounds to me like we need to start a comp.lang.perl newsgroup. I'd
certanly be in favor of creating one...

---
John Stanley (jo...@viper.UUCP)
Software Consultant - DynaSoft Systems
UUCP: ...{amdahl,ihnp4,rutgers}!meccts!viper!john

Chip Salzenberg

unread,
Apr 13, 1988, 1:03:55 PM4/13/88
to
In article <7...@hadron.UUCP> k...@hadron.UUCP (Kurt L. Reisler) writes:
>Which would be an appropriate news group for the discussion of uses of
>Larry Wall's outstanding "perl" program? I would be intersted in
>learning what uses you are putting it to, and what kind of tricks have
>been developed.

Here is my favorite argument parsing loop:

$debug = 0;
$verbose = 1;

while ($#ARGV >= 0) {
$_ = $ARGV[0];
last unless /^-/; # Not an option
shift;
last if ($_ eq "--"); # End of options

s/-//g;
if (s/D//g) { $debug = 1; }
if (s/s//g) { $verbose = 0; }
if (s/v//g) { $verbose = 2; }
# Other options go here

# By now we should have used all the chars in this argument.
if ($_) {
print stderr "illegal option",
(length($_) > 1 ? "s" : ""),
": -$_\n";
do usage();
exit 1;
}
}

I like it because it doesn't let illegal options pass, and it allows
multiple switches in an argument.
--
Chip Salzenberg "ch...@ateng.UU.NET" or "codas!ateng!chip"
A T Engineering My employer's opinions are a trade secret.
"Anything that works is better than anything that doesn't."

mcda...@uicsrd.csrd.uiuc.edu

unread,
Apr 18, 1988, 1:30:00 AM4/18/88
to

Here are some of my notes about perl, for what they're worth. These
are points that appeared unclear to me at some time or another.

1) "each(x)" has one state variable per array, not per "each" call.
For example,
while (($i,$j) = each(foo)) {
($m, $n) = each(foo);
...
}
will iterate once through @foo ($#foo/2 loop iterations), not twice.
(If foo has an odd number of elements, the loop above is an infinite
loop.)

2) On the other hand, each ".." has its own state variable. Also,
only the left-hand side is evaluated while it is false, and then only
the right-hand side is evaluated. If you have side effects in the
other part, they are not executed.

3) eval 'X' takes about the same time as X. (eval is the only way to
simulate "first-class" file handles, associative arrays, et cetera.)

4) Lists are only one-dimensional. Sigh.

5) "@foo = (); print $#foo;" prints -1, even if $[ == 1. Similarly,
"@foo = (); $foo[$[] = 3; print $#foo;" always prints 0. (So the
statement on page 3 of the man page, about $#foo being the subscript
of the last element, is false.)

6) On an Alliant FX/1 (a Motorola 68020-based machine), -1 % 2 == -1.
I. e. "%" takes the sign of the dividend, not the divisor. However,
this is probably dependent on how the "%" operator is implemented in C
on your particular computer, which in turn is probably dependent on
the hardware "mod" instruction (if any).

7) If you use a string in a numeric context, and the string contains
non-numeric characters, it converts as many leading characters from
the string as it can, and ignores the later ones. E. g.
$a = '1e+:1'; $a = $a + 1; print $a;
$a = 'abc'; $a = $a + 1; print $a;
print 2 and 1 respectively.

The following timing tests were done on an Alliant FX/1. Your mileage
may vary. Void where prohibited.

8) The conditionals below are ranked in order of increasing execution
time with VERY approximate normalized execution times.
1 <stmt> if <expr>
1.02 <stmt> unless <expr>
1.2 if (<expr>) {<stmt>;}
1.25 if (<expr>} {<stmt>;} else {}
1.63 do {<stmt>;} unless <expr>
1.64 do {<stmt>;} if <expr>
1.8 <expr> && <stmt>
2 <expr> ? (<stmt>) : 0
(where <stmt> is a simple assignment statement, and <expr> is a
boolean expression). Those last 2 lines suprised me a lot. I was
expecting them to be the fastest, or at least equivalent to if-then or
if-then-else.

9) Timing for the following statements enclosed in a "sub":
1 if (<expr>) {<var1>;} else {<var2;>}
1.74 <expr> ? <var1> : <var2>
(<expr> is a boolean expression, and <vari> is a variable reference,
like $a.)

10) For the following statements exclosed in "sub max":
1 $max = pop(@_);
while ($foo = pop(@_)) { $max = $foo if $max < $foo; }
$max;
1.21 $max = pop(@_);
for ($i = 0; ++$i <= $#_; ) {
if ($max < ($foo = $_[$i])) { $max = $foo; }
}
$max;

11) Neither "max", nor "min", nor "abs", nor "power" is builtin.
They're easy enough to code, though.

12) In re assigning an array to variables, as in
($a, $b, $c) = @foo;
If @foo has more than 3 elements here, the extras will not be
assigned. If @foo has less than 3 elements, the missing ones are
considered ''. Following this rule, of course,
($a) = @foo;
assigns the first element of @foo to $a. However,
$a = @foo;
assigns the LAST element of @foo to $a. @foo used in ANY scalar
context refers to its last element. (E.g. 2+@foo is 2 plus the last
element of @foo.) This makes sense if you think of the comma operator
in perl or C:
a = (1, 2);
should assign 2 to a.

Larry Wall

unread,
Apr 19, 1988, 5:56:19 PM4/19/88
to
In article <4240...@uicsrd.csrd.uiuc.edu> mcda...@uicsrd.csrd.uiuc.edu writes:
: Here are some of my notes about perl, for what they're worth. These

: are points that appeared unclear to me at some time or another.
:
: 1) "each(x)" has one state variable per array, not per "each" call.
: 2) On the other hand, each ".." has its own state variable.

These are made clearer in the 2.0 documentation.

: 3) eval 'X' takes about the same time as X. (eval is the only way to


: simulate "first-class" file handles, associative arrays, et cetera.)

It's about the same time ignoring the time to parse X.

: 4) Lists are only one-dimensional. Sigh.

Whaddya want, APL?

: 5) "@foo = (); print $#foo;" prints -1, even if $[ == 1. Similarly,


: "@foo = (); $foo[$[] = 3; print $#foo;" always prints 0. (So the
: statement on page 3 of the man page, about $#foo being the subscript
: of the last element, is false.)

Fixed in 2.0 ($#foo, not the manual).

: 6) On an Alliant FX/1 (a Motorola 68020-based machine), -1 % 2 == -1.


: I. e. "%" takes the sign of the dividend, not the divisor. However,
: this is probably dependent on how the "%" operator is implemented in C
: on your particular computer, which in turn is probably dependent on
: the hardware "mod" instruction (if any).

True, the behavior of % is very machine dependent. On lots of machines
the % operator simply doesn't work on negative numbers. Perl is simply
reflecting the lack of rigor in the definition of C here. Don't use
% on negative numbers.

: 7) If you use a string in a numeric context, and the string contains


: non-numeric characters, it converts as many leading characters from
: the string as it can, and ignores the later ones. E. g.
: $a = '1e+:1'; $a = $a + 1; print $a;
: $a = 'abc'; $a = $a + 1; print $a;
: print 2 and 1 respectively.

It'll get whatever atof() gets.

: 8) The conditionals below are ranked in order of increasing execution


: time with VERY approximate normalized execution times.
: 1 <stmt> if <expr>
: 1.02 <stmt> unless <expr>
: 1.2 if (<expr>) {<stmt>;}
: 1.25 if (<expr>} {<stmt>;} else {}
: 1.63 do {<stmt>;} unless <expr>
: 1.64 do {<stmt>;} if <expr>
: 1.8 <expr> && <stmt>
: 2 <expr> ? (<stmt>) : 0
: (where <stmt> is a simple assignment statement, and <expr> is a
: boolean expression). Those last 2 lines suprised me a lot. I was
: expecting them to be the fastest, or at least equivalent to if-then or
: if-then-else.

The last two can be as fast as the first two, but it depends on whether
<expr> is recognized by the optimizer:

do foo() if $reg; # optimized
$reg && do foo(); # optimized to same as above, I believe
$reg != 0 && do foo(); # not optimized

Registers as booleans, some range operators, some patterns and substitutions
are optimized to avoid calling the general expression evaluator.

: 9) Timing for the following statements enclosed in a "sub":


: 1 if (<expr>) {<var1>;} else {<var2;>}
: 1.74 <expr> ? <var1> : <var2>
: (<expr> is a boolean expression, and <vari> is a variable reference,
: like $a.)

Why should putting them in a sub make much difference? An expression is
an expression, and a statement a statement. Adding in the calling overhead
merely reduce the ratio from 1:2 to 1:1.74.

: 10) For the following statements exclosed in "sub max":


: 1 $max = pop(@_);
: while ($foo = pop(@_)) { $max = $foo if $max < $foo; }
: $max;
: 1.21 $max = pop(@_);
: for ($i = 0; ++$i <= $#_; ) {
: if ($max < ($foo = $_[$i])) { $max = $foo; }
: }
: $max;

Subscripting is not very efficient. Popping an array is quite efficient,
even more so than shifting an array.

: 11) Neither "max", nor "min", nor "abs", nor "power" is builtin.


: They're easy enough to code, though.

: 12) In re assigning an array to variables, as in
: ($a, $b, $c) = @foo;
: If @foo has more than 3 elements here, the extras will not be
: assigned. If @foo has less than 3 elements, the missing ones are
: considered ''. Following this rule, of course,
: ($a) = @foo;
: assigns the first element of @foo to $a. However,
: $a = @foo;
: assigns the LAST element of @foo to $a. @foo used in ANY scalar
: context refers to its last element. (E.g. 2+@foo is 2 plus the last
: element of @foo.) This makes sense if you think of the comma operator
: in perl or C:
: a = (1, 2);
: should assign 2 to a.

That's how I was thinking of it. Note that in perl 2.0, there are a few more
contexts in which @foo is interpreted as an array rather than a scalar. In
particular, within lists:

print 1,2,3,@foo,4,5,6;

This interpolates all elements of array foo as if they'd been specified
one by one. Not extremely useful in print, but what about things like
kill, chmod or do?

kill 9, @goners;

chmod 0755, <*.[ch]>;

do matchall($#foo,@foo,$#bar,@bar);

Those of you who have read this far have the reward of being notified of
the availability of perl 2.0 beta 1 via anonymous FTP from my machine,
jpl-devvax.jpl.nasa.gov (128.149.8.43). Look in pub/perl.2.0.beta1/kits.
I'm not going to offer patches for the beta version, but I expect to send
off a real 2.0 in a week or two. If you are interested in beta testing 2.0,
now is your big chance.

Let's see, what else is new in 2.0? File globbing is hacked in, though it
still calls sh underneath for now. File globs are done with <pattern>, and
<pattern> returns arrays in array contexts, so you can say

unlink <*.bak>;

@files = <*>;

not to mention

@input = <>; # oh where, oh where has my memory gone!

Speaking of arrays, there's a way to iterate over normal arrays now, such that
you not only can read each element, but also modify it.

foreach $elem (@elements) {
$elem =~ s/foo/bar/;
}

I considered using the "for (a in b)" syntax, but decided it was too late to
add a keyword like "in". So I went with the csh syntax. The "each" and the
"$elem" are optional, so you can also write the above as

for (@elements) {
s/foo/bar/; # each elem is in $_
}

How about

for ((10,9,8,7,6,5,4,3,2,1,'BOOM!')) {
print "$_\n";
sleep(1);
}

Several of the larger files are now split into smaller pieces for easier
compilation.

The @ary = (1); bug is now fixed.

Oh, here's a biggie: I scrapped the search routines I was using before and
inserted a heavily munged copy of Henry Spencer's regexp routines. This
means you can now say silly things like /(abc|def)*/. I also added \s to
match whitespace and \d to match digits.

For setuid scripts, there was a need to be able to open pipes without invoking
the shell to interpret the pipe command. To that end, opening a pipe to
or from the command "-", forks off a copy of your script so that you can
do the exec explicitly yourself, and not have to worry about weeding out
shell metacharacters:

open(secure,"|-") || exec '/usr/bin/tr', '[a-z]', '[A-Z]';

It hooks up the opened filehandle with either the stdin or stdout of the
forked off script, whichever is appropriate. You don't have to do an exec,
of course.

What else...hmm..oh yes. You can now say

do 'stat.pl';

which is similar to

eval `cat stat.pl`;

except that it's more efficient and can scan -I directories. Its primary
purpose is to slurp in subroutine libraries from, say, /usr/local/lib/perl.
The -I directories come in as array @INC, so you can modify them from within
the program.

There's more file tests, including -t to see if, for instance, stdin is
a terminal. File tests now behave in a more correct manner. You can do
file tests on filehandles as well as filenames.

The $x = "...$x..."; bug is fixed.

eof can now be used on each file of the <> input for such silly purposes
as resetting the line numbers or appending to each file of an inplace edit.

$#foo is now an lvalue. You can preallocate or truncate arrays.

reset now resets arrays and associative arrays as well as string variables.


Well, enough for starters. I particularly want to hear from people with
machines I don't have access to. So far I've only tested it on

Vax, 4.3bsd (Xinu)
Sun 3, 3.3.
Masscomp 5600

See if you can break it for me. Thanks.

Larry Wall
lw...@jpl-devvax.jpl.nasa.gov

Johan Vromans

unread,
Apr 20, 1988, 11:49:32 AM4/20/88
to
How am I using perl? Well - I don't.

I've patched it up to #28, but cannot get it to pass the tests on
my machine (an HP9000 model 320 running HP-UX version 5.3).

I've sent a mail to Larry to ask for information about my problems before I
dig into the sources, and I'm still waiting ....

It looks promising, though ...

--
Johan Vromans | j...@mh.nl via European backbone
Multihouse N.V., Gouda, the Netherlands | uucp: ..{uunet!}mcvax!mh.nl!jv
"It is better to light a candle than to curse the darkness"

mcda...@uicsrd.csrd.uiuc.edu

unread,
Apr 20, 1988, 10:09:00 PM4/20/88
to

Larry: thanks for your prompt reply.

I recently tried to use perl for a large program. I chose perl rather
than C because of perl's good string handling facilities and built-in
list processing facilities. I rapidly discovered that, while perl is
good for short programs (up to 100 lines or so?), trying to do longer
programs, or more complicated ones, becomes painful. The most
noticable:

1) there is no way to declare local names. To avoid name collisions
in "sub"s I was prefixing names with the "sub" name, which lead to
lines like

for ($SWAP_i = 0; $SWAP_i < $#lru_buffers; $SWAP_i++)

2) If I make a typo in a variable name, I get no compiler- or run-time
notice except for wrong answers. Icon has a partial solution: an
option makes all new variables start with a special <undefined> value,
which causes a run-time error if used before setting. This gets many
of the cases, but you can still sometimes use a typo-ed name on the
LHS of an assignment without warning. Only declarations can catch
those.

For a short program, all the identifiers are there in front of you,
and it's easier to see and avoid such problems. Furthermore, perl's
typelessness (which I like) and lack of declarations help---you can
get off the ground without all the overhead of typing (in either
meaning of the word). But after 175 lines of perl, I decided that
putting up with C (despite mucking about with <string.h> and sscanf
and malloc and pointers and . . .) was easier.

However, I *will* use perl in shorter applications.

--
Tim, the Bizarre and Oddly-Dressed Enchanter
Center for Supercomputing Research and Development
at the University of Illinois at Urbana-Champaign

Internet, BITNET: mcda...@uicsrd.csrd.uiuc.edu
UUCP: {ihnp4,uunet,convex}!uiucuxc!uicsrd!mcdaniel
ARPANET: mcdaniel%uic...@uxc.cso.uiuc.edu
CSNET: mcdaniel%uic...@uiuc.csnet

Larry Wall

unread,
Apr 22, 1988, 3:11:00 PM4/22/88
to
: 1) there is no way to declare local names. To avoid name collisions

: in "sub"s I was prefixing names with the "sub" name, which lead to
: lines like
:
: for ($SWAP_i = 0; $SWAP_i < $#lru_buffers; $SWAP_i++)

I agree, that's painful. Currently I'd use pushes and pops to make local
variables, but I'm planning to make a "local" operator of some sort.
For now:

sub FOO {
push(@STK,$i,$j,$k);
...
($k,$j,$i) = (pop(@STK), pop(@STK), pop(@STK));
}

But someday soon:

sub FOO {
local($i,$j,$k);
...
}

All it does internally, of course, is to push $i, $j and $k on a stack,
and restore them automatically upon exiting the block in which they are pushed.
The local operator would probably be an lvalue, so you could say

sub FOO {
local($x, $y, $z) = @_; # name formal parms
local($i, $j, $k); # push locals
...
}

Can you think of a better way to do this?

: 2) If I make a typo in a variable name, I get no compiler- or run-time


: notice except for wrong answers. Icon has a partial solution: an
: option makes all new variables start with a special <undefined> value,
: which causes a run-time error if used before setting. This gets many
: of the cases, but you can still sometimes use a typo-ed name on the
: LHS of an assignment without warning. Only declarations can catch
: those.

It will be fairly easy to add a similar option to perl. In fact, it'll take
about 10 minutes.

How about a warning on variables that are only mentioned once, either as
lvalue or rvalue? Probably tied to the same option. How about -w for "warn"?

: For a short program, all the identifiers are there in front of you,


: and it's easier to see and avoid such problems. Furthermore, perl's
: typelessness (which I like) and lack of declarations help---you can
: get off the ground without all the overhead of typing (in either
: meaning of the word). But after 175 lines of perl, I decided that
: putting up with C (despite mucking about with <string.h> and sscanf
: and malloc and pointers and . . .) was easier.
:
: However, I *will* use perl in shorter applications.

Perl isn't intended for long applications. You'll note that I write long
applications in C also.

On the other hand, I don't want to place arbitrary restrictions on long scripts
just because I'm mean and nasty.

Larry Wall
lw...@jpl-devvax.Jpl.Nasa.Gov

0 new messages