#!/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",
	"e", ":show only entries in newsrc file (not checking for new groups)",
	"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;
$opt_e = !$opt_e;

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

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

$spellCommand = "ispell -l | sort -u" unless $spellCommand;
$spellSpoolFile = &wafe'tmpFile('spell'); #'

$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);
}
$Low = 0; 

#
# 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('labelDeselect'
	.';sV FromInfo label '.&TclQuote($currentFrom)
	.';sV SubjectInfo label '.&TclQuote($currentSubject)
	.';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 getnewsgroups {
    @NewsGroups = &nntp'subscribed($nntpConn,$opt_f,$currentGroupMode,$opt_e) #'
          unless @NewsGroups;
}

sub getnewnewsgroups {
    @NewsGroups = &nntp'subscribed($nntpConn,$opt_f,$currentGroupMode,$opt_e); #'
}


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

sub updateGroupLines {
    local($lastNewsGroup) = @_;
    if ($lastNewsGroup) { 
	&Xui("savePos grouptext");
        &nntp'groupLine($lastNewsGroup,1);  #'
	&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 ...");
	&getnewnewsgroups();
	&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) =  $Low>1 ? "1-".($Low-1)."," : "";
        local($artl) = &nntp'canon_artlist(join(',',grep($Read{$_},keys %Read))); #'
        # print "merging in $currentNewsGroup: ".$unAvail.$artl."\n";
	&nntp'newsrc_merge($currentNewsGroup, $unAvail.$artl); #'
	undef %Read; 
        foreach(values %Xref) {
	    foreach (split) { &nntp'newsrc_merge($1,$2) if /^([^:]+):(\d+)$/; }#'
        }
        undef %Xref;
    }
}

($nntpConn,$server) = &nntp'connect unless $NO_NNTP; #'

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

#
# the following settings are used several times

$textatt = "type file scrollVertical always string";
$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";
$left = "left chainLeft right chainLeft";
$right = "left chainRight right chainRight";

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

####################### tcl setup ##################
&Xui(<<'__');
mergeResources topLevel {
    *l_alias.label Alias:
    *l_name.label Name:
    *l_email.label Email:
    *TransientShell*quit.label Dismiss
    *TransientShell*add.label AddAlias
    }
#
# 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)
}

# ----------- expanding and grabbing of aliases ----------------
proc expandAlias {w} {
  global beginAlias endAlias
  set ip [XawTextGetInsertionPoint $w]
  set bl [XawTextSourceScan $w $ip EOL left 1 false]
  set el [XawTextSourceScan $w $ip EOL right 1 false]
  set line [XawTextRead $w $bl $el]
  if {[string match "To:*" $line] || \
      [string match "Cc:*" $line] || \
      [string match "Bcc:*" $line]} {
     set linePos [expr $ip-$bl]
     set begin [string range $line 0 $linePos]
     set end [string range $line $linePos end]
     append end " "
     set i [string last " " $begin]
     set j [string last , $begin]
     set bw [expr {$i>-1 && $i>$j ? $i : $j}]
     if $bw==-1 {set bw [string first :]}
     set i [string first " " $end]
     set j [string first , $end]
     set ew [expr {$j>-1 && $j<$i ? $j : $i}]
     incr ew $linePos
     incr bw 1
     set word [string range $line $bw $ew]
     if [string match {} word] return
     echo "expandAlias $w $word"
     set beginAlias [expr $bl+$bw]
     set endAlias [expr $bl+$ew]
     XawTextSetSelection $w $beginAlias $endAlias
  } else {
     callActionProc $w {} insert-string 0x09
  }
}

proc replaceAlias {w string} {
  global beginAlias endAlias
  set len [string length $string]
  set XawTextSearch(ptr) $string
  set XawTextSearch(length) $len
  set XawTextSearch(firstPos) 0
  XawTextReplace $w $beginAlias $endAlias XawTextSearch
  sV $w insertPosition [expr $beginAlias+$len]
}

