#!/usr/bin/perl 
'di';
'ig00';
$yysccsid = "@(#)yaccpar 1.8 (Berkeley) 01/20/91 (Perl 2.0 12/31/92)";
#define YYBYACC 1
$NNTP=257;
$SELECT=258;
$MBOX=259;
$COLLECT=260;
$STATISTICS=261;
$IN=262;
$ON=263;
$SEARCH=264;
$UNLESS=265;
$PATTERNS=266;
$PAIRS=267;
$REGEXP=268;
$IPADR=269;
$FQDN=270;
$PORT=271;
$NEWSGROUP=272;
$FILESPEC=273;
$LIST=274;
$VETO=275;
$DESELECT=276;
$WHERE=277;
$REQUIRE=278;
$RANGE=279;
$WORD=280;
$WORDSTEM=281;
$WORDEXP=282;
$PHRASE=283;
$OUTLINE=284;
$MBOXFORMAT=285;
$FORMAT=286;
$YYERRCODE=256;
@yylhs = (                                               -1,
    0,    0,    1,    1,    1,    1,    1,    1,    1,    1,
    1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
    1,    1,    1,    4,    4,    4,    4,    4,    5,    5,
    2,    3,    3,
);
@yylen = (                                                2,
    0,    2,    1,    3,    3,    4,    4,    3,    3,    3,
    3,    3,    3,    3,    5,    5,    6,    6,    6,    4,
    4,    3,    2,    2,    2,    2,    2,    1,    2,    1,
    1,    1,    2,
);
@yydefred = (                                             1,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,   31,    2,    3,    0,    0,   32,    0,    0,
    0,   28,    0,    0,    0,    0,    0,    0,   23,    0,
    0,    0,    0,    0,    4,    0,    5,   33,    8,   13,
    0,    0,   24,   25,    0,   26,   27,   12,    0,   22,
    9,   10,   11,   14,    6,    7,    0,    0,    0,   20,
    0,    0,   29,   21,   15,    0,    0,   16,    0,   17,
   18,   19,
);
@yydgoto = (                                              1,
   14,   15,   19,   27,   46,
);
@yysindex = (                                             0,
  -10, -260, -265, -262, -247, -263,  -39,   12, -265, -263,
 -263, -255,    0,    0,    0,   -8,   -2,    0,   -7,   12,
 -224,    0, -240, -239, -236, -236,   12,   -9,    0,   -7,
   12,   12,   12,   12,    0,   12,    0,    0,    0,    0,
 -238, -231,    0,    0, -236,    0,    0,    0,   12,    0,
    0,    0,    0,    0,    0,    0,   12, -219, -218,    0,
   12, -217,    0,    0,    0,   12,   12,    0,   12,    0,
    0,    0,
);
@yyrindex = (                                             0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,   40,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,
);
@yygindex = (                                             0,
    0,   -4,   42,   30,  -20,
);
$YYTABLESIZE=275;
@yytable = (                                             13,
   13,   13,   13,   29,   22,   47,   18,   13,   16,   17,
   20,   35,   37,   21,   39,   40,   23,   24,   28,   25,
   26,   13,   48,   50,   63,   51,   52,   53,   54,   55,
   33,   56,   58,   59,   57,   60,   61,   41,   42,   31,
   32,   43,   44,   62,   64,   45,   66,   67,   69,   30,
   30,    0,   65,    0,    0,    0,   68,    0,    0,    0,
    0,   70,   71,    0,   72,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
    0,    0,    0,    0,    0,    0,    2,    3,    4,    5,
    0,    0,    0,    0,    6,    0,    0,    0,    0,    0,
    0,    7,   34,    8,   38,    9,   10,   11,   36,   49,
    0,    0,    0,    0,   12,
);
@yycheck = (                                             10,
   10,   10,   10,    8,  268,   26,  272,   10,  269,  270,
  273,   16,   17,  261,   19,   20,  280,  281,   58,  283,
  284,   10,   27,   28,   45,   30,   31,   32,   33,   34,
  286,   36,  264,  265,  273,  267,  268,  262,  263,   10,
   11,  282,  282,  275,   49,  282,  266,  266,  266,   10,
    9,   -1,   57,   -1,   -1,   -1,   61,   -1,   -1,   -1,
   -1,   66,   67,   -1,   69,   -1,   -1,   -1,   -1,   -1,
   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
   -1,   -1,   -1,   -1,   -1,   -1,  257,  258,  259,  260,
   -1,   -1,   -1,   -1,  265,   -1,   -1,   -1,   -1,   -1,
   -1,  272,  271,  274,  272,  276,  277,  278,  271,  279,
   -1,   -1,   -1,   -1,  285,
);
$YYFINAL=1;
#ifndef YYDEBUG
#define YYDEBUG 0
#endif
$YYMAXTOKEN=286;
#if YYDEBUG
@yyname = (
"end-of-file",'','','','','','','','','',"'\\n'",'','','','','','','','','','','','','','','','','','','','',
'','','','','','','','','','','','','','','','','','','','','','','','','','','',"':'",'','','','','','','','','','',
'','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','',
'','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','',
'','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','',
'','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','',
'','','','','','','','','','','','','','','','','','','','','','','','','','','','',"NNTP","SELECT","MBOX",
"COLLECT","STATISTICS","IN","ON","SEARCH","UNLESS","PATTERNS","PAIRS","REGEXP",
"IPADR","FQDN","PORT","NEWSGROUP","FILESPEC","LIST","VETO","DESELECT","WHERE",
"REQUIRE","RANGE","WORD","WORDSTEM","WORDEXP","PHRASE","OUTLINE","MBOXFORMAT",
"FORMAT",
);
@yyrule = (
"\$accept : stmt_list",
"stmt_list :",
"stmt_list : stmt_list statement",
"statement : terminator",
"statement : NNTP IPADR terminator",
"statement : NNTP FQDN terminator",
"statement : NNTP IPADR PORT terminator",
"statement : NNTP FQDN PORT terminator",
"statement : SELECT newsgroup_list terminator",
"statement : DESELECT newsgroup_list terminator",
"statement : WHERE search_expr terminator",
"statement : REQUIRE search_expr terminator",
"statement : UNLESS search_expr terminator",
"statement : MBOX FILESPEC terminator",
"statement : MBOXFORMAT FORMAT terminator",
"statement : COLLECT STATISTICS IN FILESPEC terminator",
"statement : COLLECT STATISTICS ON REGEXP terminator",
"statement : COLLECT STATISTICS ON SEARCH PATTERNS terminator",
"statement : COLLECT STATISTICS ON UNLESS PATTERNS terminator",
"statement : COLLECT STATISTICS ON VETO PATTERNS terminator",
"statement : COLLECT STATISTICS ON PAIRS",
"statement : NEWSGROUP ':' RANGE terminator",
"statement : NEWSGROUP ':' terminator",
"statement : LIST terminator",
"search_expr : WORD WORDEXP",
"search_expr : WORDSTEM WORDEXP",
"search_expr : PHRASE word_list",
"search_expr : OUTLINE word_list",
"search_expr : REGEXP",
"word_list : WORDEXP word_list",
"word_list : WORDEXP",
"terminator : '\\n'",
"newsgroup_list : NEWSGROUP",
"newsgroup_list : newsgroup_list NEWSGROUP",
);
#endif
sub yyclearin { $yychar = -1; }
sub yyerrok { $yyerrflag = 0; }
$YYSTACKSIZE = $YYSTACKSIZE || $YYMAXDEPTH || 500;
$YYMAXDEPTH = $YYMAXDEPTH || $YYSTACKSIZE || 500;
$yyss[$YYSTACKSIZE] = 0;
$yyvs[$YYSTACKSIZE] = 0;
sub YYERROR { ++$yynerrs; &yy_err_recover; }
sub yy_err_recover
{
  if ($yyerrflag < 3)
  {
    $yyerrflag = 3;
    while (1)
    {
      if (($yyn = $yysindex[$yyss[$yyssp]]) && 
          ($yyn += $YYERRCODE) >= 0 && 
          $yycheck[$yyn] == $YYERRCODE)
      {
#if YYDEBUG
       print "yydebug: state $yyss[$yyssp], error recovery shifting",
             " to state $yytable[$yyn]\n" if $yydebug;
#endif
        $yyss[++$yyssp] = $yystate = $yytable[$yyn];
        $yyvs[++$yyvsp] = $yylval;
        next yyloop;
      }
      else
      {
#if YYDEBUG
        print "yydebug: error recovery discarding state ",
              $yyss[$yyssp], "\n"  if $yydebug;
#endif
        return(1) if $yyssp <= 0;
        --$yyssp;
        --$yyvsp;
      }
    }
  }
  else
  {
    return (1) if $yychar == 0;
#if YYDEBUG
    if ($yydebug)
    {
      $yys = '';
      if ($yychar <= $YYMAXTOKEN) { $yys = $yyname[$yychar]; }
      if (!$yys) { $yys = 'illegal-symbol'; }
      print "yydebug: state $yystate, error recovery discards ",
            "token $yychar ($yys)\n";
    }
#endif
    $yychar = -1;
    next yyloop;
  }
0;
} # yy_err_recover

