#!/usr/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("setValues FromInfo label {$currentFrom} width 480");
   &Xui("setValues SubjectInfo label {$currentSubject} width 480");
   &Xui("setValues DateInfo label {$currentDate} width 480");
}


#
# 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 ##################
&UI( <<"End of Widget Tree");
#
# 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]; \\
              set content \$content[string range \$text(ptr) 0 [expr \$text(length)-1]]; \\
           }; \\
          echo got selection [Selection [string range \$content 0 [expr \$length-1]]];\\
          return [Selection [string range \$content 0 [expr \$length-1]]]}  \\
    {deselo \$w; echo empty selection}}

#
#
# own and disown of selections
#
set holder "";
proc selo {w} {global holder; \\
#		   echo selo \$w called; \\
	if {\$w == \$holder} return; \\
	deselo \$holder; \\
#	echo widget \$w owns now selection; \\
        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; \\
#		   echo deselo \$w called \$holder holder; \\
	if {\$holder == ""} return; \\
#	echo widget \$holder lost selection; \\	
        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 markInText {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"; \\
#	textGetSelectionPos \$w f t; \\
        textReplace \$w \$from \$to text; \\
#	textSetSelection \$w \$f \$t; \\\
	sV \$w editType read; \\
}

#
# standard button settings 

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 \$n}"}

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"; \\
      }

#
# 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()}; \\
    action \$Widget override {<SelClr>: exec(deselo %w)}; \\
    action \$Widget override {<Btn2Down>: exec(sV %w cursor pencil) select-start()}; \\
    action \$Widget  override {<Btn2Motion>: extend-adjust()}; \\
    action \$Widget override {<Btn2Up>: extend-end(CUT_BUFFER0) exec(selo %w;sV %w cursor hand2)}; \\
#    action \$Widget override {Shift<Motion>: extend-adjust()}; \\
}

#
# Widget tree of the appliction

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 
         action ggrep override "<Key>Return : exec(sendvalue %w)"
         action ggrep override "<Enter> : exec(sV %w $highLight)"
         action ggrep override "<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} 
#      simplerMenuButton configButton topf config \\
#           {label config fromVert gsort fromHoriz save-newsrc}
       

   shrink grouptext topf gsort 

#   asciiText grouptext paned $textatt  $newsgroups height 140 $roColors 
   asciiText grouptext paned $textatt /dev/null height 140 $roColors \\
	allowResize true cursor hand2 displayCaret false $textFont
      textSelectActions grouptext 
      action grouptext override {<Btn1Down>: \\
          select-start() select-end(CUT_BUFFER0) next-line() exec(selo %w)}
      action grouptext override {<Btn1Up>:\\
          exec(echo  newsgroup [Selection [fetchBuffer topf 0]])}; 
      action grouptext override {<Key>u:\\
          exec(echo  unsubscribe [Selection [fetchBuffer topf 0]])}; 
      action grouptext override {<Key>l:\\
          exec(echo  subscribe-last [Selection [fetchBuffer topf 0]])}; 
      action grouptext override {<Key>f:\\
          exec(echo  subscribe-first [Selection [fetchBuffer topf 0]])}
       action grouptext override {<Key>s: exec(echo  save-newsrc)}
 
   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}
         action grep override "<Key>Return : exec(sendvalue grep)"
         action grep override "<Enter> : exec(sV grep $highLight)"
         action grep override "<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
        textSelectActions subjecttext
        action subjecttext override {<Btn1Down>: \\
              select-start() select-end(CUT_BUFFER0) \\
	           exec(echo article [Selection [fetchBuffer topf 0]]; markInText %w) \\
                   select-start() extend-adjust() next-line() exec(selo %w)}
        action subjecttext override {<Btn1Up>:  exec(selo %w) previous-line()}
        action subjecttext override {<Btn3Down>:  exec(echo refresh)}
        action subjecttext override {<Btn3Motion>:  no-op()}
        action subjecttext override {<Btn3Up>:  no-op()}

        action subjecttext override {<Key>Return:\\
              select-start() select-end(CUT_BUFFER0) \\
	           exec(echo article [Selection [fetchBuffer topf 0]]; markInText %w) \\
                   select-start() extend-adjust() exec(selo %w)}
        action subjecttext override {<Key>Down:\\ 
                scroll-one-line-up() select-start() extend-end(CUT_BUFFER0) \\
	           exec(echo article [Selection [fetchBuffer topf 0]]; markInText %w) \\
                   select-start() extend-adjust() exec(selo %w)}
       action subjecttext override {<Key>Up:\\
                scroll-one-line-down() select-start() extend-end(CUT_BUFFER0) \\
	           exec(echo article [Selection [fetchBuffer topf 0]]; markInText %w) \\
                   select-start() extend-adjust() exec(selo %w)}
        action subjecttext override {<Key>s:\\
		 exec(global mail; set mail [Selection [fetchBuffer topf 0]]; popup savemenu exclusive)}

      action subjecttext override {<Key>u: exec(echo unsubscribe)}
      action subjecttext override {<Key>c: exec(echo catchup)}
      action subjecttext override {<Key>t: exec(echo thread)}
      action subjecttext override {<Key>m: exec(echo mark \[wSelection 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)"

#
# 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); \\
      sV \$w insertPosition \$insertPosition(\$w); \\
      }
End of Widget Tree

##################### 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"));


sub beep {
    &Xui("callActionProc articletext {} no-op RingBell"); 1;
}

# 
# 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" 
#                 ."Nntp-Posting-Host: $localHost\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 (<STDIN>) {
    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"; 
}

