#!/usr/local/bin/perl 
#
# Copyright (C) 1992 by Gustaf Neumann, Stefan Nusser
#
#      Wirtschaftsuniversitaet Wien,
#      Abteilung fuer Wirtschaftsinformatik
#      Augasse 2-6,
#      A-1090 Vienna, Austria
#      neumann@wu-wien.ac.at, nusser@wu-wien.ac.at
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose and without fee is hereby granted, provided
# that the above copyright notice appears in all copies and that both that
# copyright notice and this permission notice appear in all supporting
# documentation.  This software is provided "as is" without expressed or
# implied warranty.
#
# Date: Mon, Apr 13 1992
# Author: Gustaf Neumann
# Version: 0.9

$NO_NNTP = 0;
unshift(@INC, ".");

%privOptions = (
	"f", "newsrc: name of newsrc, default is ~/.newsrc",
	"p", "print Command: such as 'mp | lpr'",
	"s", "signature file: such as ~/.elm/signature",
	"N", "nntp server: name of the internet host providing news via nntp",
	"C", ": prefer files for communication",
	);

$WafeLib = $ENV{'WAFELIB'} || "/usr/lib/X11/wafe";
require "$WafeLib/perl/wafe.pl";
$opt_f = $opt_f || ($opt_N && "$ENV{'HOME'}/.newsrc.$opt_N") || "$ENV{'HOME'}/.newsrc";
$nntpServer = $opt_N || $defaultNntpHost;

require 'nntp.pl';
require 'wafe_mu.pl';

#
# create various temporary files
#
$newsgroups = &wafe'tmpFile("newsgroups");
$newssubj = &wafe'tmpFile("newssubj");
$newsarticle = &wafe'tmpFile("newsarticle"); #'

$currentGroupMode = "with new articles";
$currentArticleMode = "unread articles";
$currentSortMode = "by Article Number";
$currentGroupSortMode = "as in newsrc";

sub bynumkey { $keys[$a] <=> $keys[$b]; }
sub byanumkey { $keys[$a] cmp $keys[$b]; }

$do_sCache = 0;
if ($do_sCache) {
    dbmopen(subjectCache,".subject_cache",0664);
}

#
# print contents of @array into widget $w using file $fname
# $grep can be used to restrict output, 
# $sortSubjects and sortGroups can be used to change the order

