The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#! /bin/env perl
# -*- perl -*-

# @Author: Tong SUN, (c)2002-2008, all right reserved
# @Version: $Date: 2008/11/04 21:38:09 $ $Revision: 1.10 $
# @HomeURL: http://xpt.sourceforge.net/

# @CREDIT:
# First draft in script from
# http://www.geocities.com/SiliconValley/Horizon/9144/cdlwb007.html
# By Sujit Pal, Concord, California, USA

# {{{ POD, Intro: 

=head1 NAME

news-search - console-based newsgroup articles searcher.

=head1 SYNOPSIS

  news-search [options...] ngname [ngname...] [param=value...]

Examples:

  news-search --max_p=30 tor.housing Subject='rent|sublet|room|bdr|house|apt|apartment|condo' NoSubject='sale|FS'

This search the default 'news' nntp server. Or, to specify an alternative one:

  export NNTPSERVER=news.easysw.com
  news-search --max_p=80 -v 'cups.*' Body='die|break|broke'

Check out all the program options that you can use. E.g.,

  news-search --headers='Newsgroups|Message-ID|Bytes' -nopbody alt.binaries.e-book.technical alt.binaries.e-books alt.binaries.e-book Subject='Java|JDBC|JNDI|JAXP|EJB|Servlet|Jsp|struts' NoSubject=Javascript

=head1 DESCRIPTION

This program is a console-based newsgroup articles searcher.

=head1 COMMAND LINE ARGUMENTS

Parameters passed from the command line are grouped into two categories,
the optional program options, followed by mandatory searching arguments.

The following searching arguments are supported.

=over 4

=item *

ngname, newsgroup pattern to search. May have more than one newsgroups to
search at once. May have wildcard ('*') in group name, eg, '*linux*'.

=item *

Subject=pattern, look in the Subject: line.

=item *

From=pattern, look for author in the From: line.

=item *

Body=pattern, look for pattern in article body.

=back

The pattern can be any Perl regular expressions.

The above pattern match keyword can also be prefixed with 'No', e.g.,

    NoSubject=pattern

to ignore messages if pattern found in the Subject: line.

=cut

# }}} 

# ############################################################## &ps ###
# ................................................... Program starts ...


# ============================================================== &us ===
# ............................................................. Uses ...

# -- global modules
use strict;			# !
use integer;			# !

use Getopt::Long;
use Pod::Usage;
#require Data::Dumper;

use News::Search;


# ============================================================== &cs ===
# ................................................. Constant setting ...
my $VERSION = sprintf("%d.%02d", q$Revision: 1.10 $ =~ /(\d+)\.(\d+)/);


# ============================================================== &gv ===
# .................................................. Global Varibles ...


# ############################################################## &ss ###
# ................................................ Subroutions start ...

# my handler for group starts ...
sub my_group_handler {
    my $newsgroup = shift;
    print STDERR "\n\nSearching group '$newsgroup'\n\n";
}

# my handler for news message ...
sub my_message_handler {
    print STDERR ".";
}

# ============================================================== &sb ===
# .................................................... Script begins ...

# {{{ POD, Options: 

=head1 OPTIONS

=over 4

=item -max_p=i

Maximum number of posts to search (not return). Default is 10.

=item -subpre=s

The prefix string printed before printing the Subject.

=item -subsuf=s

The suffix string printed after printing the Subject.

=item -headers=s

Message headers to print.

=item -phead

Print message headers. Default is yes.
May be turned off by prefixing the option with "no" or "no-".

=item -pbody

Print message body. Default is yes.
May be turned off by prefixing the option with "no" or "no-".

=item -verbose

Be verbose. Default is no.

=item -help|?

Print a brief help message.

=item -man

Show the manual page.

=back

Options names may be abbreviated to uniqueness, case does not matter, and a
single dash is sufficient, even for long option names.

=cut

# }}} 

#
# Handle command line arguments
#
die "\nnews-search - console-based newsgroup articles searcher\n".
    "Use the options -help or -man to get more helps.\n"
    if $#ARGV == -1;

my %options =
    (
     max_p   => 10,		# maximum number of posts to search (not return)
     subpre  => "Subject: ",	# prefix before printing the Subject
     subsuf  => "\n",		# suffix after printing the Subject
     headers => 'Date|From',	# message headers to print
     phead   => 1,		# print message header?
     pbody   => 1,		# print message body?
     verbose => 0,
);

GetOptions(\%options,
	   "max_p=i",
	   'subpre=s',
	   'subsuf=s',
	   'headers=s',
	   'phead!',
	   'pbody!',
	   'verbose!',
	   'help|?',
	   'man',
    ) or pod2usage(2);
pod2usage(1) if $options{help};
pod2usage(-exitstatus => 0, -verbose => 2) if $options{man} or !@ARGV;

#print STDERR "\n==>> \n" . Data::Dumper->Dump([\%options, \@ARGV]) . "==<< \n";

# ============================================================ &pclp ===
# .................................. process command line parameters ...

my $ns = News::Search->
    new( {
	  msg_limit   => $options{max_p},
	  msg_headers => $options{headers},
	  verbose     => $options{verbose},
	  on_group    => \&my_group_handler,
	  on_message  => \&my_message_handler,
      } );
$ns->search_for(\@ARGV);

my %newsarticles = $ns->SearchNewsgroups;
print STDERR "\n";
#print STDERR "\n==>> \n" . Data::Dumper->Dump([\%newsarticles]) . "==<< \n";

# Dump found news postings
foreach my $article (values %newsarticles) {
    print "$options{subpre}$article->{SUBJECT}$options{subsuf}";
    print join("\n",@{$article->{"HEADER"}}). "\n\n" if $options{phead};
    print $article->{"BODY"}. "\n" if $options{pbody};
}
print "\n\n";

=head1 AUTHOR

SUN, Tong C<< <suntong at cpan.org> >>
http://xpt.sourceforge.net/

=head1 COPYRIGHT

Copyright 2003-2008 Tong Sun, all rights reserved.

This program is released under the BSD license.

=cut