# adding aliases
proc addAlias {alias name address} {
  global fields currentField
  if ![set S [widgetId addAliasShell]] {
    mergeResources topLevel {
	*addAliasShell*Text*editType edit 
	*addAliasShell*Text*displayCaret false 
        *addAliasShell*left chainLeft 
	*addAliasShell*right chainLeft 
	*addAliasShell*Text.right chainRight 
	*addAliasShell*Text.width 320
	*addAliasShell*Form.right chainRight 
	*addAliasShell*Label.width  80 
	*addAliasShell*Label.justify right 
	*addAliasShell*Label.borderWidth 0
	*addAliasShell*Command.bottom chainBottom
	*addAliasShell*Command.top chainBottom
	*addAliasShell*Command.left chainLeft
	*addAliasShell*Command.right chainLeft
    }

    set S [TransientShell addAliasShell paned]
    set T [Form aliasForm $S]
    set F [Form aliasInnerForm $T right chainRight bottom chainBottom]
      Label  l_alias $F 
      Text   alias   $F fromHoriz $F*l_alias

      Label  l_name  $F fromVert $F*alias
      Text   name    $F fromHoriz $F*l_name fromVert $F*alias

      Label  l_email $F fromVert $F*name
      Text   email   $F fromHoriz $F*l_email fromVert $F*name

    Command quit  $T callback "popdown $S"  fromVert $F
    Command add   $T callback "newAlias $S" fromHoriz $T*quit fromVert $F
    callback $S popupCallback positionCursor 0

    set fields [list [widgetId $S*alias] [widgetId $S*name] \
		[widgetId $S*email]]
    foreach f $fields {
      action $f override "\
	<Key>Return: exec(nextField $T) \n\
	<Key>Tab:    exec(nextField $T) \n\
	<Btn1Down>:  exec(gotoField $T %W)"
    }
  } 
  set currentField 0
  turnOn $S.aliasForm $S*alias
  sV $S*name  string $name
  sV $S*alias string $alias
  sV $S*email string $address
  popup $S none
}

proc newAlias {S} {
  global fields
  set command newAlias
  foreach f $fields { append command \t[gV $f string] }
  echo $command
  popdown $S
}

proc whenTextRO {w button char} {
  if {[string compare edit [gV $w editType]] && [gV $button sensitive]} {
    callCallbacks $button callback
  } else {  
    callActionProc $w {} insert-string $char
  }
}

proc whenSensitive {w button} {
  if [gV $button sensitive] { callCallbacks $button callback } 
}

# set field inactive or active
proc turnOff {f}   { sV $f displayCaret false }
proc turnOn  {s f} { sV $f displayCaret true;setKeyboardFocus $s $f }

# jump to the next field from the field list
proc nextField {s} { global currentField fields
    turnOff [lindex $fields $currentField]
    set currentField [expr ($currentField+1)%[llength $fields]]
    turnOn $s [lindex $fields $currentField]
}

# jump to the next field from the field list
proc gotoField {s f} { global currentField fields
    turnOff [lindex $fields $currentField]
    set currentField [lsearch $fields $f]
    turnOn $s $f
}

__

#
# 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 \\
     type string string "" \\
     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 skipAdjust true 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 \\
         type string string "" \\
	 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 skipAdjust true $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 {} $every_hlabel width 480 \\
              $normalFont fromHoriz Subject fromVert From right chainRight
    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 \\
         type string string "" \\
         $normalFont autoFill true showGrip false $roColors allowResize true

    Box buttons paned $backGround showGrip false skipAdjust true
	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)"
__
&Xui(<<'__');
foreach l {FromInfo SubjectInfo DateInfo currentnewsgroup} {
    action $l override { <Btn1Down>: exec(labelSelect %W) }
}

set selectedLabel {}
proc labelDeselect {} {
  global selectedLabel
  if [string compare "" $selectedLabel] { invert $selectedLabel }
  set selectedLabel {}
}

proc labelSelect {w} {
  global selectedLabel
  if ![string compare $w $selectedLabel] { 
      labelDeselect
      return
  } 
  labelDeselect
  if [string match "" [set contents [gV $w label]]] { return }
  invert $w
  ownSelection $w [gV $w label] labelDeselect NULL 
  set selectedLabel $w
}