sub pArray {
    local($fname,$w,$grep,$sortSubjects,$sortGroups,$before,$after,@array) = @_;
    local(@keys,$_);

    if ($before||$after) {
        local(@b,@a);
#        print "before=<$before>, after=<$after>\n";
	eval '@b = grep(/^(\s*'.$before.'\s)/ && (substr($_,33,1)="<"),@array)' if $before;
        eval '@a = grep(/^(\s*'.$after.'\s)/ && (substr($_,33,1)='
	    .'(/^\s*'.$currentArticleNumber.'\s/ ? "=" : ">")),@array)' if $after;
	@array = (@b,@a);
    }

    @array = grep(/$grep/i,@array) if $grep;
    if ($sortSubjects && $currentSortMode ne "by Article Number") {
	undef @keys;
	local($k);
        for (@array) {
	    ($k = substr($_,34)) =~ s/^\s*Re:\s*//;
	    $k =~ s/\s*$/$;.substr($_,0,7)/e;
	    push(@keys,$k);
	};
	@array = @array[sort byanumkey $[ .. $#array];
    }
    if ($sortGroups && $currentGroupSortMode ne "as in newsrc") {
	undef @keys;
        for (@array) {push(@keys,$_);};
	@array = @array[sort byanumkey $[ .. $#array];
    }

    if ($w eq '>') {
    	open(TMP, ">$fname") || die "can't open $fname for writing!";
    	print TMP join("\n", @array);
    	close(TMP);
    } else {
       $Text{$w} = join("\n", @array);
       if ($opt_C) {
          &wafe'fileTransaction($fname, 
            "open(TMP, '>$fname') || die \"can't open $fname for writing!\";"
             .'print TMP  $main\'Text{"'.$w.'"};'
             .'close(TMP);'
             ."&wafe'setWidgetToFile('$w','$fname');"
          );
       } else {
          &wafe'tunnel("COMM",join("\n", @array),"sV $w type string string \$COMM");
       }
    }
}

#
# set info lables From: Subject: and Date: 
# to current values

sub changeLables {
   &Xui("sV FromInfo label ".&TclQuote($currentFrom));
   &Xui("sV SubjectInfo label ".&TclQuote($currentSubject));
   &Xui("sV DateInfo label ".&TclQuote($currentDate));
}


#
# sendMode($mode)
# handles changes of the sendmode state and
# and refreshes the mail dieplay if not sendmode

sub sendMode {
    local($sendmode) = @_;
    local($text) = $FullHeader?$currentArticle:$currentBody;

    &wafe'sensitive($text ne "" && !$sendmode, 
          ("reply","forward","header","followup", "thread")); 
    &wafe'sensitive($text ne "" || $sendmode > 0,("print","save"));
    &wafe'sensitive($currentNewsGroup ne "" && $sendmode == 0,("post","catchup"));
    &wafe'sensitive($sendmode != 0,("send"));
    &wafe'sensitive($text ne "" &&  #'
            $currentFrom =~ /^(\S+)\b/ && 
	    $1 eq "$wafe_mu'user@$localHost",("cancel")); #'
    &wafe'sensitive($sendmode == 0,("quit")); #'

    &pArray($newsarticle,'articletext',"","","",'','',$text) #'
        unless $sendmode;
    &Xui("sV articletext editType "
             .($sendmode ? "edit wrap never" : "read wrap line") 
             .($opt_C ? " string $newsarticle" : ""));

    $SendMode = $sendmode;
    return 1;
}

#
# retrieve the newsgroup entries fromnewsrc et al
# and cache the result

sub newsgroups {
    @NewsGroups = &nntp'subscribed($nntpConn,$opt_f,$currentGroupMode)
          unless @NewsGroups;
    @NewsGroups;
}

#
# force recalculation of a single line (last visited newsgroup) 
# in newsgroup display

sub updateGroupLines {
    local($lastNewsGroup) = @_;
    if ($lastNewsGroup) { 
	&Xui("savePos grouptext"); 
	undef $nntp'groupLine{$lastNewsGroup}; #'
	&groupMode($currentGroupMode,1);
	&Xui("restorePos grouptext"); 
    }
}

#
# change group mode and/or 
# refresh  newsgroup output on the screen

sub groupMode {
    local($groupmode,$refresh) = @_;
    if ($currentGroupMode ne $groupmode || $refresh) {
	$currentGroupMode = $groupmode;	
	&info("scanning newsgroups ...");
        undef @NewsGroups;
	&pArray($newsgroups,'grouptext',$gpattern,0,1,'','',&newsgroups);
        &info("");
    }
}

#
# when a new newsgroup is entered 
# merge newsrc entries (in perl array)

sub flushCurrentNewsGroup {
    if ($currentNewsGroup) {
#               mark the read articles and 
#               the articles below the first-last range of the newsserver as read
	local($unAvail) = (split(/ /,$nntp'groups{$currentNewsGroup}))[1];
        $unAvail = $unAvail>1 ? "1-".($unAvail-1)."," : "";
        local($artl) = &nntp'canon_artlist(join(',',grep($Read{$_},keys %Read)));
	&nntp'newsrc_merge($currentNewsGroup, $unAvail.$artl);
	undef %Read; 
        foreach(values %Xref) {
	    foreach (split) { &nntp'newsrc_merge($1,$2) if /^([^:]+):(\d+)$/; }
        }
        undef %Xref;
    }
}

unless ($NO_NNTP) {
    ($nntpConn,$server) = &nntp'connect;
#    &pArray($newsgroups, '>', "", "", 1, '','', &newsgroups());
};


#
# set default resources
#
&wafe'setResources("",%textResources);

#
# the following settings are used several times

$textatt = "type file scrollVertical always string";
$headerlabel = 'justify left top ChainTop bottom chainTop borderWidth 0 '
    .'left chainLeft right chainLeft';
$headerbox = 'justify left  borderWidth 0 ';


$topfield = "$backGround borderWidth 0";

$every_hlabel = "justify left $topfield left chainLeft";	
$const_hlabel = "$every_hlabel  right chainLeft $boldFont ";
$var_hlabel = "label {} $every_hlabel resizable true right chainLeft $normalFont";

$top = "top chainTop bottom chainTop";
$bot = "top chainBottom bottom chainBottom";
$left = "left chainLeft right chainLeft";
$right = "left chainRight right chainRight";

$newsgroups = "{./wafenews}" if $NO_NNTP;

####################### tcl setup ##################
&Xui(<<'__');
#
# returns the first words of a selection
proc Selection {selection} {
    foreach line [split [string trimright $selection] \n] {
        lappend lines [lindex $line 0] }
    return $lines
}


proc wSelection {w} {
  XawTextGetSelectionPos $w from to
  if $from!=$to {
     return [Selection [XawTextRead $w $from $to]]
  } else {
     deselo $w
     return {}
  }
}

#
#
# own and disown of selections
#
set holder ""

proc selo {w} {global holder
  if {$w == $holder} return;
  deselo $holder
    if {$w == "grouptext"} {
	sV subscribe-first sensitive true
	sV subscribe-last sensitive true
        sV save-newsrc sensitive true
	sV unsubscribe sensitive true
    }
    if {$w == "subjecttext"} {
	sV msave sensitive true
	sV mark sensitive true
    }
    set holder $w
}

proc deselo {w} {global holder
  if {$holder == ""} return
  if {$holder == "grouptext"} { 
     sV subscribe-first sensitive false
     sV subscribe-last sensitive false
     sV unsubscribe sensitive false
  }
  if {$holder == "subjecttext"} {
     sV msave sensitive false
     sV mark sensitive false
  }
  set holder ""
}

#
# insert character R in to denote that article was read

proc markT {w} {
  sV $w editType edit
  XawTextGetSelectionPos $w from to
  set text(firstPos) 0; set text(length) 1; set text(ptr) "R"
  XawTextReplace $w [expr $from+6] [expr $from+7] text
  sV $w editType read
}

proc doTextReplace {w from to string} {
  sV $w editType edit
  set text(firstPos) 0; set text(length) [string length $string];
  set text(ptr) $string
  XawTextReplace $w $from $to text
  sV $w editType read
}
__


#
# standard button settings (need perl substitutions)
&Xui(<<"__");
proc simpleButton {name father args} {
  eval Command \$name \$father $buttonAtts \$args {callback {echo %w}}}

proc simpleMenuButton {n father menu args} {
  eval MenuButton \$n \$father menuName \$menu \$args \\
     $top $left $topfield $boldFont $threeDNarrow}

#
# standard button settings with simpler appearance

proc simplerButton {n father args} {
  eval Command \$n \$father $normalFont borderWidth 0 \\
       $backGround \$args $threeDNarrow}

proc simplerButtonCB {n father args} {
  eval simplerButton \$n \$father \$args {callback {echo %w}}}

proc simplerButtonSel {n father source args} {
  eval simplerButton \$n \$father \$args \\
       "callback {echo %w \\[wSelection \$source\\]}"}

#
# standard menue settings

proc simpleMenue {name father label default vert horiz hlabel} {
  eval simpleMenuButton \$name \$father \${name}modes \\
      \$vert \$horiz {label \$label}
  eval Label \${name}mode \$father label {\$default} width 135 $top $left \\
      $topfield justify left \$vert fromHoriz \$hlabel $normalFont $twoD
  SimpleMenu \${name}modes \$name $menueAtts
}
#
# modifying the size of a child in a Paned widget
proc shrink {text father v} {
  Command enlarge\$text \$father $normalFont borderWidth 0 label + \\
	$backGround fromVert \$v horizDistance 590 $right $top \\
        callback "setHeight \$text +40"
  Command shrink\$text \$father $normalFont borderWidth 0 label - \\
	$backGround fromVert \$v fromHoriz enlarge\$text $right $top \\
        callback "setHeight \$text -40"
}
__


&Xui(<<'__');
#
# set height of a widget $w to +/i increment (used only by shrink)
proc setHeight {w e} {global textHeight
  set newHeight [expr [gV $w height]$e]
  if {$newHeight < 1} {return}
  set textHeight($w) $newHeight
  sV $w height $textHeight($w)
}

#
# configure textwidget for line selection
proc textSelectActions {Widget} {
  XawTextSetSelectionArray $Widget selectLine selectNull
  action $Widget override {\
    <Btn1Motion>: no-op()
    <SelClr>:     exec(deselo %w)
    <Btn2Down>:   exec(sV %w cursor pencil) select-start()
    <Btn2Motion>: extend-adjust()
    <Btn2Up>:     extend-end(CUT_BUFFER0) exec(selo %w;sV %w cursor hand2)
  }
}

#
# send string "save xxxx" to the application and maintain 
# the global variable mail

proc sendsave {} { global mail
  echo "save <$mail> [gV savetext value]"
  set mail -1
}

#
# send name of a text widget and string value to the application
proc sendvalue {w} { 
  echo "$w [gV $w string]" 
}

#
# save and restore display position and caret of a text widget
proc savePos {w} {global displayPosition insertPosition
  set displayPosition($w) [gV $w displayPosition]
  set insertPosition($w) [gV $w insertPosition]
}
proc restorePos {w} {global displayPosition insertPosition
  sV $w displayPosition $displayPosition($w) insertPosition $insertPosition($w)
}
__

#
# Widget Tree of the appliction (needs per substitutions)

&Xui(<<"__");
Paned paned topLevel orientation vertical width 630 
  Form topf paned $backGround showGrip false defaultDistance 0
    Label info topf \\
         $backGround $normalFont label {} width 630 borderWidth 1 $infoColors

    simpleMenue group topf {Groups:} {$currentGroupMode} \\
         {fromVert info} {} group
    simpleMenue gsort topf {Sort:} {$currentGroupSortMode} \\
	 {fromVert info} {fromHoriz groupmode} gsort

    Label ggrepLabel topf label {Grep:} $const_hlabel $twoD \\
         fromVert info fromHoriz gsortmode

    Text ggrep topf \\
         editType edit width 125 $topfield $top $left $normalFont \\
         fromHoriz ggrepLabel fromVert info \\
	 translations {#override
            <Key>Return: exec(sendvalue %w)
            <Enter>: exec(sV %w $highLight)
	    <Leave>: exec(sV %w $backGround) }
#         callback ggrep callback exec {sendvalue ggrep}

    simpleMenuButton configButton topf config \\
         label Config fromVert info fromHoriz ggrep

    simplerButtonSel subscribe-first topf grouptext \\
         fromVert gsort sensitive false 
    simplerButtonSel subscribe-last topf grouptext \\
         fromVert gsort fromHoriz subscribe-first sensitive false 
    simplerButtonSel unsubscribe topf grouptext \\
         fromVert gsort fromHoriz subscribe-last sensitive false 
    simplerButtonCB save-newsrc topf \\
         fromVert gsort fromHoriz unsubscribe sensitive true 

  shrink grouptext topf gsort 

  Text grouptext paned $textatt /dev/null height 140 $roColors \\
     allowResize true cursor hand2 displayCaret false $textFont \\
     translations {#override
        <Btn1Down>: select-start() select-end(CUT_BUFFER0) \\
                    next-line() exec(selo %w)
        <Btn1Up>: exec(echo "newsgroup [Selection [fetchBuffer topf 0]]")
        <Key>u: exec(echo "unsubscribe [Selection [fetchBuffer topf 0]]")
        <Key>l: exec(echo "subscribe-last [Selection [fetchBuffer topf 0]]")
        <Key>f: exec(echo "subscribe-first [Selection [fetchBuffer topf 0]]")
        <Key>s: exec(echo save-newsrc)
     }
  textSelectActions grouptext  
  Form groupb paned \\
     $backGround min 39 max 39 showGrip false defaultDistance 0

     simpleMenue article groupb {Articles:} {$currentArticleMode} \\
         {} {} article
     simpleMenue sort groupb {Sort:} {$currentSortMode} \\
         {} {fromHoriz articlemode} sort

     Label grepLabel groupb \\
         label {Grep:} $const_hlabel $twoD fromHoriz sortmode
     Text grep groupb  editType edit width 125 $topfield \\
         fromHoriz grepLabel $top $left $normalFont \\
         sensitive false displayCaret false \\
	 callback {sendvalue grep} translations {#override
            <Key>Return: exec(sendvalue grep)
            <Enter>: exec(sV grep $highLight)
            <Leave>: exec(sV grep $backGround)
	 }

     Label newsgroup groupb \\
         label {Newsgroup: } $const_hlabel fromVert article 
     Label currentnewsgroup groupb width 200 $var_hlabel \\
         fromVert article fromHoriz newsgroup

     simplerButtonCB catchup groupb \\
         fromVert article fromHoriz sortmode  sensitive false $left 
     simplerButton msave groupb \\
         fromVert article fromHoriz catchup sensitive false $left \\
         label {save} callback {
              set mail [wSelection subjecttext]; popup savemenu exclusive }
     simplerButtonSel mark groupb subjecttext \\
         fromVert sort fromHoriz msave sensitive false $left 
     simplerButtonCB thread groupb \\
         fromVert sort fromHoriz mark sensitive false $left 

     shrink subjecttext groupb sort 

     Text subjecttext paned \\
         $textatt {/dev/null} height 140 $textFont \\
         showGrip false $roColors allowResize true \\
	 cursor hand2 displayCaret false translations {#override
	  <Btn1Down>: select-start() select-end(CUT_BUFFER0) \\
             exec(echo "article [Selection [fetchBuffer topf 0]]"; markT %w) \\
             select-start() extend-adjust() next-line() exec(selo %w)
          <Btn1Up>:     exec(selo %w) previous-line()
          <Btn3Down>:   exec(echo refresh)
          <Btn3Motion>: no-op()
          <Btn3Up>:     no-op()
          <Key>Return:  select-start() select-end(CUT_BUFFER0) \\
	     exec(echo "article [Selection [fetchBuffer topf 0]]"; markT %w) \\
             select-start() extend-adjust() exec(selo %w)
          <Key>Down: scroll-one-line-up() \\
             select-start() extend-end(CUT_BUFFER0) \\
	     exec(echo "article [Selection [fetchBuffer topf 0]]"; markT %w) \\
             select-start() extend-adjust() exec(selo %w)
          <Key>Up: scroll-one-line-down() \\
             select-start() extend-end(CUT_BUFFER0) \\
	     exec(echo "article [Selection [fetchBuffer topf 0]]"; markT %w) \\
             select-start() extend-adjust() exec(selo %w)
          <Key>s: exec( \\
             set mail [Selection [fetchBuffer topf 0]]; \\
             popup savemenu exclusive)
          <Key>u: exec(echo unsubscribe)
          <Key>c: exec(echo catchup)
          <Key>t: exec(echo thread)
          <Key>m: exec(echo "mark [wSelection subjecttext]")
	 }
         textSelectActions subjecttext

  Form headerform paned defaultDistance 0 min 57 max 57 $backGround 
    Label From  headerform label {From:} $const_hlabel
    Label FromInfo headerform label {} $var_hlabel width 480 fromHoriz From
    Label Subject  headerform label {Subject:} $const_hlabel fromVert  From
    Label SubjectInfo  headerform label {} $var_hlabel width 480 \\
              fromHoriz Subject fromVert  From
    Label Date  headerform label {Date:} $const_hlabel fromVert  Subject
    Label DateInfo headerform label {} $var_hlabel width 480 \\
              fromHoriz Date fromVert Subject

   Text articletext paned $textatt {/dev/null} height 270 \\
         $normalFont autoFill true showGrip false $roColors allowResize true

    Box buttons paned $backGround showGrip false min 26 max 26 
	simpleButton quit     buttons  
	simpleButton abort    buttons  
	simpleButton post     buttons sensitive false 
	simpleButton followup buttons sensitive false 
	simpleButton reply    buttons sensitive false
            action reply override {<Btn3Up> : exec(echo "%w u")}
	simpleButton forward  buttons sensitive false 
            action forward override {<Btn3Up> : exec(echo "%w u")}
	simpleButton header   buttons sensitive false 
	simpleButton print    buttons sensitive false 

        Command save buttons sensitive false $buttonAtts \\
            callback {set mail -1; popup savemenu none}

	simpleButton send     buttons sensitive false 
	simpleButton cancel   buttons sensitive false 

           TransientShell  savemenu buttons 
           callback savemenu popupCallback positionCursor 45

             Dialog savetext savemenu label {File name or folder name:} \\
		 value {} $backGround
             sV savetext.label $backGround $boldFont 
             Command savequit savetext label {cancel} $buttonAtts \\
                 callback {sV subjecttext sensitive true;popdown savemenu}

             action savetext.value override \\
                  "<Key>Return: exec(sV subjecttext sensitive true;sendsave) \\
                                     XtMenuPopdown(savemenu)"
__
##################### uff, back in perl ############################

&changeLables();
&simpleMenue("groupmodes","groupmode","label",
	("with new articles","all subscribed","all available")); 
&simpleMenue("articlemodes","articlemode","label",
	("unread articles","newest 100","newest 300","newest 1000",
	 "all articles")); 
&simpleMenue("sortmodes","sortmode","label",
	("by Article Number","by Subject")); 
&simpleMenue("gsortmodes","gsortmode","label",
	("as in newsrc","alphabetic")); 
&Xui("realize; deleteWindowProtocol quit;"
    ."sV info label {reading from NNTP-server $server. Please stand by ...}");



unless ($NO_NNTP) {
#    ($nntpConn,$server) = &nntp'connect;
    &pArray($newsgroups, '>', "", "", 1, '','', &newsgroups());
    &Xui("sV grouptext string $newsgroups;sV info label {}");
};
undef $server;

&wafe'applyActions("grouptext",@textActions);
&wafe'applyActions("subjecttext",@textActions);
&wafe'applyActions("articletext", (@textActions, 
             'Ctrl<Key>w : exec(sV %w editType edit)',
             "Ctrl<Key>f : exec(sV %w $textFont)",
             "Ctrl<Key>v : exec(sV %w $normalFont)",
             ));
&wafe_mu'createConfig("topf","configButton",
		      ("mailIncludePrefix","printCommand","signatureFile",
                       "defaultMailHost","defaultMailEncoding"));


# 
# receive reply from nntp server 
# used  in updateSubjectLines

sub readBack
{
    local($conn,$num,$cmd) = @_;
    local($fail) = 0;
    local(@msg,$string,$buffer);

    $msgok = &chat'expect($conn, 100,
			  "^220 .*\r?\n", '1',
			  "^221 .*\r?\n", '1',
			  "^222 .*\r?\n", '1',
			  "^4.. .*\r?\n", '0')  || return (1,"");

    # gather the lines of the text into an array.  stop when you see
    # a dot on a line by itself.
    $*=1;
    ($string = &chat'expect($conn, 100, '^\.\r?\n', '$`', 'TIMEOUT','$fail=2,""')) =~ tr/\r//d;
    $*=0;
    ($fail,$string);
}


#
# read for a given newsgroup the header information
# depending on article mode

sub updateSubjectLines {
    local($lastng,$ng,$retain) = @_;
    local($low,$high,$rangelist,$rng);
    local($nrToRead,$nrRead);

    undef @subjectLines;
    $currentNewsGroup = $ng;

    &Xui("sV currentnewsgroup label {$ng}");
    local($sense) = $ng ? "true" : "false";
    &Xui("sV grep string {} sensitive $sense displayCaret $sense"); 

    $currentSubject=$currentDate=$currentFrom=$currentReplyto=$currentPath= 
	$currentArticle=$currentBody= '' unless $retain;
    &changeLables;
    &sendMode(0);


    if ($ng) {
	local($i,$j,$prev);
	local($first, $last, $fail);

        &pArray($newssubj,'subjecttext','','','', '','', ());
        &info("reading newsgroup $ng ..."); 

# print "doing now a setgroup $ng\n";
	if ($lastng ne $ng) {
	    return "no articles" unless &nntp'setgroup($nntpConn, $ng); #'# this should not happen
        }
# print "setgroup $ng DONE\n";

        ($low,$high) = (split(' ',$nntp'groups{$ng}))[(1,0)];  #'
	$rangelist = "$low-$high";

#      print "start rangelist = $rangelist, newsrc=<$nntp'newsrc{$ng}>\n";

	if ($currentArticleMode eq "unread articles") {
	    $rangelist = (split($;,$nntp'groupLine{$ng}))[1];   #'
        }

	if ($currentArticleMode =~ /newest\s+(\d+)\s*$/) {
            local($min) = ($high-$1);
            $min = $low if $min<$low;
	    $rangelist = "$min-$high";
        }

#       print "final rangelist = $rangelist, count= \n";
	local($toRead) = &nntp'canon_count($rangelist);

	foreach $rng (&nntp'canon_expand($rangelist)) {
	    ($first, $last) = split(/-/, $rng);
	    $first += 0; $last +=0;
#            local($_);

#          print "looking for $first to $last \n";
$starttime = time;
           next if $last == 0;

#            for($first .. $last) {
#                $Heads{$_} = &nntp'msgtext($nntpConn,$_,"head") if $Heads{$_} eq "";
#            }

            local($read);
            for($first .. $last) { 
		unless ($Heads{$_} && $do_sCache) {
		    $Heads{$_} = $subjectCache{"$ng$;$_"};
		}
		$read .= ",$_" if $Heads{$_};
	    }
            local($rrng) =&nntp'canon_inverse(&nntp'canon_artlist($read),$first,$last);
#     print " range to read in $ng: $rrng\n";
#    open(LOG,">>LOG");
#    print "$ng: reading $rrng\n";

            for ((split(',',$rrng))) {
                next if /0\-0/;
		if  (($i,$j) = /^(\d+)-(\d+)/) {
#		    ($i,$j) = ($1,$2);
		    $nrToRead++; 
#    print  "head 1: $i\n";
                   &chat'print($nntpConn, "head $i\r\n");
                   for($i+1 .. $j) {
#    print "head $_\n";
		       &chat'print($nntpConn, "head $_\r\n");

		       $nrToRead++; 
		       &info("reading newsgroup $ng, "
                                 .(sprintf("%2.0f",$nrToRead*100/$toRead))."%") 
			         if ($nrToRead % 10) == 0; 
		       $prev = $_-1;
		       ($fail,$Heads{$prev}) = &readBack($nntpConn,"head");
		       print "failed to read head $prev, reason $fail\n" if $fail>1;
		       $subjectCache{"$ng$;$prev"} = $Heads{$prev} if $do_sCache;
                   }
		   ($fail,$Heads{$j}) = &readBack($nntpConn,"head");
		   print "failed to read head $j, reason $fail\n" if $fail>1;
		   $subjectCache{"$ng$;$j"} = $Heads{$j} if $do_sCache;
	       } else {
#    print "head x: $i\n";
		&chat'print($nntpConn, "head $_\r\n");  #'
		($fail,$Heads{$_}) = &readBack($nntpConn,"head");
		$subjectCache{"$ng$;$_"} = $Heads{$_} if $do_sCache;
                print "failed to read single head $_, reason $fail\n" if $fail>1;
	       }
            }
#    print  "--------------\n\n\n";
#    close LOG;
#          print "lookup of header took ",(time - $starttime), " seconds\n";

            for($first .. $last) {
		if (length($Heads{$_}) < 20) {
#		    print "STRANGE HEAD $_ <$Heads{$_}>\n" ;
		    &markAsRead($_,1,0,0);
		    next;
		}
                $nrRead++;
                $subject=$from=$replyto=$lines="";
                $* = 1;
                   $subject = $1 if $Heads{$_} =~ m/^Subject:\s+(.*)/; 
                   $from = $1 if $Heads{$_} =~ m/^From:\s+(.*)/; 
                   $replyto = $1 if $Heads{$_} =~ m/^Reply-To:\s+(.*)/; 
                   $lines=$1 if $Heads{$_} =~ m/^Lines:\s+(\d+)/; 
                $* = 0;

		($name,$addr) = &wafe_mu'nameAddress($from); #'

#                print "RANGELIST returns <", &nntp'inRangelist($_,$nntp'newsrc{$ng}) , ">\n";
                push(@subjectLines,sprintf("%5d %s %-18s %-6s %-53s", $_, 
                                    ($Read{$_} || &nntp'inRangelist($_,$nntp'newsrc{$ng})) ? "R" : " ",
                                    substr($name || $addr,0,18),
                                    $lines && "($lines)"|| "",
                                    $subject
                         ));
            }
        }
     }
   &pArray($newssubj,'subjecttext',"",1,"", '','', @subjectLines);
#  print "reading headers lines takes ", time - $starttime," seconds\n";
   &info(""); 
   return ($nrRead+0)." articles found in $ng";
}


sub posting {
    local($filename,$widget,$type,$from,$subject,$date,$msgid,$mailbody) = @_;
    local($name,$fromaddr,$fullfromaddr,$posting);

    open(MAILTEXT,">$filename") || 
	(&main'info("can't open $filename for writing\n"), return(0)); #'"

    $subject = $type eq "post" ? "" : 
               ($subject =~ /^[Rr]e:/ ? $subject : "Re: $subject");

    local($toNewsGroup) = $currentNewsGroup;
    $toNewsGroup = $1 if $type eq "followup" 
               && $Heads{$currentArticleNumber} =~ /Followup\-To:\s+(.*)/;

    print MAILTEXT "Newsgroups: $toNewsGroup\nSubject: $subject\n"
	                     ."Distribution: $defaultNntpDistribution\n"
                             ."Organization: $defaultNntpOrganization\n";

    if ($type eq "followup") {
        print MAILTEXT "References: $currentReferences $msgid\n\n"
	         ."In article $msgid from [$date] you wrote:\n";
        for (split("\n",$mailbody)) {print MAILTEXT "$main'mailIncludePrefix$_\n";} 
    } else {
	print MAILTEXT "\n\n\n";
     }
     print MAILTEXT "\n\n--\n$wafe_mu'signature" if $wafe_mu'signature;  #"'
     close(MAILTEXT);

     &sendMode(2);
     &Xui("sV $widget type file string $filename");
     if ($type ne "post") {
	&Xui("callActionProc $widget {} forward-paragraph");
	&Xui("callActionProc $widget {} next-line");
	&Xui("callActionProc $widget {} next-line");
     } else {
	&Xui("callActionProc $widget {} next-line");
	&Xui("callActionProc $widget {} end-of-line");
     }
    return 1;
}

sub nntpSend {
    local($theMail) = @_;
    local($newsg,$subj,$inHeader,$lines,$control,$_);

    for (split("\n",$theMail)) {
	last unless $_;
	$newsg  = $1 if m/^Newsgroups:\s*(\S.*)$/;
	$subj = $1 if m/^Subject:\s*(\S.*)$/;
	$control = 1 if m/^Control:/;
	$lines++;
    }
       
# print "themail = <$theMail>\n---subj=<$subj>\n";

    unless ($subj) { &info("No subject specified in posting"); return(0);}
    unless ($newsg) { &info("No newsgroup specified"); return(0);}

    $lines = ($theMail =~ tr/\n/\n/) - $lines;

    &chat'print($nntpConn, "POST\r\n");  #'
    local($result,$msg) = &chat'expect($nntpConn, 100,  #'
		 "^340 .*$nntp'cr", '(0)',
		 "^4.. .*$nntp'cr", '(1,$&)',
		 "TIMEOUT", '(2)');
#print "result=<$result>, msg = <$msg>\n";
    &info("Posting not allowed: <$msg>"),return(0) if $result == 1;
    &info("Posting failed due to timeout"),return(0) if $result == 2;

    $control && &info("cancelling your article ...") ||
    &info("posting your article with $lines lines ...");

    local($theName) = (getpwuid($<))[6];
    $theName = $1 if $theName =~ /^([^,]+),/;
    $theName = "($theName)" if $theName;
    &chat'print($nntpConn,
                  "Path: $localHost!$wafe_mu'user\r\n" 
                 ."Message-ID: <".time."8-3$$@$localHost>\r\n"
                 ."From: $wafe_mu'user@$localHost $theName\r\n"
		 ."Date: " . (&wafe_mu'mailDateNow) . "\r\n"
                 ."Lines: $lines\r\n");

     $inHeader = 1;
     for (split("\n",$theMail)) {
         next if $inHeader && 
              /^(Path|Message\-ID|From|Date|Nntp\-Posting\-Host):/;
         $inHeader = 0 unless $_;

         $_ = ".$_" if /^\./;
#	 print "$_\n";
         &chat'print($nntpConn, "$_\r\n");
     }

    &chat'print($nntpConn, "\r\n.\r\n");
#	 print "\r\n.\r\n";
    $ret = &chat'expect($nntpConn, 300,
		 "^240 .*$nntp'cr", '1',
		 "^4.. .*$nntp'cr", '$&',
		 "^.*$nntp'cr", '$&',
			"TIMEOUT", '"Timeout after 300 seconds"');

    (&info("posting failed, reason: $ret") && return(0)) unless $ret == 1;

    $control && &info("article cancelled") || &info("article posted");

    &sendMode(0);
    local($target) = &wafe_mu'folderName("News","Articles");
    &wafe_mu'printArgInto(&wafe_mu'fromHeader($wafe_mu'user,$theMail),
                          ">> $target");
}



#
# mark article number $art  as read or unread 
# and update listing on demand

sub markAsRead {
    local($art,$read,$update,$screen) = @_;
#    print "mark as read of <$art> = <$read> $update, $screen\n";
    $Read{$art} = $read;
    $*=1;
    ($read && ($Xref{$art} = $1) || undef $Xref{$art}) if $Heads{$art} =~ /^Xref:(.*)$/;
    $* = 0;
    return unless $update;

    for ($[ .. $#subjectLines) {
	if (@subjectLines[$_] =~ /^\s*$art/) {
	    substr($subjectLines[$_],6,1) = $read == 1 ? "R" : " ";
	    last;
	}
    }
    return unless $screen;

    $* = 1;
    $Text{'subjecttext'} =~ /^\s*$art/;
    $* = 0;
    local($pos) = length($`);
    local($char) = $read ? 'R' : ' ';
    &Xui("doTextReplace subjecttext ".($pos+6)." ".($pos+7)." {$char}");
    &Xui("deselo subjecttext") unless $art == $currentArticleNumber;
}

