#!/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 {
       $asciiText{$w} = join("\n", @array);
       if ($opt_C) {
          &wafe'fileTransaction($fname, 
            "open(TMP, '>$fname') || die \"can't open $fname for writing!\";"
             .'print TMP  $main\'asciiText{"'.$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} {
  textGetSelectionPos $w from to
  if $from!=$to {
     set length [expr $to-$from]
     for {set toread $length; set read 0; set content ""} {$read<$length} {
         set from [expr $from+$text(length)]
	 set toread [expr $length-$read]} {
            textSourceRead $w $from text $toread
            set read [expr $text(length)+$read]
            append content [string range $text(ptr) 0 [expr $text(length)-1]]
         }
         return [Selection [string range $content 0 [expr $length-1]]]
  } else {deselo $w}
}

#
#
# 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
  textGetSelectionPos $w from to
  set text(firstPos) 0; set text(length) 1; set text(ptr) "R"
  textReplace $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
  textReplace $w $from $to text
  sV $w editType read
}
__


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

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

#
# standard button settings with simpler appearance

proc simplerButton {n father resources} {
  eval command \$n \$father $normalFont borderWidth 0 \\
      $backGround \$resources $threeDNarrow}

proc simplerButtonCB {n father resources} {
  simplerButton \$n \$father "\$resources callback {echo %w}"}

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

#
# standard menue settings

proc simpleMenue {name father label default vert horiz hlabel} {
  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} {
  textSetSelectionArray $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

    asciiText 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 

  asciiText 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
     asciiText 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 {global mail; \\
              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 

     asciiText 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(global mail; \\
             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

   asciiText 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 {global mail; 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;
    $asciiText{'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) = $currentMsgId) =~ s/(\W)/\\\1/g;
       local(@referenced,@references);
       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;
       &pArray($newsgroups,'grouptext',$gpattern,0,1,'','',&newsgroups);
    }
    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"; 
}