proc invert {w} {
  sV $w background [gV $w foreground] foreground [gV $w background]
}

# key events in the following widges should be directed into the articletext
 foreach w {headerform buttons} { setKeyboardFocus $w articletext }

proc spellErrors {file textWidget} {
  set sh [TransientShell spellErrors topLevel]
  callback spellErrors popupCallback position info:100/15
  Form spellForm $sh
  Viewport spellView $sh.spellForm allowVert true height 200
  List spellList $sh*spellView \
      callback "spellSearch $sh $textWidget \"%s\" 1" \
      left chainLeft right chainRight top chainTop bottom chainBottom
  XawListChange $sh*spellList 0 0 1 File $file
  Command dismiss $sh.spellForm fromVert $sh*spellView \
      callback "destroyWidget $sh" \
      left chainLeft right chainLeft top chainBottom bottom chainBottom
  Command next $sh.spellForm fromVert $sh*spellView fromHoriz $sh*dismiss \
      sensitive false \
      callback "spellSearch $sh $textWidget {} 0" \
      left chainLeft right chainLeft top chainBottom bottom chainBottom
  popup $sh none
}

proc spellSearch {sh textW word first} {
  if $first {
     sV $sh*next sensitive true
     XawTextSetInsertionPoint $textW 0
  } else {
     XawTextSetInsertionPoint $textW [expr [XawTextGetInsertionPoint $textW]+1]
     set word [set [XawListShowCurrent $sh*spellList](string)]
  }
  set XawTextSearch(ptr) $word
  set XawTextSearch(length) [string length $word]
  set XawTextSearch(firstPos) 0
  set ip [XawTextSearch $textW right XawTextSearch]
  if {$ip<0} { 
    XawTextSetInsertionPoint $textW [expr [XawTextGetInsertionPoint $textW]-1]
    sV $sh*next sensitive false
    bell $sh 0
  } else { 
    XawTextSetInsertionPoint $textW $ip
    XawTextSetSelection $textW $ip [expr $ip+$XawTextSearch(length)]
  }
}

__
##################### 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) {
    &getnewsgroups();
    &pArray($newsgroups, '>', "", "", 1, '','', @NewsGroups);
    &Xui("sV grouptext type file string $newsgroups;sV info label {}");
};
undef $server;

&wafe'applyActions("grouptext",@textActions);
&wafe'applyActions("subjecttext",@textActions);
&wafe'applyActions('articletext', (@textActions, 
             'None<Key>h : exec(whenTextRO %W header   %a)',
             'Shift<Key>p: exec(whenTextRO %W post     %a)',
             'None<Key>r : exec(whenTextRO %W reply    %a)',
             'None<Key>f : exec(whenTextRO %W forward  %a)',
             'Shift<Key>f: exec(whenTextRO %W followup %a)',
             'None<Key>p : exec(whenTextRO %W print    %a)',
             'None<Key>s : exec(whenTextRO %W save     %a)',
             "Ctrl $meta<Key>Return: exec(whenSensitive %W send)",
             '<Key>Escape: exec(echo back)',
             '<Key>Tab   : exec(expandAlias %W)',
             'Ctrl<Key>a : exec(echo getAddress)',
             'Ctrl<Key>w : exec(sV %w editType edit)',
             'Ctrl<Key>p : exec(echo spell)',
             "Ctrl<Key>f : exec(sV %w $textFont)",
             "Ctrl<Key>v : exec(sV %w $normalFont)",
             ));
&wafe_mu'createConfig("topf","configButton",
		      ("printCommand","spellCommand",
		       "mailIncludePrefix","signatureFile",
		       "replyTo", "defaultMailHost","defaultMailEncoding"));


&wafe'setResources('',( #'
    '*addAliasShell*Command', $buttonAtts,
    '*addAliasShell*Label', "$backGround $boldFont",
    '*addAliasShell*Form*Text', $roColors,
    '*addAliasShell*Form', $backGround,
    '*spellErrors*List', $roColors,
    '*spellErrors*Command', $buttonAtts,
));