#
# delete from array elements indexed by @elements

sub deleteElements {
    local(*array,@elements) = @_;
    local($_,@erase);
    for ($[ .. $#array) {
#	print "e=$elements[0], a=$array[$_]\n";
	if ($elements[0]  eq $array[$_]) {
#	    print " . . . equal, pushing $_, $array[$_], $#elements \n";
	    push(@erase,$_);
	    last unless shift(@elements);
	}
    }
    for (reverse @erase) {
        splice(@array,$_,1);
   }
}


#
# the application is mapped to the screen, let see, what it talks to us.
#

$FullHeader = 0;
$SendMode = 0;

while ($_=&wafe'read) {
    if (/^newsgroup\s+(\S*)/) {
        if ($SendMode) {
            &warn("Finish writing your mail before you switch to another newsgroup!");
	    next;
        }
        next if $1 eq $currentNewsGroup;
        &flushCurrentNewsGroup();
        local($lastNewsGroup) = $currentNewsGroup;
	$currentNewsGroup = $1;
        undef %Heads;
        $pattern = '';
#        &updateSubjectLines($currentNewsGroup, "",0);
	local($nextMsg) = &updateSubjectLines($lastNewsGroup,$currentNewsGroup,0);
        &updateGroupLines($lastNewsGroup);
	&info($nextMsg);
    }

    if (/^subscribe\-(last|first)\s*(.*)/) {
	local($ngs) = $2 || $currentNewsGroup || next;
	local($where) = $1;
        local(@new) = split(" ",$ngs);
        foreach (@new) {
	    $nntp'subscribed{$_}=1;             #'
            undef $nntp'newNewsGroup{$_};       #'
            undef $nntp'groupLine{$_};          #'
	}
        &deleteElements(*nntp'ngorder,@new);    #'
        @nntp'ngorder = ($where eq 'first') ?   #'
			(@new,@nntp'ngorder) :  #'
			(@nntp'ngorder,@new);   #'
        &updateGroupLines($currentNewsGroup);
        &updateSubjectLines($currentNewsGroup,"",1);
    }

    if (/^unsubscribe\s*(.*)/) {
	local($ngs) = $1 || $currentNewsGroup || next;
        foreach $ng (split(" ",$ngs)) {
            next unless $nntp'groups{$ng};      #' must be a bad selection
	    undef $nntp'subscribed{$ng};        #'
            undef $nntp'groupLine{$_};          #'
            push(@nntp'ngorder,$ng), $nntp'newsrc{$ng} = "0-0" 
		 if $nntp'newNewsGroup{$ng};    #'
            &info("Group $ng is unsubscribed now");
	}
        &updateGroupLines($currentNewsGroup);
        &updateSubjectLines($currentNewsGroup, "",0);
    }

    if (/^save-newsrc/) {
#      local($ngs) = $1 || $currentNewsGroup || next;
        &nntp'newsrc_write($opt_f);             #'
        &info("$opt_f file has been saved");
    }



    if (/^catchup/) {
        &info("catching up in newsgroup $currentNewsGroup");
        foreach  (keys %Heads) {  &markAsRead($_,1,0); }
        &flushCurrentNewsGroup();
        &updateGroupLines($currentNewsGroup);
        &updateSubjectLines($currentNewsGroup,"",0);
    }

    if (/^mark\s+(.+)/) {
        foreach  (split(/ /,$1)) {  
            &markAsRead($_,$Read{$_} ? 0 : 1,1,1); 
        }       
    }

    if (/^(article) (\S+).*/ || /^(body) (\S+).*/ || /^(head) (\S+).*/ || /^(next)/) {
        if ($SendMode) {
            &warn("Finish writing your mail before you read another article!");
	    next;
        }
        next if $currentArticleNumber == $2;
        $currentArticleNumber = $2;
        $currentSubject=$currentDate=$currentFrom=$currentReplyto=
                $currentPath= $currentReferences= '';
        &info("reading article $currentArticleNumber ...");
        ($fail,$currentBody) = &nntp'msgtext($nntpConn, $currentArticleNumber, "body"); #'
	&warn("could not retrieve article $curretnArticleNumber"),next if $fail;
        $currentArticle = $Heads{$currentArticleNumber} . "\n\n$currentBody";

        $*=1;  
             local($head);
             ($head = $Heads{$currentArticleNumber}) =~ s/\n\s+/ /g;
        $*=0;

        for (split("\n",$head)) {
		$currentPath = $1 if m/^Path:\s+(.*)/;
		$currentSubject = $1 if m/^Subject:\s+(.*)/;
		$currentFrom = $1 if m/^From:\s+(.*)/;
		$currentReplyto = $1 if m/^Reply\-To:\s+(.*)/;
		$currentMsgId = $1 if m/^Message\-ID:\s+(.*)/;
		$currentDate = $1 if m/^Date:\s+(.*)/;
		$currentReferences = $1 if m/^References:\s+(.*)/;
	}

       &changeLables;
       &sendMode(0);
       &markAsRead($currentArticleNumber,1,1,0); 
       &info("");
    }

    if (/^groupmode\s+(.*)/) {
        &flushCurrentNewsGroup() ;
	&groupMode($1,1);
    }
    if (/^articlemode\s+(.*)/) {
       $articlemode = $1;
       if ($currentArticleMode ne $1 || $thread) {
          $currentArticleMode = $articlemode;
          &updateSubjectLines($currentNewsGroup,$currentNewsGroup,1) 
	      if $currentNewsGroup;
	  $thread=0;
       }
    }


    if (/^(reply|forward)\s*(\w*)/) {
        &wafe_mu'returnMail($newsarticle,"articletext",$1,$2,    #'
	      $currentReplyto || $currentFrom,"",
	      $currentSubject,$currentDate,
	      $FullHeader?$currentArticle:$currentBody);
    }

    if (/^(post|followup)/) {
        &posting($newsarticle,"articletext",$1,
	      $currentReplyto || $currentFrom,$currentSubject,$currentDate,
              $currentMsgId,$currentBody);
    }

    if (/^print/) {
       &info("printing with $printCommand ...");
       local($content) = $SendMode ?
     	       &wafe_mu'widgetContent($newsarticle,"articletext") :  #'
	       $currentArticle;
       (&wafe_mu'printArgInto($content,"|$printCommand") &&          #'
               &info("File printed")) || 
               &warn("cannot print using $printCommand");
    }

    if (/^save\s+<(.*)>\s*(.*)/) {
        # $1 is either -1 for the current article or a number indicating the article
        # $2 is an optional file name
        local($doWith,$optFn) = ($1,$2);

        if ($doWith == -1) {
           local($target,$content,$from) = ($SendMode && $doWith == -1) ?
                 (&wafe_mu'folderName("Mail", $optFn || "outgoing"),        #'
	          &wafe_mu'widgetContent($newsarticle,"articletext"),       #'
                  $wafe_mu'user)    :                                       #'
                 (&wafe_mu'folderName("News", $optFn || $currentNewsGroup), #'
	          $currentArticle, 
	          $currentPath);
           local($ptarget) = ($target =~ /^\|/ ? $target : ">>$target");

           (&wafe_mu'printArgInto(&wafe_mu'fromHeader($from, $content),$ptarget)&&
                &info("article saved into $target")) || 
		&warn("file cannot be saved into $target");


         } else {
           local($target) = &wafe_mu'folderName("News", $optFn || $currentNewsGroup); #'
           local($ptarget) = ($target =~ /^\|/ ? $target : ">>$target");
           local($content);
           open(OUT,$ptarget);

           foreach $artNr (split(" ",$doWith)) {
               local($fail,$body) =&nntp'msgtext($nntpConn, $artNr, "body"); #'
	       &warn("could not retrieve article $artNr"),next if $fail;
 	       local($c) = $Heads{$artNr} ."\n\n $body";

               $* = 1; local($path) = "$1" if $c =~ m/^Path: (.*)/;  $* = 0;
               unless ($path) 
                    {print "CANNOT parse PATH in article $artNr <$c>\n---------\n";}
               local($content) = &wafe_mu'fromHeader($path || "nobody", $c); #'
               ((print OUT $content,"\n") &&
                    &info("article $artNr saved into $target")) || 
                    &warn("article $artNr cannot be saved into $target");

               &markAsRead($artNr,1,1,0);
           }
	close OUT;
        &info("articles saved into $target");
       }
    }

     if (/^grep\s+(.*)/) {
       local($npattern);
       ($npattern = $1) =~ s/(\W)/\\\1/g;
       next if $npattern eq $pattern;
       $pattern=$npattern;
       &pArray($newssubj,'subjecttext',$pattern,1,'', '','', @subjectLines);
       $thread=0;
     }
     if (/^refresh/) {
       &pArray($newssubj,'subjecttext',$pattern,1,'', '','', @subjectLines);
       &Xui("restorePos subjecttext") if $thread ;
       $thread=0;
     }

     if (/^thread/) {
       local($search,@referenced,@references);
       ($search = $currentMsgId) =~ s/(\W)/\\\1/g;
       local($cmd) = $_;

       &info("computing thread of current article ... ");
       &Xui("savePos subjecttext") unless $thread;
       local($searche) = 
               'foreach $k (keys %Heads) { $_ = $Heads{$k}; study;'
              . 'push(@referenced,$k),next if /'.$search.'/;';

       foreach $ref (split(/\s+/,$currentReferences)) {
               local($refm);
               ($refm = $ref) =~ s/(\W)/\\\1/g, 
               $searche .= 'push(@references,$k),next if /Message\-ID:\s+'.$refm.'/;';
       };
       eval "$searche}";

       &Xui("deselo subjecttext");
#       print "references: ",join(" ",@references),"\n";
#       print "referenced: ",join(" ",@referenced),"\n";
       &pArray($newssubj,'subjecttext',"",1,'',
            (join('\s)|^(\s*', sort @references)), 
            (join('\s)|^(\s*', sort @referenced)), 
            @subjectLines);
       $thread=1;
       &info("");
       $_=$cmd;
     }

    if (/^sortmode\s+(.*)/) {
	$currentSortMode = $1;
       &pArray($newssubj,'subjecttext',$pattern,1,"",'','',@subjectLines);
    }

    if (/^ggrep\s+(.*)/) {
       ($gpattern = $1) =~ s/(\W)/\\\1/g;
       &info("grepping in newsgroups ...");
       &pArray($newsgroups,'grouptext',$gpattern,0,1,'','',&newsgroups);
       &info("");
    }
    if (/^gsortmode\s+(.*)/) {
	$currentGroupSortMode = $1;
       &pArray($newsgroups,'grouptext',$gpattern,0,1,'','',&newsgroups);
    }


    if (/^send/) {
	&wafe_mu'send($newsarticle,"articletext") if $SendMode == 1;
	&nntpSend(&wafe_mu'widgetContent($newsarticle,"articletext")) if $SendMode == 2;
    }

    if (/^cancel/) {
	  &nntpSend("Newsgroups: $currentNewsGroup\nSubject: $currentSubject\n"
             ."Control: cancel $currentMsgId\n\ncancel $currentMsgId\n");
    }

    if (/^header/) {
       $FullHeader = !$FullHeader;
       &sendMode(0);
    }

    if (($varname) = /^setconfig\s*(\S+)$/) {
        local($value);
        eval '$value = $'."$varname;"; #'
        &Xui("sV configsetvaltext value {$value};popup configsetvalmenu none");
        undef $varname; 
    }
    if (($varname,$value) = /^setPerl\s(\S+)\s(.*)$/) {
        eval "\$$varname = \"".$value."\";";
        undef $varname; undef $value;
    }

    if (/^(quit|abort)/) {
	next if $SendMode && &sendMode(0);

        unless ($1 eq "abort") {
           &info("saving newsrc ...");
           &flushCurrentNewsGroup();
	   foreach $ng (grep($nntp'newNewsGroup{$_},keys %nntp'newNewsGroup)) {
              push(@nntp'ngorder,$ng) unless $nntp'subscribed{$ng};
           }
           &nntp'newsrc_write($opt_f);   #'
        }
	dbmclose(subjectCache) if $do_sCache;
        &wafe'cleanup();
    }

    &wafe'unlockTextWidget();
#   print "wafe says: $_.\n"; 
}