sub yyparse
{
#ifdef YYDEBUG
  if ($yys = $ENV{'YYDEBUG'})
  {
    $yydebug = int($1) if $yys =~ /^(\d)/;
  }
#endif

  $yynerrs = 0;
  $yyerrflag = 0;
  $yychar = (-1);

  $yyssp = 0;
  $yyvsp = 0;
  $yyss[$yyssp] = $yystate = 0;

yyloop: while(1)
  {
    yyreduce: {
      last yyreduce if ($yyn = $yydefred[$yystate]);
      if ($yychar < 0)
      {
        if (($yychar = &yylex) < 0) { $yychar = 0; }
#if YYDEBUG
        if ($yydebug)
        {
          $yys = '';
          if ($yychar <= $#yyname) { $yys = $yyname[$yychar]; }
          if (!$yys) { $yys = 'illegal-symbol'; };
          print "yydebug: state $yystate, reading $yychar ($yys)\n";
        }
#endif
      }
      if (($yyn = $yysindex[$yystate]) && ($yyn += $yychar) >= 0 &&
              $yycheck[$yyn] == $yychar)
      {
#if YYDEBUG
        print "yydebug: state $yystate, shifting to state ",
              $yytable[$yyn], "\n"  if $yydebug;
#endif
        $yyss[++$yyssp] = $yystate = $yytable[$yyn];
        $yyvs[++$yyvsp] = $yylval;
        $yychar = (-1);
        --$yyerrflag if $yyerrflag > 0;
        next yyloop;
      }
      if (($yyn = $yyrindex[$yystate]) && ($yyn += $yychar) >= 0 &&
            $yycheck[$yyn] == $yychar)
      {
        $yyn = $yytable[$yyn];
        last yyreduce;
      }
      if (! $yyerrflag) {
        &yyerror('syntax error');
        ++$yynerrs;
      }
      return(1) if &yy_err_recover;
    } # yyreduce
#if YYDEBUG
    print "yydebug: state $yystate, reducing by rule ",
          "$yyn ($yyrule[$yyn])\n"  if $yydebug;
#endif
    $yym = $yylen[$yyn];
    $yyval = $yyvs[$yyvsp+1-$yym];
    switch:
    {
if ($yyn == 4) {
#line 22 "nscan.y"
{ $label = NNTP; 
	  $them = $yyvs[$yyvsp-1]; 
last switch;
} }
if ($yyn == 5) {
#line 25 "nscan.y"
{ $label = NNTP; 
	  $them = $yyvs[$yyvsp-1]; 
last switch;
} }
if ($yyn == 6) {
#line 28 "nscan.y"
{ $label = NNTP; 
	  $them = $yyvs[$yyvsp-2]; 
	  $port = $yyvs[$yyvsp-1]; 
last switch;
} }
if ($yyn == 7) {
#line 32 "nscan.y"
{ $label = NNTP; 
	  $them = $yyvs[$yyvsp-2]; 
	  $port = $yyvs[$yyvsp-1]; 
last switch;
} }
if ($yyn == 8) {
#line 36 "nscan.y"
{
    push(@selectedGroups, @groupList);
    foreach $group (@selectedGroups)
    {
	$range{$group} = 
	  '' unless $range{$group}; # blank range
    }
    @groupList = ();		# clear list of newsgroups

last switch;
} }
if ($yyn == 9) {
#line 46 "nscan.y"
{
    push(@deselectedGroups, @groupList);
    @groupList = ();

last switch;
} }
if ($yyn == 10) {
#line 51 "nscan.y"
{
    push(@whereList, $yyvs[$yyvsp-1]);	
# $2 is value of search expression
    push(@theSearchPatterns, $yyvs[$yyvsp-1]);
    foreach $Group (@selectedGroups)
    {				# 
# build Perl regular expression for case insensitive word search
	
	if($pattern{$Group})
	{			# 
	    $pattern{$Group} = 
	      join("\034", $pattern{$Group}, $yyvs[$yyvsp-1]);
	}			# 
	else
	{
	    $pattern{$Group} = $yyvs[$yyvsp-1];
	}
    } # close loop over selected groups


last switch;
} }
if ($yyn == 11) {
#line 72 "nscan.y"
{ 
    push(@requireList, $yyvs[$yyvsp-1]); 
    foreach $Group (@selectedGroups)
    {
	if($required{$Group})
	{
	    $required{$Group} = 
	      join("\034", $required{$Group}, $yyvs[$yyvsp-1]);
	}
	else
	{
	    $required{$Group} = $yyvs[$yyvsp-1];
	}
    }
    push(@theSearchPatterns, $yyvs[$yyvsp-1]);

last switch;
} }
if ($yyn == 12) {
#line 89 "nscan.y"
{ 
    push(@unlessList, $yyvs[$yyvsp-1]); 
    foreach $Group (@selectedGroups)
    {
	if($veto{$Group})
	{
	    $veto{$Group} = 
	      join("\034", $veto{$Group}, $yyvs[$yyvsp-1]);
	}
	else
	{
	    $veto{$Group} = $yyvs[$yyvsp-1];
	}
    }
    push(@theVetoPatterns, $yyvs[$yyvsp-1]);


last switch;
} }
if ($yyn == 13) {
#line 107 "nscan.y"
{
    $mbox = $yyvs[$yyvsp-1];
    $mbox =~ s/^~/$ENV{'HOME'}/; 
# translate leader tilde to HOME directory
    $outFile = $yyvs[$yyvsp-1]; 

last switch;
} }
if ($yyn == 14) {
#line 114 "nscan.y"
{
#
# this concept to add support for MMDF courtesy of 
# Tim O'Malley
#
    $MboxFormat = $yyvs[$yyvsp-1];
    die "$0: Invalid MBOXFORMAT $MboxFormat in configuration file."
      unless $MboxFormat =~ /unix|elm|mmdf/io;

last switch;
} }
if ($yyn == 15) {
#line 124 "nscan.y"
{ 
		$doCollect = $TRUE;
		$statFile = $yyvs[$yyvsp-1]; 
		$statFile =~ s/^\~/$ENV{'HOME'}/;
	
last switch;
} }
if ($yyn == 16) {
#line 130 "nscan.y"
{
		$doCollect = $TRUE;
		$statistics{$yyvs[$yyvsp-1]} = 0; # initialize pattern histogram
	
last switch;
} }
if ($yyn == 17) {
#line 135 "nscan.y"
{
		$doCollect = $TRUE;
		$doSearchPatterns = $TRUE;
	
last switch;
} }
if ($yyn == 18) {
#line 140 "nscan.y"
{
		$doCollect = $TRUE;
		$doVetoPatterns = $TRUE;
	
last switch;
} }
if ($yyn == 19) {
#line 145 "nscan.y"
{
	    $doCollect = $TRUE;
	    $doVetoPatterns = $TRUE;
	
last switch;
} }
if ($yyn == 20) {
#line 150 "nscan.y"
{
		$doCollect = $TRUE;
		$doPairs = $TRUE;
		%pairs = ();
	
last switch;
} }
if ($yyn == 21) {
#line 156 "nscan.y"
{
		$range{$yyvs[$yyvsp-3]} = $yyvs[$yyvsp-1];
	
last switch;
} }
if ($yyn == 22) {
#line 160 "nscan.y"
{
		$range{$yyvs[$yyvsp-2]} = '';
	
last switch;
} }
if ($yyn == 23) {
#line 164 "nscan.y"
{
		print "NNTP Server is $them \n";
		print "Port is $port \n";
                print "Statistics file is $statFile \n";
		print "doCollect is $readable{$doCollect} \n";
		print "doSearchPatterns is $readable{$doSearchPatterns} \n";
		print "doVetoPatterns is $readable{$doVetoPatterns} \n";
		print "doPairs is $readable{$doPairs} \n";
		print @whereList, "\n";
		print @requireList, "\n";
		print @unlessList, "\n";
		print @groupList, "\n"; print "\n";
		print "Selected Groups are @selectedGroups \n";
		print "Patterns are ", %pattern, " \n";
		print "Required patterns are ", %required, " \n";
		print "Vetoed patterns are ", %veto, " \n";
		print %range, "\n";
	
last switch;
} }
if ($yyn == 24) {
#line 186 "nscan.y"
{
    $newpat = '/\b' . $yyvs[$yyvsp-0] . '\b/i'; 
# translate word to Perl regular expr
    $yyval = $newpat;

last switch;
} }
if ($yyn == 25) {
#line 192 "nscan.y"
{
    $newpat = '/\b' . $yyvs[$yyvsp-0] . '\w*/i'; 
# translate word stem 
# to Perl regular expr
    $yyval = $newpat;

last switch;
} }
if ($yyn == 26) {
#line 199 "nscan.y"
{
# build search expression using Perl regular expressions
  @words = split("\034", $yyvs[$yyvsp-0]);
  $newpat = '/' . join('\s+', @words) . '/i';
  $yyval = $newpat;

last switch;
} }
if ($yyn == 27) {
#line 206 "nscan.y"
{
# an outline is a sequence of words that may contain intervening
# words.  For example, the outline "the red flower" would match
# "the red flower", "the beautiful red flower", 
# "the red hibiscus flower" etc.
#
  @words = split("\034", $yyvs[$yyvsp-0]);
  $newpat = '/' . join('[\s\w\'\-]+', @words) . '/i';
  $yyval = $newpat;

last switch;
} }
if ($yyn == 28) {
#line 217 "nscan.y"
{
	 $yyval = $yyvs[$yyvsp-0];		# straight Perl regular expression
     
last switch;
} }
if ($yyn == 29) {
#line 223 "nscan.y"
{ $yyval = join("\034", $yyvs[$yyvsp-1], $yyvs[$yyvsp-0]); 
last switch;
} }
if ($yyn == 30) {
#line 225 "nscan.y"
{
    $yyval = $yyvs[$yyvsp-0];

last switch;
} }
if ($yyn == 32) {
#line 233 "nscan.y"
{ push(@groupList, $yyvs[$yyvsp-0]); 
last switch;
} }
if ($yyn == 33) {
#line 235 "nscan.y"
{ push(@groupList, $yyvs[$yyvsp-0]); 
last switch;
} }
#line 617 "y.tab.pl"
    } # switch
    $yyssp -= $yym;
    $yystate = $yyss[$yyssp];
    $yyvsp -= $yym;
    $yym = $yylhs[$yyn];
    if ($yystate == 0 && $yym == 0)
    {
#if YYDEBUG
      print "yydebug: after reduction, shifting from state 0 ",
            "to state $YYFINAL\n" if $yydebug;
#endif
      $yystate = $YYFINAL;
      $yyss[++$yyssp] = $YYFINAL;
      $yyvs[++$yyvsp] = $yyval;
      if ($yychar < 0)
      {
        if (($yychar = &yylex) < 0) { $yychar = 0; }
#if YYDEBUG
        if ($yydebug)
        {
          $yys = '';
          if ($yychar <= $#yyname) { $yys = $yyname[$yychar]; }
          if (!$yys) { $yys = 'illegal-symbol'; }
          print "yydebug: state $YYFINAL, reading $yychar ($yys)\n";
        }
#endif
      }
      return(0) if $yychar == 0;
      next yyloop;
    }
    if (($yyn = $yygindex[$yym]) && ($yyn += $yystate) >= 0 &&
        $yyn <= $#yycheck && $yycheck[$yyn] == $yystate)
    {
        $yystate = $yytable[$yyn];
    } else {
        $yystate = $yydgoto[$yym];
    }
#if YYDEBUG
    print "yydebug: after reduction, shifting from state ",
        "$yyss[$yyssp] to state $yystate\n" if $yydebug;
#endif
    $yyss[++$yyssp] = $yystate;
    $yyvs[++$yyvsp] = $yyval;
  } # yyloop
} # yyparse
#line 239 "nscan.y"

sub yylex
{

lexloop:
    {
	# get a line of input
	if($line eq '')
	{
	#    &prompt;
	    $line = $config[$yyi];
	    $yyi++;		# increment pointer to line in config file
	    if(!$line || $line eq '')
	    {
	      return(0);
	    }
	}

	$line =~ s/^[ \t\f\r]*(.|\n)//
	    || next lexloop;

	local($char) = $1;

	if($char eq '#')
	{
	    $line = "\n";
	    &yylex;
	}
	elsif( $yylex_mode == $WORD_MODE && $char =~/^[\w\'\-]/ 
&& $line =~ s/^([\w\'\-]*)// )
	  {
	    $yylval = $char.$1;
	    $WORDEXP;
	  }
	elsif( $yylex_mode == $ROOT_MODE && $char =~ /^N/ 
&& $line =~ s/^NTP// )
	{
	    $yylex_mode = $NNTP_MODE;
	    $NNTP;
	}
	elsif( $yylex_mode == $NNTP_MODE && $char =~ /^[\w\+\-]/ 
&& $line =~ s/^([\w\+\-]*\.?([\w\+\-\*]+\.?)+)// )
	{			# looking for Fully Qualified Domain Name
	    $yylval = $char.$1;
	    $FQDN;
	}
	elsif( $yylex_mode == $NNTP_MODE && $char =~ /^\d/ 
&& $line =~ s/^(\d*\.\d+\.\d+\.\d+)// )
	{			# looking for Internet Protocal Address
	    $yylval = $char.$1;
	    $IPADR;
	}
	elsif( $yylex_mode == $ROOT_MODE && $char =~ /^S/ 
&& $line =~ s/^ELECT//)
	{
	    $yylex_mode = $NEWSGROUP_MODE;
	    $SELECT;		# select newsgroups to search
	}
	elsif( $yylex_mode == $ROOT_MODE && $char =~ /^D/ 
&& $line =~ s/^ESELECT// )
	{
	    $yylex_mode = $NEWSGROUP_MODE;
	    $DESELECT;		# deselect newsgroups to search
	}
	elsif( $yylex_mode == $ROOT_MODE && $char =~ /^\d/ 
&& $line =~ s/^(\d*(\-\d+)?(,(\d+(\-\d+)?))*)// )
	{			# excluded range token processed here
	    $temp = $char.$line;	# 
#	    ($yylval,$rest) = split(/ /, $temp, 2);
	    $yylval = $char.$1;	# 
	    $RANGE;		# 
	}
	elsif( $yylex_mode == $ROOT_MODE && $char =~ /^W/ 
&& $line =~ s/^HERE// )
	{
	    $WHERE;
	}
	elsif( $yylex_mode == $ROOT_MODE && $char =~ /^W/ 
&& $line =~ s/^ORD\b// )
	{
	    $yylex_mode = $WORD_MODE; # a WORD token can overlap a file name  
	    $WORD;		# hence use of WORD and FILE mode
	}
	elsif( $yylex_mode == $ROOT_MODE && $char =~ /^W/ 
&& $line =~ s/^ORDSTEM\b// )
	{
	    $yylex_mode = $WORD_MODE; # see comment above
	    $WORDSTEM;
	}
	elsif( $yylex_mode == $ROOT_MODE && $char =~ /^P/ 
&& $line =~ s/^HRASE// )
	{
	    $yylex_mode = $WORD_MODE; # see comment above
	    $PHRASE;
	}
	elsif( $yylex_mode == $ROOT_MODE && $char =~ /^O/ 
&& $line =~ s/^UTLINE// )
	{
	    $yylex_mode = $WORD_MODE;
	    $OUTLINE;
	}
	elsif( $yylex_mode == $ROOT_MODE && $char =~ /^U/ 
&& $line =~ s/^NLESS// )
	{
	    $UNLESS;
	}
	elsif( $yylex_mode == $ROOT_MODE && $char =~ /^V/ 
&& $line =~ s/^ETO// )
	{
	    $VETO;
	}
	elsif( $yylex_mode == $ROOT_MODE && $char =~ /^R/ 
&& $line =~ s/^EQUIRE// )
	{
	    $REQUIRE;
	}
	elsif( $yylex_mode == $ROOT_MODE && $char =~ /^S/ 
&& $line =~ s/^EARCH// )
	{
	    $SEARCH;
	}
	elsif( $yylex_mode == $ROOT_MODE && $char =~ /^M/ 
&& $line =~ s/^BOXFORMAT// )
	{
	    $yylex_mode = $FMT_MODE;
	    $MBOXFORMAT;
	}
	elsif( $yylex_mode == $FMT_MODE && $char =~/^\w/ 
&& $line =~ s/(\w*)// )
	{
	    $yylval = $char.$1;
	    $FORMAT;
	}
	elsif( $yylex_mode == $ROOT_MODE && $char =~ /^M/ 
&& $line =~ s/^BOX// )
	{
	    $yylex_mode = $FILE_MODE;
	    $MBOX;
	}
	elsif( $yylex_mode == $FILE_MODE && $char =~ /^[\w\+\/\-\~]/ 
&& $line =~ s/^([\w\/\+\-\.]*)// )
	{
	    $yylval = $char.$1;
	    $FILESPEC;
	}
	elsif( $yylex_mode == $ROOT_MODE && $char =~ /^C/ 
&& $line =~ s/^OLLECT// )
	{
	    $COLLECT;
	}
	elsif( $yylex_mode == $ROOT_MODE 
&& $char =~ /^I/ && $line =~ s/^N// )
	{
	    $yylex_mode = $FILE_MODE;
	    $IN;
	}
	elsif( $yylex_mode == $ROOT_MODE 
&& $char =~ /^O/ && $line =~ s/^N// )
	{
	    $ON;
	}
	elsif( $yylex_mode == $ROOT_MODE && $char =~ /^P/ 
&& $line =~ s/^ATTERNS// )
	{
	    $PATTERNS;
	}
	elsif( $yylex_mode == $ROOT_MODE && $char =~ /^P/ 
&& $line =~ s/^AIRS// )
	{
	    $PAIRS;
	}
	elsif( $yylex_mode == $ROOT_MODE && $char =~ /^S/ 
&& $line =~ s/^TATISTICS// )
	{
	    $STATISTICS;
	}
	elsif( $yylex_mode == $ROOT_MODE && $char =~ /^\// 
&& $line =~ s/^(.*\/[igo]?)// )
	{
	    $yylval = $char.$1;
	    $REGEXP;
	}
	elsif( $yylex_mode == $ROOT_MODE && $char =~ /^L/ 
&& $line =~ s/^IST// )
	{
	    $LIST;
	}
	elsif( ( $yylex_mode == $NEWSGROUP_MODE ||
		$yylex_mode == $ROOT_MODE )
&& $char =~ /^[\w\-\+\*\?]/ 
&& $line =~ s/^([\w\+\-\*\?]*(\.([\w\+\-\*\?]+\.?)+)?)// )
	{			# read newsgroup name token
	    $yylval = $char.$1; # can overlap file names or FQDN
	    $NEWSGROUP;		# hence use of ROOT, FILE, and NNTP modes
	}
	elsif( $char eq "\n")	
# terminator - don't care about mode of scanner
	{
	    $yylex_mode = $ROOT_MODE; # reset mode of lexical scanner
	    $yylval = $char;
	    ord($char);
	}
	else
	{
	    $yylval = $char;
	    ord($char);
	}
    }				# close lexloop
}				# end of sub yylex

sub yyerror
{
    $cfgvalid = $FALSE;		# problem in configuration file
    if ( !$opt_X )		# not runing in X Window mode
    {
	print STDERR "Configuration file $configFile line $yyi; ", @_, "\n";
    }
}
#
#	Name: newscan
#	Date: $Date: 1995/04/10 02:15:57 $
#	Version: $Revision: 2.2 $
#	Author: John F. McGowan, Ph.D. ($Author: jfm $)
#       Author E-Mail: jfm@jupiter.slac.stanford.edu
#
#	Description:
#
#		newscan is a utility to scan netnews for articles that
#       contain matches to regular expressions.  
#	newscan can also exclude articles that contain
#       matches to selected regular expressions.  newscan is writen
#       in Perl (Practical Extraction and Report Language).  Perl is
#	available for essentially all Unix systems, as well as some
#       non-Unix systems.  A good source of information on Perl
#       is "Programming Perl" by Larry Wall and Randal L. Schwartz.
#       
# 	Articles that contain matches are stored in a file 
#       in Unix mailbox format.
#	This file may be read using mail readers such as mail, elm, etc.
#
#		Search is controlled by a resource file .newscanrc (by 
#       default) in the current directory.  the default file may overridden
#	through the environment variable 
#       NEWSCAN, e.g. setenv NEWSCAN /me/.myrc.
#		newscan is intended to be run as a 
#       background job, e.g. newscan &
#	since it takes a while to scan selected newsgroups for articles of
#	interest.
#


require 'sys/socket.ph';	# handle machine dependencies for sockets
require "getopts.pl";

# newscan initialization code

#
# newscan can 
# play an audio file in Sun/Next format to indicate completion of a
# search.  Require audio file to be in PATH of user.
#
@dirs = split(/:/, $ENV{'PATH'});
foreach $dir ( @dirs )
{
    $audio_file = $dir . "\/newscan.au";
    if(-e $audio_file)
    {
	last;		# terminate the loop
    }
}				# loop over directories in path ends

$TRUE = 1;			# human readable Boolean true
$FALSE = 0;			# human readable Boolean false

$NAME{$TRUE} = 'TRUE';
$NAME{$FALSE} = 'FALSE';

$ROOT_MODE = 0;			# root mode of lexical scanner
$NNTP_MODE = 1;	     # NNTP mode (reading an address for NNTP server )
$WORD_MODE = 2;			# WORD mode (reading a word )
$FILE_MODE = 3;			# reading a filename
$FMT_MODE = 4;			# reading a mailbox file format
$NEWSGROUP_MODE = 5;		# reading newsgroup names after
				# SELECT or DESELECT

$line = '';                     # buffer used by yylex subroutine

$oldTime = time();		# a timer to fire reporting in X server mode
$total_articles_scanned = 0;    # just what it says
$total_articles_found = 0;      # means what it says


$MboxFormat = "unix";	      # default to standard Unix mail mailbox format
			      # options include ELM and MMDF

$yylex_mode = $ROOT_MODE;     # initial mode for the newscan lexical scanner


&Getopts('c:r:H:X:hesa');
                       # -c takes argument configuration file specification
                       # -e indicates edit configuration file before search
		                # -r invokes mail reader
                                # -H <hostname> specifies Internet 
                                # domain name of local host explicity 
                                # from command line
				# -X <arg> invokes newscan in mode 
# where it is running under an X Window System shell such as xnewscan
		                # -h is help
				# -s is silent mode
				# -a audio mode (play audio messages)
if( $opt_H )
{
    $hostname = $opt_H;
}

if( ! $opt_s && ! $opt_X )	# if not silent give status message
{
# extract the newscan version number for vanity message
    $the_version = '$Revision: 2.2 $';  # use single quotes to avoid 
                                          # evaluation of $Revision as a 
                                          # perl variable
    ($head, $ver, $rest) = split(/ /, $the_version, 3);
 print "Running newscan newsreader version $ver by John F. McGowan, Ph.D. ";
 # initial message 
    print "\n"; # new line
}

@cPath = ( "/bin", "/usr/local/bin", "/usr/ucb", "/usr/bin");

@readers = ( "elm", "Mail", "mailx" );  # standard mail readers

if($opt_r)
{
	if($ENV{'READER'})
	{
		$myReader = $ENV{'READER'};
		$foundReader = 1;  # indicate that reader has been found
	}
	else
	{
# select a reasonable default mail reader - prefers elm if exists
		$foundReader = 0;
		foreach $command (@readers)
		{
			if(!$foundReader)
			{
			foreach $path (@cPath)
			{
			  if(-e "$path/$command" && -x "$path/$command")
			    {
			      $myReader = $path . "/" . $command;
			      $foundReader = 1;
			    }
			} # close loop over paths
			} # close if not foundReader
		}
#		$myReader = 'elm';  # use elm mail reader for now
	}

	if(!$foundReader)
	{
	   print "newscan problem!  Could not find a mail reader from ",
"built in list @readers in any of @cPath directories!\n ",
"Please set the READER environment variable to a reader on your system!\n";
		die;
	}

#	print "Checking if file $opt_r exists \n";

	if(-e $opt_r)		# file exists
	{
# some newsgroups may contain binary executables so don't do this for now
#	    if(-T $opt_r)	# file is a text file
#	    {
#                print "Please wait!  newscan using $myReader mail reader to 
# read $opt_r file containing results of a search. \n";   
# let the user know this may take a while
		exec("$myReader -f $opt_r");
#	    }
#	    else
#	    {
#		die "newscan error: Folder file $opt_r is not a text file!";
#	    }
	}
	else			# let user know folder doesn't exist
	{
	    die "newscan error: Folder of articles $opt_r does not exist!  
Cannot read!";
	}

}

if($opt_h)
{
	print <<"EndOfHelp";

newscan -- a network news scanner  ( Version $Revision: 2.2 $ )

	newscan searches selected Internet network news groups
	for articles that match perl regular expressions.  newscan
	implements a boolean query key-pattern full-text information
	retrieval system.  In plain English, this means newscan scans
	the complete text of each article for matches to various
	combinations of perl regular expressions.

	Command Line Options:

		-c <file-specification>   

			This flag selects the configuration file that
		tells newscan which newsgroups to search and what 
		patterns to search for.  If this option flag is not
		specified, newscan uses the default configuration file
		.newscanrc in the user's home directory or specified by
		the environment variable NEWSCAN.

		-e

			This flag indicates that the configuration file
		should be edited before doing the search.  newscan will
		pop the user into the editor specified by the EDITOR
		environment variable.  If no configuration file exists,
		newscan provides a template configuration file that the
		user should edit.

		-r <file-specification>

			This flag invokes a mail reader on the mailbox
		format file specified by the file-specification.  Should
		be the file containing the articles found by newscan in
		a search.  The user should set the environment variable
		READER to his or her favorite mail reader.  Otherwise,
		newscan will select a mail reader, trying to use elm
		first if it exists.

                -H <name-of-local-host>

                        This flag explicitly sets the name of the local
                host.  For example, -H 440.rahul.net

                -s

                        Silent mode.  Suppress progress messages issued by
                newscan during search.

		-h

			Output this help message.

EndOfHelp
	exit;
}

@Months = ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug',
'Sep','Oct','Dec');

%DaysInMonth = ( 'Jan', '31',
		 'Feb', '28',  # 1992 is leap year with 29 days
		 'Mar', '31',
		 'Apr', '30',
		 'May', '31',
		 'Jun', '30',
		 'Jul', '31',
		 'Aug', '31',
		 'Sep', '30',
		 'Oct', '31',
		 'Nov', '30',
		 'Dec', '31',
);

@DaysOfWeek = ('Mon','Tue','Wed','Thu','Fri','Sat','Sun');

%NewsArticle = ();


# read the configuration file (default to .newscanrc)
if($opt_c)
{
# configuration file is specified at command line.
	$configFile = $opt_c;
	$configFile =~ s/^\~/$ENV{'HOME'}/;
}
else
{
	if($ENV{'NEWSCAN'})
	{
		$configFile = $ENV{'NEWSCAN'};
	}
	else # default configuration file in user's home directory
	{
		$configFile = $ENV{'HOME'} . '/.newscanrc';
	}
}
# edit the configuration file before start if -e

@editors = ( 'pico', 'emacs', 'vi' );

if($ENV{'EDITOR'})
{
	$myeditor = $ENV{'EDITOR'};
}
else
{
	$foundEditor = 0;
	foreach $command (@editors)
	{
		foreach $path (@cPath)
		{
			if(-e "$path/$command" && -x "$path/$command")
			{
				$myeditor = $path . "/" . $command;
				$foundEditor = 1;
			}
		}
		last if $foundEditor;
	}
	die "Could not find an editor!\n" unless $foundEditor;
#	$myeditor = 'emacs';  # I am an emacs snob
}

if($opt_e)
{
	if(-e $configFile)
	{
# do nothing if configuration file already exists
	}
	else # file does not exist - provide a form
	{
		open(CONFIG,">$configFile");
		print CONFIG <<"EndOfConfig";
# Template Configuration file for newscan Internet news scanner
#
# Note:  Lines beginning with # are comments.  Remove leading # and
#        edit line if you wish to activate line.
#
# Line following: specify the Internet address of the NNTP 
# server for the system.
# The NNTP server is the machine where the Internet news is stored.
# OR this line may be omitted if you use the NNTPSERVER environment variable
# This line takes precedence over NNTPSERVER variable.
NNTP <nntphost> 
# specify the mailbox format file for the found articles
MBOX <file-for-found-articles>
# specify the Internet newsgroups to be searched (space delimited list)
SELECT <list of newsgroups>
# retrieve a news article if it contains a match to regular expression
#WHERE /perl regular expression/[i]
# do not retrieve a news article if it contains a match to UNLESS 
# regular expression
#UNLESS /perl regular expression/[i]
# a news article must contain a match to a REQUIRE regular expression to
#  be retrieved.
#REQUIRE /perl regular expression/[i]
# collect statistics on search in a file -- to aid in refining 
# search criteria
#COLLECT STATISTICS IN <file-specfication>
# collect statistics on frequency of articles that match a pattern
#COLLECT STATISTICS ON /perl regular expression/i
# collect statistics on search patterns specified by WHERE and REQUIRE lines
#COLLECT STATISTICS ON SEARCH PATTERNS
# collect statistics on unless patterns specified by UNLESS
#COLLECT STATISTICS ON UNLESS PATTERNS
# collect statistics on frequency of articles that match pairs of patterns
#COLLECT STATISTICS ON PAIRS
#<newsgroup>:<excluded range>
#
EndOfConfig
		close(CONFIG);
	}
	system("$myeditor $configFile");  # edit the configuration file
	print "Do you wish to do this search (y/n):";
	$ans = <STDIN>;
	exit 0 if $ans =~ /[Nn]/;
}


open(CONFIG,"$configFile");  # open configuration file
@config = <CONFIG>;   # array @config now contains a copy of config file
close(CONFIG);
#
$newgroup = 'NULL';
if (@groups) { undef @groups };	# try to free memory
@groups = ();  # null list of groups 

# parse the configuration file
$mbox = "newscanBox";  # default mailbox file for found articles
$port = 119;           # default to 119 as NNTP server port

$cfgvalid = $TRUE;		# assume configuration file valid to start

&parse(*config);		# parse subroutine - interface to yyparse


if( $opt_X )
{
#
#  1xx - informative message
#  2xx - Command ok
#  3xx - Command ok so far
#  4xx - Command was correct, but couldn't be performed for some reason
#  5xx - Command unimplemented, or incorrect, or a serious program error
#   occured
#
    if($opt_X eq "VERIFY")	# just check that config file is valid
    {
	print "100 $NAME{$cfgvalid}\n"; # report validity of 
                                        # configuration file (query)
	exit;
    }
    elsif ($opt_X eq "MBOX" )
    {
	print "101 $mbox\n";	# report mailbox format file
	exit;
    }
    elsif ($opt_X eq "STAT")
    {
	print "102 $statFile\n"; # report statistics file
	exit;
    }
    elsif ($opt_X eq "QUERY")
    {
	print "100 $NAME{$cfgvalid}\n";
	print "101 $mbox\n";
	print "102 $statFile\n";
	exit;
    }
    elsif ($opt_X eq "RUN")
    {
#
# this mode runs newscan as a server sending information to an X windows
# client via a regular pipe
#
	$| = 1;   # need unbuffered output for 
                  # pipe InterProcess Communication
	print "103 Running $0\n";
    }
    else
    {
	print STDERR "501 Unrecognized command\n";
	exit;
    }
}



&FixRange(*range);


if($doCollect)
{
	$statistics{'ALL'} = 0;  # count of number of articles scanned
	$statistics{'FOUND'} = 0; # count of number of articles found
}

# add search patterns to patterns to collect statistics for
# if requested
if($doSearchPatterns)
{
# append search patterns to statistics
foreach $pattern (@theSearchPatterns)
{
	$statistics{$pattern} = 0;
}
}

if($doVetoPatterns)
{
# append search patterns to statistics
foreach $pattern (@theVetoPatterns)
{
	$statistics{$pattern} = 0;
}
}

# open mailbox file to store found newsarticles
open(MBOX,">>$mbox");
#
# connect to the NNTP server
# $port = 119 unless $port;
#
$them = $ENV{'NNTPSERVER'} unless $them; 
# use NNTPSERVER environment variable


$AF_INET = &AF_INET;		# &AF_INET defined in sys/socket.ph
$SOCK_STREAM = &SOCK_STREAM;	# = 2 for Irix 5 ( 1 for Irix 4 )

$sockaddr = 'S n a4 x8';

$hostname = $ENV{'LOCALHOST'} unless $hostname; # get name of local host 
# from environment variable
chop($hostname = `hostname`) unless $hostname;	# get name of local host 
#from Unix hostname utility
$them = $hostname unless $them; # try local machine as NNTP Server

# translate protocol name to associated number
($name,				# human-readable name of protocol
 $aliases,			# human-readable aliases of protocol
 $proto) = getprotobyname('tcp'); # protocol number ( 6 for tcp )

# translates service (port) name to corresponding number
($name,				# name of service (NNTP in this case)
 $aliases,			# aliases for service
 $port,				# port number of service (119 in this case)
 $proto) = getservbyname('nntp' ,'tcp')	# protocol (TCP in this case)
	unless $port =~ /^\d+$/;

# translates network hostname to corresponding number
($name,$aliases,$type,$len,$thisaddr) =
	gethostbyname($hostname);

$this_numeric = unpack('N', $thisaddr);
if( !defined $name ) # something went wrong in gethostbyname
{				# try fully qualified domain name (FQDN)
				# if $hostname from Unix hostname command
    chop($local_host = `hostname`);
    if($hostname eq $local_host)
    {
	chop($domain = `domainname`);
	$fqdn = $local_host . '.' . $domain; 
# try Fully Qualified Domain Name
	($name,		   # Domain name of host
	 $aliases,	   # aliases of the host
	 $type,		   # type of address AF_INET = 2 (IP Address Type)
	 $len,		   # length of address in bytes (4 for IP)
	 $thisaddr) = gethostbyname($fqdn);
    }
}
elsif( $hostname =~ /\d+/ && $this_numeric == $hostname )
{			      # if hostname is strict number gethostbyname
			      # may return that number as the IP address!
    chop($local_host = `hostname`);

    if($hostname eq $local_host) # hostname from hostname command
    {
	chop($domain = `domainname`);
	$fqdn = $local_host . '.' . $domain; 
# try fully qualified domain name
	($name, $aliases, $type, $len, $thisaddr) =
	    gethostbyname($fqdn);
    }
}



# translate network hostname to corresponding number
if($them =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/)
{
	$thataddr = pack('C4',$1,$2,$3,$4);
}
elsif($them =~ /(\w+)(\.\w+)*/)
{
	($name,$aliases,$type,$len,$thataddr) = gethostbyname($them);
}
else
{
	die "Fatal error: NNTP host not specified in proper format!\n";
}
$this = pack($sockaddr, $AF_INET, 0, $thisaddr);
$that = pack($sockaddr, $AF_INET, $port, $thataddr);

# Make the socket a filehandle

if(socket(S, $AF_INET, $SOCK_STREAM, $proto))
{
#	print "socket ok\n";
}
else
{
	die "Fatal Error: socket failed ", $1,"\n";
}

# give the socket an address
if(bind(S, $this))
{
#	print "bind ok\n";
}
else
{
    ($ip1, $ip2, $ip3, $ip4) = unpack('C4', $thisaddr);
    $localaddr = $ip1 . '.' . $ip2 . '.' . $ip3 . '.' . $ip4;
    $this_numeric = unpack('N', $thisaddr);

        if ( $hostname =~ /^\d+$/ )
	{
	    print <<"EofError";
newscan: FATAL ERROR!!  
newscan: Bind to local host $hostname ( IP $localaddr ) failed!
newscan:
newscan: Your official host name ( $hostname ) is a strict number.
newscan: This can cause problems with some implementations of the sockets
newscan: networking software.  The software may interpret the numeric name
newscan: as an Internet address instead of looking up the Internet address
newscan: in the host table or domain name system.
newscan:
newscan: newscan thinks your local host's Internet address is $localaddr
newscan: As a 32 bit number, this address is $this_numeric
newscan:
newscan: If $this_numeric = $hostname ( your official host name )
newscan:    the host table lookup almost certainly failed!!
newscan:
newscan: Quick Fix (works on some systems)
newscan:
newscan:   If your host has a valid alias host name that 
nwescan: is not a strict number, e.g. $hostname.megacorp.com
newscan: specify this alias to newscan via the LOCALHOST environment
newscan: variable or the -H <alias host name> command line argument:
newscan:
newscan:     newscan -H $hostname.megacorp.com
newscan:
EofError

    if ( $opt_a ) # sound effects only if requested by user
{
    &PlayFile($audio_file); # play an audio file in Sun/Next format
}
    die "newscan: Aborting newscan - bye now :-( \n" ;
	}
	else
	{
print <<"EofError";
newscan: ERROR!!  Bind to local host $hostname (IP $localaddr ) failed!
newscan: You may want to check that TCP/IP is up and configured correctly
newscan: on your system.  For example, if you run SLIP as the author
newscan: of newscan does, you may want to verify that SLIP is running.
newscan:
EofError

    if ($opt_a)
{				# play audio file
    &PlayFile($audio_file);
}				# 
die "newscan: Aborting newscan - bye now :-( \n";
}
}

# Call up the server
if(connect(S,$that))
{
#	print "Connect to $them ok\n";
}
else
{
 die "Fatal Error: Apparently, can't connect to $them tcp/ip port $port.",
 "Error: ", $1, "\n";		# list to avoid over 80 char lines
}

#  Set socket to be command buffered
select(S); $| = 1; select(STDOUT);
# loop over groups to search
$_ = <S>;  # read confirmation of connection message from NNTP server

($stat, $rest) = split(/ /,$_,2);	# split connection message

if( $stat == 200 || $stat == 201 )
{
    # on initial connection, NNTP server will return
    # 200 server ready - posting allowed
    # 201 server ready - no posting allowed
    # otherwise there has been a problem
}
else
{
 die "newscan: Abort! NNTP Server $them refused connection with message: ",
  "$stat $rest \n";
}
#
# After connection, send MODE READER command.  MODE READER will put
# INN news servers into mode where they recognize RFC 977 NNTP commands.
#
print S "MODE READER\n";
$_ = <S>; # read reply from server (error if nntpd, 200 xxx if inn)

($stat, $rest) = split(/ /, $_, 2); # split response to MODE READER command

if( $stat == 200)
{
    if(!$opt_X && !$opt_s)
    {
	print "Information: News Server Supports MODE READER command!\n";
    }
}
elsif ( $stat == 500 )
{
    if(!$opt_X && !$opt_s)
    {
	print "Information: News Server Does Not Use MODE READER command.\n";
    }
}
else
{
  die "newscan Fatal Error: Unexpected response to MODE READER command\n";
}

# implement Unix glob-style pattern matching for news group selection
#  * asterisk matches 0 or more characters (* is .* in regular expressions)
#  ? question mark matches any 1 character (? is . in regular expressions)
#

$k = 0;
foreach $Group (@selectedGroups)
{
    if( ($Group =~ /([^\\]|^)\*/) || ($Group =~ /([^\\]|^)\?/) )
    {
	$expandGroup{$Group} = $k; # offset into selectedGroups
    }
    $k++;
}

if(keys expandGroup)
{
    $total_groups = 0;
    $oldTime = time();
    print S "LIST\n";		# retrieve list of valid groups 
                                # from NNTP Server
    $flush_save = $|;
    $| = 1; print "L"; $| = $flush_save;
    if (@validGroups) { undef @validGroups; }
    @validGroups = ();

    $_ = <S>;			# read first line back
    while(! /^\.[^\.].*$/)
    {
	$total_groups++;
	chop; chop;		# remove trailing \r\n
	$line = $_ . "\n";	# add trailing newline
	push(@validGroups, $line);

	$flush_save = $|;
	$| = 1;
	if (! $opt_X )
	{
	    print ".";
	}
	else # running as server for X GUI front end
	{
	    if ( time() - $oldTime > 1 )
	    {
		print "108 $total_groups $#validGroups $line\n";
		$oldTime = time();
	    }
	}
	$| = $flush_save;
	$_ = <S>;		# read next line
    }
}

foreach $vGroup (@validGroups)
{
    ($vGroup, $remainder) = split(/ /,$vGroup);
#    $flush_save = $|;
#    $| = 1; print "."; $| = $flush_save;
}

foreach $Group (keys expandGroup)
{
    if (@subList) { undef @subList; }
    @subList = ();		# null array of groups to substitute
    ($GroupMatch = $Group) =~ s/\*/.*/g; # replace asterisk with .*

    $GroupMatch =~ s/\+/\\\+/g;
    $GroupMatch =~ s/\.([^\*])/\\\.$1/g; # replace . with \.
    $GroupMatch =~ s/\?/\./g;	
# replace ? with . --- needs to follow . --> \.
    $GroupMatch = "^"  .  $GroupMatch;
    $GroupMatch = $GroupMatch .  "\$" ;
    
    foreach $vGroup (@validGroups) # loop over valid Groups
    {
	if ( $vGroup =~ /$GroupMatch/ ) # match valid group to group
	{
	    push(subList, $vGroup);
	}
    }

    if($#subList < 0)
    {
	print STDERR "newscan: Warning $Group doesn't match any Newsgroup ",
"known to News Server!\n"; # says News not NNTP, is clearer.
    }

    splice(@selectedGroups, $expandGroup{$Group}, 1, @subList);
    &RangeSplice(*range, $Group, *subList); # won't work????
    &AssocSplice(*pattern, $Group, *subList);
    &AssocSplice(*required, $Group, *subList);
    &AssocSplice(*veto, $Group, *subList);
}				# end loop over groups to expand

# remove deselected groups 

#print "Just before deselection @deselectedGroups \n";

foreach $dGroup (@deselectedGroups)
{
    $dGroup =~ s/\*/.*/g;	# change * to .* (Perl regexp version)
    $dGroup =~ s/\+/\\\+/g;	# change + to \+
    $dGroup =~ s/\.([^\*])/\\\.$1/g; # change . not followed by * to \.
}

foreach $Group (@selectedGroups)
{
    $match = $FALSE;
    foreach $dGroup (@deselectedGroups)	# loop over deselected groups
    {
	if( $Group =~ /$dGroup/ ) # use =~ to get support for * expansion
	{
	    $match = $TRUE;
	}
    }				# end loop over deselected groups

    if($match)
    {				# 
	delete $range{$Group};
	delete $pattern{$Group};
	delete $required{$Group};
	delete $veto{$Group};
	undef $Group;	# don't undef $dGroup, may match another $Group
    }			# 
}

# DESELECTED News Groups should be removed from @selectedGroups here


# loop over groups that are keys of range array
# this allows different searches for different groups
# %range associative array includes other groups 
# such as cross posting groups

foreach $group (sort keys %range)
{
# send group command to NNTP server
	print S "GROUP $group \n";
	$_ = <S>; # read status reply from NNTP server
#        if ($!) 
#	    {
#		print "Socket Read Returned Error!\n";
#	    }
	($status,$gn,$gfirst,$glast,$gname) = split(/ /);
	if($status == 411)
	{
		warn "Warning! Group $group does not exist! Skipping to ",
"next group!\n";
	}
	elsif($status == 211) # loop over articles in the group
	{			
	    if( $gn > 0 )	# if group is not empty
	    {

	      $found_in_group{$group} = 0; # initialize to none found
	      if( ! $opt_s && ! $opt_X ) 
		{
		  $flush_save = $|; # save flush control
		    $| = 1;		# flush on every print or write
		      print "G( $group )"; 
		  $| = $flush_save; # return to standard method
		} # indicate doing a newsgroup
	    
		  if( $opt_X && $opt_X eq "RUN" )
		    {
		      print "104 GROUP $group \n";
		    }


	      $i = $gfirst;
	      $prevArticle = $gfirst;
	      $do = 1;
	      $article_count = 0;	# start with no articles

	    while($do)
	    {
# check if article is in not in excluded range (previously scanned articles)
			if(!&InRange($range{$group},$i))
			{
# check if article exists
				print S "STAT $i\n"; 
				$_ = <S>;  # read reply
				($status,$article,$id,$rest) = split(/ /);
				if($status == 223)
				{
# article retrieved
				    if ( ! $opt_s && ! $opt_X ) 
				    {
					$flush_save = $|;
					$| = 1;	
# flush on every print or write
					print "\."; # period for each 
# article scanned 
					$| = $flush_save;
					     
				    }
					print S "article\n";
					$_ = <S>;
					($status,$article,$id,$rest) = 
					    split(/ /);
					if($status == 220)
					{
					    if (@text) { undef @text } 
# try to free memory
						@text = ();
						$_ = <S>; 
# read first line of text

						while(!/^\.[^\.].*$/) 
# loop until encounter lone period at start of line (.^J ends text in NNTP)
						{ 
							chop; chop; 
# remove trailing CR LF (^M ^J)
							$line = $_ . "\n"; 
# add line feed LF ^J to end of line
							push(@text, $line); 
# add line to text list
							$_ = <S>; 
# read another line
						}

						
		&Collect(*text, *statistics, *pairs, *doPairs) if %statistics;

				   $match = $FALSE; # start with no match
						if(!&Veto(*text,*veto,*group))
						{
# look for match
# deal with required patterns first
							$Required = $TRUE; 
# start by assuming it matches all required patterns
			   foreach $search (split("\034",$required{$group}))
			   {
				$blatz = 0;
				$blatz = grep(eval($search),@text);
				if($blatz == 0)
				{
				    $Required = $FALSE; 
# does not match pattern $search which is required.
				}
				
			    }	# 
							
		if($Required)
		{ 
# at this point $match is FALSE (no match has been found )
		    @search_patterns = split("\034", $pattern{$group});
		    if(!defined(@search_patterns) 
		       || $#search_patterns < 0) 
# $#array is FINAL SUBSCRIPT of array (0 for one element array 
		    {
			$match = $TRUE;
		    }
# if there are search patterns from WHERE then check
		    foreach $search (@search_patterns)
		    {
			$blatz = 0;
			$blatz = grep(eval($search),@text);
			if($blatz)
			{
			    $match = $TRUE;
			}
		    } # close loop over search patterns
		} # close if Required
						    }
# clear the NewsArticle array
					    %NewsArticle = ();
# find any cross references
					    if(!&GetXRef(*text,*NewsArticle))
						{
						    $NewsArticle{$group} = 
							$article;
						}
# NewsArticle is an associative array containing the group and article number
# in group for the article ( an article may be posted to multiple groups
					# if match store article
					    if($match)
					    {
						if(! $opt_s && ! $opt_X ) 
# not quiet
						{
						    $flush_save = $|; 
# save i/o buffering state
						    $| = 1; 
# immediate output
						    print 'F'; 
# indicate found an article
						    $| = $flush_save;
						}
					      $statistics{'FOUND'}++; 
# total number of articles found during search
					      $found_in_group{$group}++; 
# total number of articles found in this newsgroup
					      $total_articles_found++;
					      &ToMailBox(*text, $MboxFormat);
					      print MBOX @text;
					    }
# update the range --- only care about selected groups in @selectedGroups
					    foreach $newsgroup (keys %range)
					    {
						$ArticleInGroup = 
						    $NewsArticle{$newsgroup};
						@theRange = 
						    split(',',
							$range{$newsgroup});
						
						&UpDateRange(*theRange,
							   *ArticleInGroup);
						$range{$newsgroup} = 
						    join(',',@theRange);
					    }
					    
					} # 
				    else
				    {
					warn "Warning! ARTICLE ",
"command returned unexpected status response: $status \n";
				    }
				}
				elsif($status == 423)
				{
# 423 no such article number in this group
					warn "Warning! Article ",
"$i in Group $group does not exist!\n";
				}
				elsif($status == 430)
				{
# 430 no such article found
				    warn "Warning! Article $i ",
"in Group $group not found!\n";
				}
				else
				{
				    die "Aborting! ",
"STAT $i in Group $group returned unexpected status response: $status.\n";
				} # end if for result of STAT command
				
			    }  # end if !&InRange  
			else	# skip article 
			{	# 
# print s to indicate skipping an excluded
# article
			    if( ! $opt_s && ! $opt_X )
			    {	# 
				$flush_save = $|;
				$| = 1;
				print "s";
				$| = $flush_save;
			    }	# 
			}	# 
			
			$article_count++; # number of articles scanned
                        $total_articles_scanned++;

                        if ($opt_X && ($opt_X eq "RUN") 
			    && ( time() - $oldTime > 1 ))
			{
			    $percent = $article_count/$gn;
			    $percent *= 100.0;
			    printf "105 $group %4.2f %d %d %d %d\n", 
$percent, $found_in_group{$group}, $article_count, 
$total_articles_found, $total_articles_scanned;
			    $oldTime = time();
			}

			    
			if( ($article_count % 50) == 0)
			{	# print percent done every 50 articles
			    $percent = $article_count/$gn;
			    $percent *= 100.0;

#			    if ( $opt_X && $opt_X eq "RUN" )
#			    {
#				printf "105 $group %4.2f %d %d %d %d\n", 
# $percent, $found_in_group{$group};
#			    }

			    if (! $opt_s && ! $opt_X )
			    {
				$flush_save = $|;
				$| = 1;
				printf "( %4.2f\%", $percent;
				print " )";
				$| = $flush_save;
			    }
			}
			&NNTPNext;  # go to next article in group
		} # end while($do)
	}			# end if $gn > 0 (not an empty group )
	}
	else			# doesn't recognize NNTP response to GROUP
	{
		die "Abort! NNTP GROUP $group ",
"command returned unexpected response: $status. \n";
	}
} # close loop over groups to search
print S "quit\n"; # close the connection to the server
close MBOX;       # close the file of found news articles

# update the configuration file

&FixRange(*range);  # clean up the range 

foreach $group (keys %range)
{
	$notPresent{$group} = 'T';
}

foreach $i (0 .. $#config)
{
	$_ = $config[$i];
	foreach $group (keys %range)
	{
		$qgroup = $group;
		$qgroup =~ s/(\W)/\\\1/g; 
# quote non word characters (e.g. +)
		if(s/$qgroup\s*:(.*)/$group:$range{$group}/)
		{
			$notPresent{$group} = 'F';
		}
		
	}
 	$config[$i] = $_;
}
#
# append group range lines if don't exist
# only do this for groups that have been selected
# don't care about cross postings to groups that have not been
# selected
#
foreach $group (keys %range)  # keys of range are all groups to be searched
{
	if($notPresent{$group} =~ /T/)
	{
		$line = "$group:$range{$group}\n";
		push(@config,$line);
	}
}

open(CONFIG,">$configFile");
print CONFIG @config;
close CONFIG;
# save statistics

if($statistics{'ALL'})
{
	open(STAT,">>$statFile");  # open to append to file
	print STAT "######\n";
	@groups = keys %range;
	print STAT "Statistics on regular expression incidence ",
"in newsgroups: @groups \n";
	($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) 
	    = localtime(time);
	$month = (Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec)[$mon];
	$year = '19' . $year;
	print STAT "Collected $hour:$min:$sec (Local Time)  ",
"$month $mday, $year \n";
	print STAT "Regular Expression (Perl Syntax) : ",
"Number of Articles With a Match \n";
	$length = 0;
	foreach $pattern (keys %statistics)
	{
		$curLength = length($pattern);
		$length = $curLength if $curLength > $length;
	}

	if($length + 2 < 60)
	{
		$length = $length + 2;
	}
	else
	{
		$length = 60;
	}

	foreach $pattern (keys %statistics)
	{
		printf STAT "%-${length}s: %d\n", $pattern,  
$statistics{$pattern};
	}
	if($doPairs)
	{
	  print STAT " Incidence of pairs of regular expressions \n";
	  print STAT " Only pairs that occur in at least one (1) ",
"article are reported. \n";	# 
	  print STAT " <Perl Regular EXpression><Perl Regular Expression>",
" : Number of Articles with Match :  Correlation Coefficient \n";

		$length = 0;
		foreach $key (keys %pairs)
		{
			$length = length($key) if length($key) > $length;
		}

		if($length < 58)
		{
			$length += 2;
		}
		else
		{
			$length = 60;
		}

		foreach $key (keys %pairs)
		{
			$Nall = $statistics{'ALL'};
			$Npairs = $pairs{$key};
			($pOne,$pTwo) = split(/\034/,$key);
			$NOne = $statistics{$pOne};
			$NTwo = $statistics{$pTwo};
			$muOne = $NOne/$Nall;
			$muTwo = $NTwo/$Nall;
			$varOne = $muOne - ($muOne * $muOne);
			$varTwo = $muTwo - ($muTwo * $muTwo);

			if($varOne && $varTwo)
			{
				$corr 
				  = ( (($Npairs)/$Nall) - ($muOne * $muTwo))
					/(sqrt($varOne) * sqrt($varTwo));
			}
			else
			{
				$corr = "UNDEFINED";
			}

			($index = $key) =~ s/\034/ /;

			printf STAT "%-${length}s : %-10d : %5.3f \n", 
$index, $pairs{$key}, $corr if $pairs{$key} && !($corr =~ "UNDEFINED");
		}
	}
	close STAT;
}

if(! $opt_s && ! $opt_X ) # Let user know where found articles are
{
    $flush_save = $|;		# save original i/o buffering method
    $| = 1;			# force flush on each print or write
    if(! defined $statistics{'FOUND'} )
    {
	$statistics{'FOUND'} = 0;
    }

  print "\nnewscan: Search completed! $statistics{'FOUND'} News articles ",
"with match saved in $mbox ! \n"; 
# think number of articles found is comforting to user 
# - I find it comforting to see
    $| = $flush_save;
}

if( ( ! $opt_s && ! $opt_X ) && $statFile && -e $statFile )	   
# if statistics file name defined
{
    print "newscan: Search statistics saved in $statFile. \n"; 
# Let user know where to find statistics
}

if( ! $opt_s && ! $opt_X )
{
    print "newscan: Search Completed! \n"; # Final exit message
}

if( $opt_X && $opt_X eq 'RUN' )
{
  print "205 QUIT\n"; # let client (xnewscan) know newscan search terminated
}

if( $opt_a )
{
    &PlayFile($audio_file);	# play audio file to indicate completion
}

##### Support Subroutines Follow #####

sub parse
{
# parse the configuration file (Network News Query Language NNQL)
#
# This subroutine handles lexical scanning and parsing of the newscan
# configuration file.  It fills in various data structures that drive
# the search.  These data structures function as an Intermediate
# Representation (IR) of the query in compiler terminology.  The search
# engine uses these data structures to perform the semantic processing
# in compiler terminology.  The semantics (meaning) in this case is
# finding articles with matches to the query.
#
    local(*config) = @_;

    $yyi = 0;			# use yyi to avoid global conflict for now

    &yyparse();			# call byacc Perl parser

    return 1;			# return
}


sub AssocSplice			# splice into an associative array
{
# asterisk * prefix to pass variables by name (not value)
    local(*assoc, $group, *expandGroups) = @_;

    foreach $xGroup (@expandGroups)
    {
	$assoc{$xGroup} = $assoc{$group};
    }
    delete $assoc{$group};
}

sub RangeSplice			# splice into range array
{
    local(*assoc, $group, *expandGroups) = @_;

    foreach $xGroup (@expandGroups)
    {
	if( ! $assoc{$xGroup} )
	{
	    $assoc{$xGroup} = $assoc{$group};
	}
    }
    delete $assoc{$group};
}


sub Collect
{
	local(*text, *statistics, *pairs, *doPairs) = @_;
	local(@ans);
	local($pattern);
	local(%FoundPattern) = () if $doPairs;
	local($pOne, $pTwo);
	local(%done) = () ;  # array of found patterns

	foreach $pattern (keys %statistics)
	{
	    if(@ans) { undef @ans; }
		@ans = ();  # clear answer array
		@ans = grep(eval($pattern),@text) unless $pattern =~ 'ALL' 
		    || $pattern =~ 'FOUND';
		if(@ans)
		{
			$FoundPattern{$pattern} = 1 if $doPairs;
			$statistics{$pattern}++;
		}
		
	}
	$statistics{'ALL'}++;  # count all articles scanned

	if($doPairs)
	{
		foreach $pOne (keys %statistics)
		{
			$done{$pOne} = 1;
			foreach $pTwo (keys %statistics)
			{
				if(!$done{$pTwo})
				{
					if($FoundPattern{$pOne} 
					   && $FoundPattern{$pTwo})
					{
						if($pairs{$pOne,$pTwo})
						{
						  $pairs{$pOne,$pTwo}++;
						}
						else
						{
						  $pairs{$pOne,$pTwo} = 1;
						}
					} # end if found pattern one and two
				} # end if pattern two not already checked
			} # end loop over pattern two
		} # end loop over pattern one
	}
	return;
}

sub NNTPNext
{
# group sending next command to NNTP server and code to parse response
				print S "next\n";
				$_ = <S>; # retrieve reply to next
				($status,$article,$id,$rest) = split(/ /);
				if($status == 223)
				{
# 223 n a message
				    $i = $article; # $i is global variable
				# containing the article number
				}
				elsif($status == 421)
				{
# 421 no next article in group
					$do = 0;
				}
				elsif( $status == 420 )
				{
# 420 no current article has been selected
				  warn "newscan: NNTP Server has returned ",
"420 no current article has been selected error message in response to 
NNTP NEXT command.\n";
				    $do = 0;

				}
				else
				{
				 die "Abort! Unexpected response $status ",
"from NEXT command.\n";
				}
}

sub InRange
{
	local($range, $n) = @_;

	local(@ranges) = split(/,/,$range);

	local($answer) = 0;  # return false unless in range
	local($i);
# should use a more efficient search algorithm here now that ranges
# are sorted in ascending order

	for $i (0 .. $#ranges)
	{
		$_ = $ranges[$i];
		if(/^\s*(\d+)\s*$/)   
		{
			$1 == $n ? ($answer = 1) : ($answer);
		}
		elsif(/^\s*(\d+)-(\d+)\s*$/) 
		{
			if($n >= $1 && $n <= $2)
			{
				$answer = 1; return $answer;
			}
		}
		else
		{
		  die "Abort in InRange! Syntax error in range: $_ \n";
		}
	}

#	print "InRange: answer is $answer \n";
	return $answer
}
			



sub ToMailBox
{
#
# Name: ToMailBox
# Date: 1993
# Author: John F. McGowan
#
# Description: Stores articles in mailbox format folder file.
#
	local(*text, $fmt) = @_;
	local($path) = &GetPath(*text);
	local($date) = &GetDate(*text);
	if( $fmt =~ /unix/i )
	{
	    local($header) = sprintf("%s %s %s\n","From",$path,$date);
#
# Problems with the mail (or Mail) mail reader.
#
# Unix mail, in its infinite wisdom, treats 
# a blank line followed by a line starting with From followed
# by a space as the beginning of a new message.  Except, it appears from
# reports from users, for the very first article in the mail folder file.
# The mail mail reader will actually split a message containing a line
# starting with From into two messages.  elm seems to ignore a line
# in the body of the mail message starting with From.  So, following
# the sendmail convention, newscan will change From .... to >From ....
#
# Dave Taylor's elm mail reader on the other hand treats 
# ^From\s+[^\s]\s+{Unix date}.*\n as start of a message!  So elm rarely
# has problem with lines starting with From in body of message and doesn't
# care about blank line before From
#

	    foreach $line (@text)
	    {
		$line =~ s/^From />From /g;
	    }
	    unshift(@text,$header);
	    push(@text, "\n"); # append newline to end of article 
                               # for Unix mailer
	}
	elsif ($fmt =~ /elm/i)
	{
	    local($header) = sprintf("%s %s %s\n","From",$path,$date);
	    unshift(@text, $header);
	}
	elsif ($fmt =~ /mmdf/i) # MMDF mailbox format
	{
	    local($header) = "\001\001\001\001\n";
	    local($trailer) = $header;
	    unshift(@text, $header);
	    push(@text, $trailer);
	}
}

sub GetPath
{
	local(*text) = @_;
	local($line) = '';
	local($path) = '';

	foreach $line (@text)
	{
		$_ = $line;
		if(/^\s*[Pp]ath\s*:\s*([^\s]*)\s*$/)
		{
			$path = $1;
		}
	}
	if($path)
	{
		return $path;
	}
	else
	{
		warn "Warning! Could not find a Path line in article!\n";
	}
}

sub GetDate
{
	local(*text) = @_;
	local($line) = '';
	local($date) = '';
	local($date) = '';

#
# build regular expression that exceed 78 char limit
# some mail and news software have problems with files that exceed
# 80 chars per line.  
#
	local($fmt1) = '(\w\w\w),\s+(\d\d?)\s+(\w\w\w)\s+(\d\d\d\d)';
	local($fmt2) = '\s+(\d?\d:\d\d:\d\d)\s+(-\d\d\d\d)\s*';
	local($date_fmt) = $fmt1 . $fmt2;

	local($fmt3) = '(\w\w\w),\s+(\d\d?)\s+(\w\w\w)\s+(\d\d)';
	local($fmt4) = '\s+(\d?\d:\d\d:\d\d)\s+(-\d\d\d\d)\s*';
	local($date_fmt2) = $fmt3 . $fmt4;

	local($fmt5) = '(\w\w\w),\s+(\d\d?)\s+(\w\w\w)\s+(\d\d\d\d)\s+';
	local($fmt6) = '(\d?\d:\d\d:\d\d)\s+(\w\w\w)\s*';
	local($date_fmt3) = $fmt5 . $fmt6;

	foreach $line (@text)
	{
		$_ = $line;
		if(/^\s*[Dd]ate\s*:\s*(.*)$/)
		{
			$_ = $1;
			if(
/(\w\w\w),\s+(\d\d?)\s+(\w\w\w)\s+(\d\d)\s+(\d?\d:\d\d:\d\d)\s+(\w\w\w)\s*/)
			{
# dates of form: Tue, 18 May 93 11:24:33 GMT
				$wday = $1;
				$mday = $2;
				$month = $3;
				$year = '19' . $4;
				$time = $5;
				&FixTime(*time);
				$zone = $6;
				$date = 
			     join(' ',$wday,$month,$mday,$time,$year,$zone);
			}
			elsif(/$date_fmt2/)
			{
# dates of form: Tue, 18 May 93 11:24:33 -0400
				$wday = $1; $mday = $2; $month = $3;
				$year = '19' . $4;
				$time = $5; 
				&FixTime(*time);
				$zone = 'GMT';  # kludge for this
				$date = join(' ',$wday,$month,
					     $mday,$time,$year,$zone);
			}
			elsif(/$date_fmt/) 
			{
# dates of form: Tue, 18 May 1993 11:24:33 -0400
				$wday = $1; $mday = $2; $month = $3;
				$year = $4;
				$time = $5;
				&FixTime(*time); 
				$zone = 'GMT';  # kludge for this
				$date = join(' ',$wday,
					  $month,$mday,$time,$year,$zone);
			}
			elsif(/$date_fmt3/)
			{
# dates of form: Tue, 18 May 1993 11:24:33 GMT
				$wday = $1; $mday = $2; $month = $3;
				$year = $4; $time = $5; 
				&FixTime(*time);
				$zone = $6;
				$date = join(' ',$wday,
					  $month,$mday,$time,$year,$zone);
			}
			elsif(
/(\d\d?)\s+(\w\w\w)\s+(\d\d\d\d)\s+(\d?\d:\d\d:\d\d)\s+(\w\w\w)\s*/)
			{
# dates of form: 18 May 1993 11:24:33 GMT
				$mday = $1; $month = $2; $year = $3; 
				$time = $4; 
				&FixTime(*time);
				$zone = $5;
				$wday = &GetWeekDay($mday,$month,$year);
				$date = join(' ',$wday,
					 $month,$mday,$time,$year,$zone);
			}
			elsif(
/(\d\d?)\s+(\w\w\w)\s+(\d\d)\s+(\d?\d:\d\d:\d\d)\s+(\w\w\w)\s*/)
			{
# dates of form 18 May 93 11:24:33 GMT
				$mday = $1;
				$month = $2;
				$year = '19' . $3;
				$time = $4;
				&FixTime(*time);
				$zone = $5;
				$wday = &GetWeekDay($mday,$month,$year);
				$date = join(' ',$wday,
					  $month,$mday,$time,$year,$zone);
			}
			elsif(
/(\d\d?)\s+(\w\w\w)\s+(\d\d\d\d)\s+(\d?\d:\d\d)\s+(\w\w\w)\s*/)
			{
# dates of format 1 Jul 1993 05:54 CST
				$mday = $1;
				$month = $2;
				$year = $3;
				$time = $4 . ':00';
				&FixTime(*time);
				$zone = $5;
				$wday = &GetWeekDay($mday,$month,$year);
				$date = join(' ',$wday,
					  $month,$mday,$time,$year,$zone);
			}
			elsif(
/(\d\d?)\s+(\w\w\w)\s+(\d\d)\s+(\d?\d:\d\d)\s+(\w\w\w)\s*/)
			{
# dates of format 1 Jul 93 05:54 CST
				$mday = $1;
				$month = $2;
				$year = '19' . $3;
				$time = $4 . ':00';
				&FixTime(*time);
				$zone = $5;
				$wday = &GetWeekDay($mday,$month,$year);
				$date = join(' ',$wday,
					  $month,$mday,$time,$year,$zone);
			}
			elsif(
/(\d\d?)\s+(\w\w\w)\s+(\d\d)\s+(\d?\d:\d\d:\d\d)\s+(-\d\d\d\d)\s*/)
			{
# dates of form: 18 May 93 05:11:21 -0400
				$mday = $1;
				$month = $2;
				$year = '19' . $3;
				$time = $4;
				&FixTime(*time);
				$zone = 'GMT'; 
# temporary kludge until i figure out what -0400 means
				$wday = &GetWeekDay($mday,$month,$year);
				$date = join(' ',$wday,
					  $month,$mday,$time,$year,$zone);
			}
			elsif(
/(\d\d?)\s+(\w\w\w)\s+(\d\d\d\d)\s+(\d?\d:\d\d:\d\d)\s+(-\d\d\d\d)\s*/)
			{
# dates of form: 18 May 1993 05:11:21 -0400
				$mday = $1;
				$month = $2;
				$year = $3;
				$time = $4;
				&FixTime(*time);
				$zone = 'GMT'; 
# temporary kludge until i figure out what -0400 means
				$wday = &GetWeekDay($mday,$month,$year);
				$date = 
join(' ',$wday,$month,$mday,$time,$year,$zone);
			}
			elsif(
/(\d\d?)\s+(\w\w\w)\s+(\d\d)\s+(\d?\d:\d\d:\d\d)\s*/)
			{
# dates of form: 18 May 93 05:11:21
				$mday = $1; $month = $2; 
				$year = '19' . $3;
				$time = $4;
				&FixTime(*time);
				$zone = 'GMT';  #kludge
				$wday = &GetWeekDay($mday,$month,$year);
				$date = 
join(' ',$wday,$month,$mday,$time,$year,$zone);
			}
			elsif(
/(\d\d?)\s+(\w\w\w)\s+(\d\d\d\d)\s+(\d?\d:\d\d:\d\d)\s*/)
			{
# dates of form: 18 May 1993 05:11:21
				$mday = $1; $month = $2; 
				$year = $3;
				$time = $4;
				&FixTime(*time);
				$zone = 'GMT';  #kludge
				$wday = &GetWeekDay($mday,$month,$year);
				$date = 
join(' ',$wday,$month,$mday,$time,$year,$zone);
			}
			else
			{
				warn "Warning! Format of date used in 
news article is not recognized by newscan: $_ \n";
warn "Warning! newscan is unable to reformat this date to the date format 
required for the mailbox format file!\n";
warn "Warning! A dummy date is used so that the mailbox format file with 
the stored articles will function with a mail reader!\n";
# use a dummy date below so it knows that I did find a Date line
# but could not parse date format
				$wday = 'Mon'; # kludge
				$mday = 1;
				$month = 'Jan';
				$year = '1800';
				$time = '12:00:00';  # noon
				$zone = 'GMT';
				$date = join(' ',$wday,
					 $month,$mday,$time,$year,$zone);
			    }  # end parsing of date
			last;	
# leave loop over lines after first Date line
		    }  # end if date line
	    }			# end loop over lines in article

	if($date)
	{
		return $date;
	}
	else
	{
 		warn "Warning! Could not find Date line in article!\n";
	}
}

sub FixRange
{
	local(*range) = @_;
	local($group,@theRange,@sortedRange);

	foreach $group (keys %range)
	{
		@theRange = split(',',$range{$group});
		@sortedRange = sort SortElements @theRange;
		&CleanUpRange(*sortedRange);
		$range{$group} = join(',',@sortedRange);
	}
	return;
}

sub SortElements
{
	if( ($minA,$maxA) = $a =~ /(\d+)\-(\d+)/)
	{
	}
	elsif( ($minA) = $a =~ /(\d+)/)
	{
		$maxA = $minA;
	}
	else
	{
		die "Error in range\n";
	}

	if( ($minB,$maxB) = $b =~ /(\d+)\-(\d+)/)
	{
	}
	elsif( ($minB) = $b =~ /(\d+)/)
	{
		$maxB = $minB;
	}
	else
	{
		die "Error in range\n";
	}

	if( $maxA > $maxB && $minA >= $minB)
	{
		return 1;
	}
	elsif( $maxB > $maxA && $minB >= $minA)
	{
		return -1;
	}
	else
	{
		return 0;
	}


}


sub CleanUpRange
{
	local(*theRange) = @_;

	local($i)=0;
	local($next);
	local($element);
	local($newElement);
	local($minLo, $maxLo, $minHi, $maxHi);

	while($i < $#theRange)
	{
			if( 
			($minLo,$maxLo) = $theRange[$i] =~ /(\d+)\-(\d+)/)
			{
			}
			elsif( ($minLo) = $theRange[$i] =~ /(\d+)/)
			{
				$maxLo = $minLo;
			}
			else
			{
				warn "CleanUpRange Error! \n";
			}

			if( ($minHi,$maxHi) = $theRange[$i+1] 
=~ /(\d+)\-(\d+)/)
			{
			}
			elsif( ($maxHi) = $theRange[$i+1] =~ /(\d+)/)
			{
				$minHi = $maxHi;
			}
			else
			{
				warn "CleanUpRange Error! \n";
			}

			if($minHi <= ($maxLo + 1) && $minHi >= $minLo 
&& $maxLo <= $maxHi)
			{
# merge two elements in the range
				$newElement = "$minLo\-$maxHi";
				splice(@theRange,$i,2,$newElement);
			}	
			else
			{
# don't merge elements -- move to next element in list
				$i++;  # increment element in range
			}			

	} # end while loop
	return;
}
sub UpDateRange
{
	local(*theRange,*theArticle) = @_;

	local($i) = 0;
	local($lrange);
# theRange is an array consisting of article numbers nn or nn-mm 
# where nn and mm are article numbers
	foreach $lrange (@theRange)
	{
		local($_) = $lrange;
		if(/^(\d+)$/)
		{
			if($1 == $theArticle)
			{
				return 0;
			}
			elsif($1 == ($theArticle - 1) )
			{
				local($newRange) = join('-',$1,$theArticle);
				splice(@theRange,$i,1,$newRange);
				return 1; # return 1 if updated range
			}
			elsif($1 == ($theArticle + 1) )
			{
				local($newRange) = join('-',$theArticle,$1);
				splice(@theRange,$i,1,$newRange);
				return 1;  # return 1 if updated range
			}
			else
			{
			}
		}
		elsif(/^(\d+)-(\d+)$/)
		{
			if($1 <= $theArticle && $2 >= $theArticle)
			{
				return 0;
			}
			elsif(($theArticle + 1) == $1 )
			{
				$newRange = join('-',$theArticle,$2);
				splice(@theRange,$i,1,$newRange);
				return 1; # returns one if updated range
			}
			elsif(($theArticle - 1) == $2 )
			{
				$newRange = join('-',$1,$theArticle);
				splice(@theRange,$i,1,$newRange);
				return 1;  # returns one if updated range
			}
			else
			{
			}
		}
		else
		{
		}
		$i++;
	}	# end loop over range list
# if it gets here then article is not in excluded range and
# is not one after an existing range
	push(@theRange,$theArticle);
	return 1;
}

sub GetWeekDay
{
	local($mday,$month,$year) = @_;

	local($i) = 0;
	local($days) = 0;  # Number of days since 1 Jan 1991
			   # 1 Jan 1991 is a Tue
# count number of days in current year to date
	while($Months[$i] ne $month && ($i < 12))
	{
		$days += $DaysInMonth{$Months[$i]};
		if($Months[$i] eq 'Feb' && ( $year - 1988 ) % 4 == 0)
		{
			$days++;  # add additional day for the leap year
		}
		$i++; 
	}

	$days += $mday;
# count number of days in years since 1990 (counts 1991)
	for(local($past) = 1991; $past < $year; $past++)
	{
		if(($past - 1988) % 4) # non-zero if not a leap year
		{
			$days += 365;
		}
		else # a leap year
		{
			$days += 366;
		}
	}

	local($weekday) = $days % 7;  # at this point 0 is a Monday
	return $DaysOfWeek[$weekday];
}

sub Veto
{
	local(*text,*veto,*group) = @_;
# veto article if matches a veto pattern
	local($search, $blatz);  # declare local variables
	local(@vpatterns) = split("\034",$veto{$group});
	foreach $search (@vpatterns)
	{
		$blatz = grep(eval($search),@text);
		if($blatz)
		{
			return 1; # veto this article
		}
	}
	return 0;  # no veto
}

sub GetXRef
{
	local(*text,*NewsArticle) = @_;  
# return array of news article numbers
	local(@fields);
	local($line,$field,$key,$value);
	local($iret) = 0;  # did not find an Xref: line in text
	foreach $line (@text)
	{
# parse Xref line
		local($_) = $line;
		chop;
		if(/^Xref:/)
		{
			@fields = split;
			shift(@fields); shift(@fields);
			foreach $field (@fields)
			{
				($key,$value) = split(':',$field);
				$NewsArticle{$key} = $value;
				$iret = 1;
			}
		
		}  # end if Xref line
	} # end loop over lines in article text
	return $iret;
}

sub FixTime
{
	local(*time) = @_;
	local($_) = $time;
	if(/^\d\d:\d\d:\d\d$/)
	{
# do nothing: time in correct format
	}
	elsif(/^\d:\d\d:\d\d$/)
	{
		$time = '0' . $time;

	}
	elsif(/^\d\d:\d\d$/)
	{
		$time = $time . ':00';
	}
	elsif(/^\d:\d\d$/)
	{
		$time = '0' . $time . ':00';
	}
	else
	{
		warn "Fixtime Warning!  Do Not Recognize 
Time format of time: $time !\n";
	}
	return;

}

sub PlayFile
{
#
#   Name: PlayFile
#   Author: John F. McGowan, Ph.D.
#   
#   Description: Use /dev/audio if it exists to play an audio file 
#   if the audio file exists.  Otherwise use the Unix BEL to alert the
#   user that something has happened.
#
#   /dev/audio is u-law 8 bit output on Sun Sparcstation  with SunOS 4.x 
#   /dev/audio is u-law 8 bit output on Solaris, but buffered differently
#   /dev/audio is Sun/Next format file audio output special file on Linux
#
#   SGI machines do not have /dev/audio interface 
#   Sony NEWS line machines use /dev/sb0 to play Sun audio files
#
    local($file) = @_;		# local variable for audio file to play

    if(! -e AUDIO )		# check if handle to audio 
    {				# device driver exists
	open(AUDIO, ">/dev/audio"); # attempt to open audio device
	if( -e AUDIO )
	{
	    &PlayIt($file);	# play the audio file
	}
	else
	{
	    print "\a";		# use bell signal as default
	}
    }
    else
    {
	&PlayIt(*file);		# play the audio file
    }
}

sub PlayIt
{
    local($file) = @_;

    if( -e $file )
    {
	open(SOUND,$file);
	@sound = <SOUND>;
	close(SOUND);

	print AUDIO "@sound";	# play the sound on device AUDIO
    }
    else
    {
	print "\a";		# default to Unix bell 
    }
}

##################################################
# Next few lines are legal in both perl and nroff
.00;  # finish .ig

'di                      \" finish diversion -- previous line must be blank
.nr nl 0-1               \" fake up transition to first page again
.nr % 0                  \" start at page 1
';__END__ #### From here on it's a standard manual page ###

.TH NEWSCAN 1 "June 3, 1993"
.AT 3
.SH NAME
newscan \- scans Usenet news for articles matching regular expressions 
(uses perl regular expressions).  Articles are saved in a mail folder, a 
file in mailbox format.
.SH SYNOPSIS
.B newscan [-c configuration-file] [-e] [-r folder-file-specification] 
[-H name-of-local-host][-h] [-s]
.SH DESCRIPTION
.I newscan
searches selected newsgroups for articles matching patterns.  Patterns are 
specified as regular expressions in a configuration or resource file.  Also 
may specifify patterns to veto articles.  If an article contains this 
pattern, the article will be ignored even if it contains a search pattern.
The configuration file is specified with the command line argument 
-c configuration-file or in the environment variable NEWSCAN.  The command 
line argument takes precedence over the NEWSCAN environment variable. If 
NEWSCAN is undefined, the configuration file defaults to .newscanrc in the 
user's home directory.

Typically, newscan is run as a batch job: 
	newscan -s &      ( -s to turn off in progress messages )
OR
	newscan > newscan.out &  ( redirect in progress messages to file )

.SH OPTIONS

	What You Want To Do		Option

	Get help on
.I newscan				-h

	Edit Configuration File		-e

	Override Default Configuration File	-c configuration-file

	Invoke Mail Reader to Read a Folder	-r folder

        Explicitly Set Local Host Name          -H name-of-local-host

	Silent Mode (Suppress In Progress Messages)  -s


.SH CONFIGURATION FILE
newscan is controlled by a resource or configuration file containing 
command in a simple language that newscan understands.  These commands 
should be all capitals.

Configuration file commands:

NNTP them.them.com [119] specifies the Internet address of the NNTP
server.  The first argument is the Internet address either as a Fully
Qualified Domain Name or the dotted decimal format for the 32 bit Internet
address.  The optional second argument is the port number of the NNTP
server; this should be 119 if NNTP specification is followed.  Port
119 is reserved for NNTP.

    If this line is not specified, newscan will use the environment
variable NNTPSERVER if it is defined.  If NNTPSERVER is not defined, newscan
will use the local machine as default NNTP server.  The local machine is
frequently not an NNTP server.

MBOX <my-file-specification> specifies the file where the found
articles are stored.  This file is a mail folder that may be
read and manipulated by any mailer (Mail User Agent)
<my-file-specification> can be any valid Unix
file-specification (including path).  MBOX interprets a leading tilde
~ as the user's home directory: e.g.  MBOX ~/tmp/myfile.

SELECT my.group his.group alt.group specifies the Internet newsgroups
for which subsequent search specifications apply.  newscan searches all
newsgroups specified by SELECT lines. 

SELECT supports a wildcard * in newsgroups.  The wildcard works like
the wildcard in file names in Unix shells. For example, SELECT
comp.lang.* will search all groups in the comp.lang hierarchy:
comp.lang.fortran, comp.lang.forth, ..., comp.lang.pascal, etc.

REQUIRE /regexp/[i] specifies a perl regular expression that MUST be
found for a match to occur.  This search criterion applies only to
the newsgroups specified by the last preceding SELECT line.  

REQUIRE provides a mechanism to search for articles that contain a
match to more than one search pattern.  For example, REQUIRE /pat1/i
and REQUIRE /pat2/i will find articles that contain pat1 AND pat2; it
will not find an article that has pat1 but NOT pat2 or pat2 but NOT
pat1.  REQUIRE provides a simple mechanism to perform a logical AND of
two search patterns.

WHERE /regexp/[i] specifies the perl regular expression to be found.
As in perl, the optional trailing /i tells newscan to ignore case.  This
search criterion applies only to the newsgroups specified by the 
last preceding SELECT line.

WHERE provides a mechanism to search for articles that contain a match
to one or more of a group of search patterns.  For example, WHERE /pat1/i
and WHERE /pat2/i will find articles that contain pat1 OR pat2 OR both.
Notice the contrast to REQUIRE above!

UNLESS /regexp/[i] specifies the perl regular expression used to
exclude an article.  Even if the article contains a match to a WHERE
regular expression it will be excluded (not found) if it contains a
match to an UNLESS expression.  As in perl, the optional trailing /i
tells newscan to ignore case.

WORD blatz is equivalent to /\\bblatz\\b/i A configuration file can
contain lines such as WHERE WORD vision or REQUIRE WORD finance.
WHERE WORD vision matches the word vision.  WHERE WORD vision would
not match computer-vision since vision is not a word in this case.
This provides a somewhat simpler way to specify searches without
knowing Perl regular expressions.

WORDSTEM blatz is equivalent to /\\bblatz.*\\b/i  A configuration file
can contain lines such as WHERE WORDSTEM imag or REQUIRE WORDSTEM imag.
WHERE WORDSTEM imag would match words image, imaging, imager, and so forth.
This provides a somewhat simpler way to specify searches without knowing
Perl regular expressions.

PHRASE word1 word2 [... wordn] is equivalent to
/\\bword1\\b\\s+\\bword2\\b/i A configuration file can contain lines
such as WHERE PHRASE tcl programming or REQUIRE PHRASE tcl
programming.  This provides a somewhat simpler way to specify searches
without knowing Perl regular expressions.

OUTLINE word1 word2 [... wordn] is equivalent to
/\\bword1\\b.*\\bword2\\b/i A configuration file can contain lines
such as WHERE OUTLINE software engineer or REQUIRE OUTLINE windows
engineer.  OUTLINE windows engineer would match ms windows engineer,
windows software engineer, as well as windows engineer.  Note that
this is different from PHRASE windows engineer which would only match
windows engineer!  This provides a somewhat simpler way to specify
searches without knowing Perl regular expressions.

COLLECT STATISTICS IN <file-specification> line tells newscan to collect
statistics on the incidence of regular expressions in all articles scanned 
(not just articles that match).  These statistics are printed in human
readable form in the file <file-specification>.  newscan appends the
statistics information to <file-specification>.

COLLECT STATISTICS ON /regexp/[i] line tells newscan to collect
statisitcs on incidence of perl regular expression regexp.  newscan
counts the number of articles that contain at least one match to
regexp.

COLLECT STATISTICS ON {SEARCH|WHERE} PATTERNS tells newscan to collect
statistics on all perl regular expressions specified by WHERE /regexp/[i]
lines in the configuration file.

COLLECT STATISTICS ON {VETO|UNLESS} PATTERNS tells newscan to collect
statistics on all perl regular expressions specified by UNLESS /regexp/[i]
lines in the configuration file.

COLLECT STATISTICS ON PAIRS tells newscan to collect statistics on all
pairs of perl regular expressions for which statistics are being
collected.  newscan counts the number of articles containing at least
one match to both regular expressions.  newscan also calculates a
simple correlation coefficient between the two regular expressions in
the pair.  Note that if the correlation coefficient is 1.0, then the
two regular expressions always occur together; this means one regular
expression is a redundant (unneeded) search criterion.

my.favorite.group:1-1100,1105-1110 is a line in the configuration file
that lists ranges of articles in a group that are excluded from
search.  newscan updates this range after it finishes to avoid
repeating a search.  If this line is not provided by the user, newscan
will generate the group range line and append it to the end of the
configuration file.  newscan generates the group range line for any
other groups that a found article has been posted to.

.SH SAMPLE RESOURCE FILE

NNTP nntphost 

MBOX myBox

SELECT misc.jobs.offered ba.jobs.offered

WHERE /gui/i

WHERE /motif/i

WHERE /graphic/i

UNLESS /From:.*Headhunter/i

UNLESS /Subject:.*Recruit/i

misc.jobs.offered:1-1000

ba.jobs.offered:

.SH ENVIRONMENT 
.I NEWSCAN 
environment variable defines the configuration or resource file that
controls the search.  If NEWSCAN is not defined, then newscan defaults
to the file .newscanrc in the user's home directory.  NEWSCAN is
superseded by the -c configuration-file command line argument.

.I NNTPSERVER
environment variable specifies the Internet address of the system NNTP
server.  The NNTP <nntphost> line in the configuration file takes
precedence over the NNTPSERVER variable.

.I READER
environment variable selects the mail reader used by newscan.  If READER
is not set, newscan will try to find and use a mail reader of its choosing.
newscan will use Dave Taylor's elm if it exists.

.I EDITOR
environment variable selects the editor used by newscan.  If EDITOR is not
set, newscan will try to use first emacs and then vi.

.I LOCALHOST
environment variable explicitly sets the name of the local host.  newscan
defaults to using the Unix 
.B
hostname
utility to get the local host name.  In most cases, there is no need to
set LOCALHOST.  However, at least on Linux, if the local host's official
name returned by hostname is a number such as 440, the 
.B
gethostbyname
function called by newscan will not return the correct Internet address.  
In this case, set LOCALHOST to a valid alias of the official name that is
not a number.  For example, 440 might have an alias 440.rahul.net.  Use
440.rahul.net.

.SH FILES 
.I $HOME/.newscanrc 
is the default resource or configuration file
specifying the search.  This can be overridden from the command line
(using -c configuration-file) or by setting the NEWSCAN environment
variable.  The command line argument supersedes the NEWSCAN
environment variable.

.I newscanBox
is the default file where newscan saves found articles in mailbox
format.  This can be overridden by using the MBOX file-name command in
the configuration file.

.SH PERL REGULAR EXPRESSIONS

newscan uses Perl regular expressions to specify the patterns that it
searches for in the selected newsgroups.  Perl regular expressions are
similar to the regular expressions used in sed, vi, and emacs, but
more extensive.

    .           Matches any character except for newline
    [a-z0-9]    Matches any single character of a set
    [^a-z0-9]   Matches any signle character not in set
    \\d          Matches a digit, same as [0-9]
    \\D	        Matches a non-digit, same as [^0-9]
    \\w          Matches an alphanumeric (word) character [a-zA-Z0-9_]
    \\W          Matches a non-word character [^a-zA-Z0-9_]
    \\s          Matches a whitespace character (space, tab, newline...)
    \\S          Matches a non-whitespace character

    \\n          Matches a newline
    \\r          Matches a return
    \\t          Matches a horizontal tab
    \\f          Matches a formfeed
    \\b          Matches a backspace ( inside [] only! )
    \\0          Matches a null character
    \\000        Also matches a null character
    \\nnn        Matches an ASCII character of that octal value
    \\xnn        Matches an ASCII character of that hexadecimal value
    \\cX         Matches an ASCII control character
    \\metachar   Matches the character itself (\|, \., etc.)

    (abc)	 Remembers the match for later backreferences
    \\1		 Matches whatever first set of parens matched
    \\2          Matches whatever second set of parens matched
    \\3          Matches whatever third set of parens match ... and so on

    x?           Matches 0 or 1 x's, where x is any of above
    x*           Matches 0 or more x's
    x+           Matches 1 or more x's
    x{m,n}       Matches at least m x's but no more than n

    abc          Matches abc in order
    fee|fie|foe  Matches one of fee, fie, or foe
    
    \\b		 Matches a word boundary (outside [] only!)
    \\B          Matches a non-word boundary
    ^            Anchors match to the beginning of a line or string
    $		 Anchors match to the end of line or string
    
.SH EXAMPLES OF PERL REGULAR EXPRESSIONS 

.I 
WHERE /\\bphigs\\b/i 

Search for the word PHIGS (case insensitive) Uses \\b word boundary
symbol.  Note that this search would NOT find a news article
containing the word "SunPHIGS"

.I 
WHERE /phigs/i

Search for the string PHIGS (case insensitive) Does not use \\b.  Note
that this search would find a news article containing the word
"SunPHIGS".

.I 
WHERE /John\\s+Smith/        

Search for the string John (any number of spaces) Smith.  Case
sensitive.  Would not find "john smith".  Would find "John Smith".

.I 
WHERE /^Subject:.*phigs/i   

Search for a news article whose Subject contains the string phigs.

.I 
UNLESS /^From:.*Mad\\s+Flamer/i  

Ignore articles from Mad Flamer!


.SH SEE ALSO

"Programming Perl" by Larry Wall and Randal L. Schwartz, published by
O'Reilly and Associates (ISBN: 0-937175-64-1).  Often referred to as
the "Camel" book after the Camel on the cover.  A comprehensive book
on Perl.

"Learning Perl" by Randal L. Schwartz, published by O'Reilly and
Associates (ISBN: 1-56592-042-2).  Often referred to as the "Llama"
book after the Llama on the cover.  Introductory Perl book.

comp.lang.perl USENET newsgroup.

Perl Frequently Asked Questions (FAQ) document.

.SH AUTHOR
.I John F. McGowan, Ph.D.

e-mail: jfm@jupiter.slac.stanford.edu (or) jfm@rahul.net

.SH DIAGNOSTICS

.SH BUGS
A known nuisance is that newscan will find the same article more than
once if the article has been manually posted to more than one
newsgroup that newscan searches.  If the article has been properly
cross-posted to multiple newsgroups, newscan will find only one copy
of the article.

A number of people have reported problems running newscan under Perl
5.000 There appears to be a memory leak under Perl 5.000; During some
long searches newscan will run out of memory and quit.  I strongly
recommend using perl 4.036 to run newscan.

Please report any bugs to the author.

.SH COPYRIGHTS
(C) Copyright 1993, 1994, 1995 by John F. McGowan, Ph.D. 

You may use or modify newscan as you like.  However, you must credit
John McGowan as the author of the original version of newscan and
include the copyright notice above.

.SH DISCLAIMER
.I newscan 
is provided 
.I as is.  
There is no warranty, express or implied, that it will do what you
want.  There is no warranty that it will work reliably or without
error.  The author disclaims all responsibility or liability for any
consequences of using newscan, including but not limited to losses of
time or money.

In other words, use at your own risk.    







#line 3339 "y.tab.pl"