# 
# receive reply from nntp server 
# used  in updateSubjectLines

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

    if (&chat'expect($conn, 100, #'
	             "^220 .*\r?\n", '1',
		     "^221 .*\r?\n", '1',
		     "^222 .*\r?\n", '1',
	             "^4.. .*\r?\n", '0')) {
	# gather the lines of the text into a string.  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;
    } else {
        $fail = 1;
    }
    ($fail,$string);
}


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

sub updateSubjectLines {
    local($ng,$retain) = @_;
    local($high,$avail,$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,$first,$last,$fail,
              $subject,$from,$lines,$name,$addr);

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

	($avail,$Low,$high) = &nntp'setgroup($nntpConn, $ng);  #'
	return "No Articles!" unless $avail; # this should not happen

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

	if ($currentArticleMode eq "unread articles") {
	    local($_) = $nntp'groupLine{$ng}; #'
	    $rangelist = (split($;))[1] if $_; 
        } elsif ($currentArticleMode =~ /newest\s+(\d+)\s*$/) {
	    local($min) = ($high-$1);
	    $min = $Low if $min<$Low;
	    $rangelist = "$min-$high";
	} else {
	    $rangelist = "$Low-$high";
	}

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

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

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

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

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

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

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


sub posting {
    local($filename,$widget,$type,$from,$subject,$date,$msgid,$mailbody) = @_;
    local($name,$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]\n $from  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;
	$touchedNewsgroup{$currentNewsGroup} = 1;
        undef %Heads;
        $pattern = '';
	local($nextMsg) = &updateSubjectLines($currentNewsGroup,0);
        &updateGroupLines($lastNewsGroup);
	&info($nextMsg);
    }


    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 $currentArticleNumber"),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,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 (/^getAddress/) {
        local($sender) = $SendMode ? "" : $currentReplyto || $currentFrom;
	&wafe_mu'popupAliasDialog($sender, #'
	     'callActionProc articletext {} beginning-of-line');
    }

    &wafe_mu'expandAlias($1,$2) if /^expandAlias\s(\S+)\s(\S.*)$/; 
    &wafe_mu'addNewAlias($1,$2,$3) if /^newAlias\t([^\t]*)\t([^\t]*)\t(.*)$/;

    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 ...");
       &getnewsgroups();
       &pArray($newsgroups,'grouptext',$gpattern,0,1,'','',@NewsGroups);
       &info("");
    }
    if (/^gsortmode\s+(.*)/) {
	$currentGroupSortMode = $1;
	&getnewsgroups();
        &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 (/^save-newsrc/) {
        &flushCurrentNewsGroup() if $currentNewsGroup;
	&info("saving newsrc...");
        &nntp'newsrc_write($opt_f,1);  #'
        &info("$opt_f has been saved");
        if ($currentNewsGroup) {
            &updateSubjectLines($currentNewsGroup,0);
            $touchedNewsgroup{$currentNewsGroup} = 1;
        }
    }

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


    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("",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{$ng};         #'
            push(@nntp'ngorder,$ng), $nntp'newsrc{$ng} = "0-0" 
		 if $nntp'newNewsGroup{$ng};    #'
            &info("Group $ng is unsubscribed now");
	}
        &updateGroupLines($currentNewsGroup);
        &updateSubjectLines("",0);
    }

    if (/^spell/) {
      local($content) = &wafe_mu'widgetContent($newsarticle,'articletext'); #'
      local($errors) = &wafe_mu'spellArg($content,$spellCommand,$spellSpoolFile);#'
      # print "errors=$errors.\n";
      if ($errors > 0) {
	  &Xui("spellErrors $spellSpoolFile articletext");
      } elsif ($errors == -1) {
	  &info("could not execute SpellCommand '$spellCommand'");
      } else {
	  &info("No spelling errors found");
      }
    }

    &sendMode(0) if /^back/ && $SendMode;

    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,0);   #'
        }
	dbmclose(%subjectCache) if $do_sCache;
        &wafe'cleanup();
    }

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

