home *** CD-ROM | disk | FTP | other *** search
/ DP Tool Club 31 / CDASC_31_1996_juillet_aout.iso / internet / rnr214.zip / RNRSELB.PAS < prev    next >
Pascal/Delphi Source File  |  1996-03-04  |  82KB  |  3,423 lines

  1. unit rnrselb;
  2.  
  3. {
  4.  
  5. rnrselb.pas - rnr selectandbrowse, viewarts, browseart and friends
  6.  
  7. }
  8.  
  9. {$I rnr-def.pas}
  10.  
  11. interface
  12.  
  13. uses rnrglob,rnrconf,genericf,rnrfunc,rnrio,rnrproc,rnrkill,
  14.   rnrmous,rnrfile,rnrinit,rnrart,rnrcrea
  15.  
  16. {$ifdef charset}
  17. ,rnrchar
  18. {$endif}
  19.  
  20. ;
  21.  
  22. const
  23.   yesfirstantikill=true;
  24.   nofirstantikill=false;
  25.  
  26. procedure selectandbrowse;
  27.  
  28. implementation
  29.  
  30. var
  31.   moreselecting: boolean;
  32.   skipsection: boolean;
  33.   starbeside: integer;
  34.   selected: array[1..maxarts] of boolean;
  35.   numselected: integer;
  36.   topline,botline: integer;
  37.   selsearchstring: string;
  38.  
  39. procedure setselected(artnum: integer; b: boolean);
  40.  
  41. begin
  42.   if selected[artnum]<>b then
  43.     begin
  44.       if b then
  45.         inc(numselected)
  46.       else
  47.         dec(numselected);
  48.  
  49.       selected[artnum] := b;
  50.     end;
  51. end;
  52.  
  53. procedure selrefreshbotline;
  54.  
  55. var
  56.   perthroughgroup: integer;
  57.  
  58. begin
  59.  
  60. {handle short-integer math}
  61.  
  62.   if numarts>600 then
  63.     perthroughgroup := (10*botline) div (numarts div 10)
  64.   else if numarts>300 then
  65.     perthroughgroup := (20*botline) div (numarts div 5)
  66.   else
  67.     perthroughgroup := (100*botline) div numarts;
  68.  
  69.   if perthroughgroup>100 then
  70.     perthroughgroup := 100;   {handle roundoffs more gracefully!}
  71.  
  72.   xclreolxy(1,sellpp+selheaderlines+3);
  73.  
  74.   xwritesss('?=help  ',currenttimestring,'   ');
  75.   xwritei(perthroughgroup);
  76.   xwrites('% through this group   ');
  77.   xwritei(numarts-botline);
  78.   xwrites(' more   ');
  79.  
  80.   if not quiet then
  81.     begin
  82.       xwritei(numselected);
  83.       xwrites(' selected ');
  84.     end;
  85.  
  86.   xwriteln;
  87.  
  88.  
  89. {
  90.   xwritelnsssisis('?=help  ',currenttimestring,'   ',throughgroup,
  91.    '% through this group   ',numarts-botline,' more on later screen(s)');
  92. }
  93.  
  94.   xclreol;
  95.   xgotoxy(1,sellpp+selheaderlines+4);
  96.  
  97. end;
  98.  
  99.  
  100.  
  101. procedure browseart(artnum: integer; numleft: integer;
  102.  var willupdatej: boolean);
  103.  
  104. var
  105.   quitatarteof: boolean;
  106.   lastlineshown: integer;
  107.   artfrom: string;
  108.   artsubject: string;
  109.   artmessageid: string;
  110.   artnewsgroups: string;
  111.   numlefts: string[30];
  112.   totlines: integer;
  113.   sawendofarticle: boolean;
  114.  
  115. procedure newbrowsescreen;
  116.  
  117. begin
  118.   xclrscr;
  119. end;
  120.  
  121. procedure morelines(linestoshow: integer);
  122.  
  123. var
  124.   s: string;
  125.   ff: boolean;
  126.   wastec: char;
  127.   brandnewlinesshown: integer;
  128.  
  129. begin
  130.   if arteof then
  131.     begin
  132.       if sawendofarticle or not confirmnextarticle then
  133.         donebrowse := true
  134.       else
  135.         begin
  136.  
  137. { can't use warn here -- the last line might be in the middle of the screen }
  138.          {warn('end of article');}
  139.           xwrites('end of article -- press any key ');
  140.           wastec := xreadkey;
  141.           xwrites(cr);
  142.           xclreol;
  143.  
  144.           sawendofarticle := true;
  145.           quitatarteof := false;
  146.         end
  147.     end
  148.   else
  149.     begin
  150.       sawendofarticle := false;  {just in case they rewound}
  151.  
  152.       quitatarteof := false;
  153.       ff := false;
  154.       brandnewlinesshown := 0;
  155.       while not arteof and (brandnewlinesshown<linestoshow) and not ff do
  156.         begin
  157.           getartl(s,cols-1,yestoscreen);
  158.           ff := (pos(^L,s)<>0);
  159.           showartl(s);
  160.           if pos('@',s)<>0 then
  161.             bodyaddress := getaddressfromline(s);
  162.           inc(brandnewlinesshown);
  163.           inc(lastlineshown);
  164.         end;
  165.     end;
  166. end;
  167.  
  168. procedure rewindtopline(newtopline: integer);
  169.  
  170. const
  171.   twirl='/-\|';
  172.  
  173. var
  174.   s: string;
  175.   skippedlines: integer;
  176.   toggle: integer;
  177.  
  178. begin
  179.   toggle := 1;
  180.   newbrowsescreen;
  181.  
  182.   quitatarteof := false;
  183.   artreset;
  184.  
  185.   lastlineshown := 0;
  186.  
  187.   skippedlines := min(0,newtopline);
  188.   while skippedlines<newtopline do
  189.     begin
  190.       getartl(s,cols-1,yestoscreen);
  191.       inc(skippedlines);
  192.       inc(lastlineshown);
  193.       if (skippedlines and 31)=0 then
  194.         begin
  195.           xwritess(copy(twirl,toggle,1),#8);
  196.           toggle := 1+(toggle mod 4);
  197.         end;
  198.     end;
  199.  
  200.   xwritess(' ',#8);  {erase the rotating char}
  201.  
  202.   morelines(lpp-1);
  203. end;
  204.  
  205. procedure browserefresh;
  206.  
  207. begin
  208.   rewindtopline(lastlineshown-lpp+1);
  209. end;
  210.  
  211. procedure showlastline;
  212.  
  213. var
  214.   wastes: string;
  215.  
  216. begin
  217.   if totlines<0 then
  218.     begin
  219.       xwrites('Searching for bottom line...');
  220.       totlines := 0;
  221.       artreset;
  222.       while not arteof do
  223.         begin
  224.           inc(totlines);
  225.           getartl(wastes,cols-1,yestoscreen);
  226.         end;
  227.     end;
  228.  
  229.   rewindtopline(totlines-lpp+1);
  230. end;
  231.  
  232. procedure browsehelppage;
  233.  
  234. var
  235.   wastec: char;
  236.  
  237. begin
  238.   xclrscr;
  239.   hwritexy(1,1,
  240.    newsreadername+' '+newsreaderversion+' - newsreader-under-development');
  241.  
  242.   hwritexy(1,2,
  243.    'Russell_Schulz@locutus.ofB.ORG ('+releasedate+')');
  244.  
  245.   hwritexy(1,4,
  246.    '{space},{d},{CR} - forward 1 page, 1/2 page, 1 line');
  247.  
  248.   hwritexy(1,5,
  249.    '{u} - back 1 page             {=} back to selection screen');
  250.  
  251.   hwritexy(1,6,
  252.    '{^},{$} - top, bottom line    {TAB} skip this Subject: group');
  253.  
  254.   hwritexy(1,7,
  255.    '{n},{p} - next,previous selected article (or next group if at end)');
  256.  
  257.   hwritexy(1,8,
  258.    '{b},{a} - back,ahead through all articles (selected or unselected)');
  259.  
  260.   hwritexy(1,9,
  261.    '{r} - reply to author (in mail)    {/} search {.} search again');
  262.  
  263.   if maypost then
  264.     hwritexy(1,10,
  265.      '{m} - remail this message          {f} - followup (public, in netnews)')
  266.   else
  267.     hwritexy(1,10,
  268.      '{m} - remail this message');
  269.  
  270.   hwritexy(1,11,
  271.    '{k} - kill by subject or author (to not display again)');
  272.   hwritexy(1,12,
  273.    '{K} - antikill by subject or author (for auto-selection)');
  274.   hwritexy(1,13,
  275.    '{^R} - reread kill and antikill files from disk');
  276.  
  277.   if trusted then
  278.     hwritexy(1,14,
  279.      '{e} - edit actual article    {A} - add alias  {D} - rot13 decode')
  280.   else
  281.     hwritexy(1,14,
  282.      '                           {A} - add alias  {D} - rot13 decode');
  283.  
  284.   hwritexy(1,15,
  285.    '{x} - extract embedded file      {s},{w} - save/write article to disk');
  286.   hwritexy(1,16,
  287.    '{h} - toggle full header display  {^L} - refresh screen');
  288.   hwritexy(1,17,
  289.    '{!} - shell escape    {^D} - delete  {N} - next group (no update)');
  290.   hwritexy(1,19,
  291.    '{?} - help     {:} command mode      {Q} - quit (no update)');
  292.  
  293.   hwritexy(1,21,'file:');
  294.   hwritexy(7,21,artfn);
  295.  
  296.   hwritexy(1,23,
  297.    'see rnr.doc for more information.  press any key to return ');
  298.  
  299.   wastec := xreadkey;
  300.  
  301.   browserefresh;
  302. end;
  303.  
  304. procedure browseback;
  305.  
  306. begin
  307.   donebrowse := true;
  308.   browsedir := -1;
  309.   browseonlysel := false;
  310. end;
  311.  
  312. procedure browseahead;
  313.  
  314. begin
  315.   donebrowse := true;
  316.   browseonlysel := false;
  317. end;
  318.  
  319. procedure editart;
  320.  
  321. begin
  322.   if trusted then
  323.     begin
  324.       artclose;
  325.  
  326.       mouseshutdown;
  327.       execp(editor,editoroptions+' '+artfn);
  328.       mouseinit;
  329.  
  330.       if execresult<>0 then
  331.         warnerr(editor,execresult);
  332.  
  333.       artreset;
  334.  
  335.       browserefresh;
  336.  
  337.       headerinmem := '';  {in case user edited headers}
  338.     end;
  339. end;
  340.  
  341. procedure actuallyreplytoart;
  342.  
  343. var
  344.   subject: string;
  345.   inreplyto: string;
  346.   replyaddr: string;
  347.   artreplyto: string;
  348.   toaddr: string;
  349.  
  350.   newreplyaddr: string;
  351.   replyname: string;
  352.   defaultreply: boolean;
  353.   author: string;
  354.   originalfrom: string;
  355.   ccaddr: string;
  356.  
  357.   afullname: string;
  358.  
  359. begin
  360.   artclose;
  361.  
  362.   subject := artsubject;
  363.   subject := 'Re: '+nore(subject);
  364.   inreplyto := artmessageid;
  365.  
  366.   replyaddr := '';
  367.  
  368.   artreplyto := getheaderline(artfn,'reply-to:');
  369.   if isreasonableaddress(artreplyto) then
  370.     replyaddr := artreplyto;
  371.  
  372.   if replyaddr='' then
  373.     replyaddr := artfrom;
  374.   if replyaddr='' then
  375.     replyaddr := mailfrom;
  376.  
  377.   author := getfromaddr(replyaddr);
  378.  
  379.   if (author=getfromaddr(mailfrom)) or (author=getfromaddr(newsfrom)) then
  380.     begin
  381.       toaddr := getheaderline(artfn,'to:');
  382.       if toaddr<>'' then
  383.         begin
  384.           if isreasonableaddress(toaddr) then
  385.             replyaddr := toaddr;
  386.         end;
  387.     end;
  388.  
  389.   if not isreasonableaddress(author) then
  390.     if pos('@',replyaddr)<>0 then
  391.       begin
  392.         warn2('it looks like there are problems with the headers',
  393.          'for this message -- trying to compensate with other headers');
  394.         replyaddr := getaddressfromline(replyaddr);
  395.         author := replyaddr;
  396.       end;
  397.  
  398.   if not isreasonableaddress(author) then
  399.     if isreasonableaddress(bodyaddress) then
  400.       begin
  401.         warn2('it looks like there are problems with the headers',
  402.          'for this message -- trying to compensate with body text');
  403.         replyaddr := bodyaddress;
  404.         author := replyaddr;
  405.       end;
  406.  
  407. {handle case where Reply-To: is same as From:, but without name - keep it}
  408.   if getfromname(replyaddr)='' then
  409.     if upper(getfromaddr(replyaddr))=upper(getfromaddr(artfrom)) then
  410.       replyaddr := artfrom;
  411.  
  412. {`sender' isn't legal, but I've seen it}
  413.   if (replyaddr='poster') or (replyaddr='sender') then
  414.     replyaddr := artfrom;
  415.  
  416.   if not isreasonableaddress(replyaddr) then
  417.     begin
  418.       warn('invalid address -- trying From: header');
  419.       replyaddr := artfrom;
  420.     end;
  421.  
  422.   replyname := getfromname(replyaddr);
  423.   replyaddr := getfromaddr(replyaddr);
  424.  
  425. {use `warn2' to avoid overwriting an address in the sig, say}
  426.  
  427.   if upper(replyaddr)<>upper(getfromaddr(artfrom)) then
  428.     warn2(
  429. 'just for your info:  From: header not used -- either Reply-To: or To: or',
  430. 'body text was used, in order to get the most useful address');
  431.  
  432.   xclreolxy(1,lpp);
  433.   xwrites('Reply To: ');
  434.  
  435.   newreplyaddr := replyaddr;
  436.   xreadlnse(newreplyaddr,cols-15,yespreserve,endkeyswithspace);
  437.  
  438.   if newreplyaddr='' then
  439.     newreplyaddr := replyaddr;
  440.  
  441.   defaultreply := (newreplyaddr=replyaddr);
  442.  
  443.   if not defaultreply then
  444.     begin
  445.       replyaddr := newreplyaddr;
  446.       replyname := '';
  447.     end;
  448.  
  449. {get the address (if possible) from aliases and user/*/forward files}
  450.  
  451.   replyaddr := expandmail(replyaddr);
  452.  
  453.   if not quiet then
  454.     begin
  455.       xclreolxy(1,lpp-1);
  456.       xclreolxy(1,lpp-2);
  457.       if (pos('!',replyaddr)=0) and
  458.        (pos('@',replyaddr)=0) and
  459.        (pos(',',replyaddr)=0) then
  460.         begin
  461.           afullname := getfullnameforuser(lower(replyaddr));
  462.           if afullname='' then
  463.             afullname := ' (local, unknown name)'
  464.           else
  465.             afullname := ', '+afullname;
  466.           xwritesss('To: ',replyaddr,afullname)
  467.         end
  468.       else
  469.         xwritess('To: ',replyaddr);
  470.     end;
  471.  
  472.   xclreolxy(1,lpp);
  473.  
  474.   xwrites('CC: ');
  475.   xreadlnse(ccaddr,cols-10,nopreserve,endkeyswithspace);
  476.   ccaddr := expandmail(ccaddr);
  477.   xclreolxy(1,lpp);
  478.  
  479.   if replyaddr<>getfromaddr(artfrom) then
  480.     originalfrom := artfrom
  481.   else
  482.     originalfrom := '';
  483.  
  484.   editanddeliver(
  485.   {subject        }  subject,
  486.   {inreplyto      }  inreplyto,
  487.   {replyaddr      }  replyaddr,
  488.   {replyname      }  replyname,
  489.   {ccaddr         }  ccaddr,
  490.   {originalfrom   }  originalfrom,
  491.   {author         }  author,
  492.   {defaultreply   }  defaultreply,
  493.   {includedfile   }  '',
  494.   {justremail     }  false
  495.                 );
  496.  
  497.   artreset;
  498.  
  499. {leave refresh to caller}
  500.  
  501. end;
  502.  
  503. procedure replytoart;
  504.  
  505. begin
  506.   if not maymail then
  507.     begin
  508.       warn('you may not mail -- check your configuration');
  509.       browserefresh;
  510.     end
  511.   else
  512.     actuallyreplytoart;
  513. end;
  514.  
  515. procedure actuallymailart;
  516.  
  517. var
  518.   subject: string;
  519.   inreplyto: string;
  520.   mailaddr: string;
  521.   author: string;
  522.   ccaddr: string;
  523.   afullname: string;
  524.  
  525. begin
  526.   artclose;
  527.  
  528.   subject := artsubject;
  529.   inreplyto := artmessageid;
  530.  
  531.   author := getheaderline(artfn,'reply-to:');
  532.  
  533.   if not isreasonableaddress(author) then
  534.     author := '';
  535.  
  536.   if author='' then
  537.     author := artfrom;
  538.   
  539.   if author='' then
  540.     author := '<unknown>';
  541.  
  542. {`sender' isn't legal, but I've seen it}
  543.   if (author='poster') or (author='sender') then
  544.     author := artfrom;
  545.  
  546.   if not isreasonableaddress(author) then
  547.     if isreasonableaddress(bodyaddress) then
  548.       author := bodyaddress;
  549.  
  550.   author := getfromaddr(author);
  551.  
  552.  
  553.  
  554.  
  555.   xclreolxy(1,lpp);
  556.   xwrites('Mail To: ');
  557.  
  558.   xreadlnse(mailaddr,cols-15,nopreserve,endkeyswithspace);
  559.  
  560.   if mailaddr='' then
  561.     mailaddr := mailfrom;
  562.   mailaddr := expandmail(mailaddr);
  563.  
  564.   if not quiet then
  565.     begin
  566.       xclreolxy(1,lpp-1);
  567.       xclreolxy(1,lpp-2);
  568.       if (pos('!',mailaddr)=0) and
  569.        (pos('@',mailaddr)=0) and
  570.        (pos(',',mailaddr)=0) then
  571.         begin
  572.           afullname := getfullnameforuser(lower(mailaddr));
  573.           if afullname='' then
  574.             afullname := ' (local, unknown name)'
  575.           else
  576.             afullname := ', '+afullname;
  577.           xwritesss('To: ',mailaddr,afullname)
  578.         end
  579.       else
  580.         xwritess('To: ',mailaddr);
  581.     end;
  582.  
  583.   xclreolxy(1,lpp);
  584.  
  585.   xwrites('CC: ');
  586.   xreadlnse(ccaddr,cols-10,nopreserve,endkeyswithspace);
  587.   ccaddr := expandmail(ccaddr);
  588.   xclreolxy(1,lpp);
  589.  
  590.   editanddeliver(
  591.   {subject        }  subject,
  592.   {inreplyto      }  inreplyto,
  593.   {replyaddr      }  mailaddr,
  594.   {replyname      }  '',
  595.   {ccaddr         }  ccaddr,
  596.   {originalfrom   }  '',
  597.   {author         }  author,
  598.   {defaultreply   }  false,
  599.   {includedfile   }  '',
  600.   {justremail     }  true
  601.                 );
  602.  
  603.   artreset;
  604.  
  605. {leave refresh to caller}
  606.  
  607. end;
  608.  
  609. procedure mailart;
  610.  
  611. begin
  612.   if not maymail then
  613.     begin
  614.       warn('you may not mail -- check your configuration');
  615.       browserefresh;
  616.     end
  617.   else
  618.     actuallymailart;
  619. end;
  620.  
  621. procedure browsecopytofolder;
  622.  
  623. var
  624.   folder: string;
  625.   folderdir: string;
  626.   oldlastline: integer;
  627.  
  628. begin
  629.   if not quiet then
  630.     begin
  631.       xclreolxy(1,lpp-3);
  632.       xclreolxy(1,lpp-2);
  633.       xwritelns('Enter a folder (e.g., `=misc'') as a destination for');
  634.       xclreolxy(1,lpp-1);
  635.       xwritelns('this article, or leave blank to abort');
  636.     end;
  637.  
  638.   folder := lastfolder;
  639.  
  640.   xclreolxy(1,lpp);
  641.   xwrites('Copy to Folder: ');
  642.   xreadlnse(folder,max(cols-20,70),yespreserve,endkeyswithspace);
  643.   xclreolxy(1,lpp);
  644.  
  645.   if (folder<>'') and (numoccur('\',unslash(folder))=0) and
  646.    (numoccur(':',folder)=0) and (pos('..',folder)=0) then
  647.     begin
  648.       if folder[1]<>'=' then
  649.         folder := '='+folder;
  650.  
  651.       lastfolder := folder;
  652.  
  653.       xwritesss('Copying to ',folder,'...');
  654.  
  655.       unfoldergroup(folder);
  656.  
  657.       if not joinedtoexactgroup(folder) then
  658.         addnewmailgroup(folder);
  659.  
  660.       folderdir := getgroupdir(folder);
  661.       mkhier(folderdir);
  662.           
  663.       oldlastline := lastlineshown;
  664.  
  665.       artclose;
  666.       copyfile(artfn,getuniqfile(folderdir));
  667.  
  668.       artreset;
  669.       lastlineshown := oldlastline;
  670.     end;
  671.  
  672.   browserefresh;
  673. end;
  674.  
  675. procedure actuallymovetofolder(folder: string);
  676.  
  677. var
  678.   folderdir: string;
  679.  
  680. begin
  681.   if folder[1]<>'=' then
  682.     folder := '='+folder;
  683.  
  684.   if folder<>'=trash' then
  685.     lastfolder := folder;
  686.  
  687.   xwritesss('Moving to ',folder,'...');
  688.  
  689.   unfoldergroup(folder);
  690.  
  691.   if not joinedtoexactgroup(folder) then
  692.     addnewmailgroup(folder);
  693.  
  694.   folderdir := getgroupdir(folder);
  695.   mkhier(folderdir);
  696.           
  697.   artclose;
  698.   copyfilethenempty(artfn,getuniqfile(folderdir));
  699.  
  700. {$ifdef if_you_delete_the_file_you_have_to_be_way_more_careful}
  701.  
  702.   movefile(artfn,getuniqfile(folderdir));
  703.  
  704. { if #3 is the highest-numbered, and you move #3, it'd mess up your }
  705. { join file.  perhaps it just should just re-enter the group somehow? }
  706. { or go through the data structures, removing any evidence of this }
  707. { article (harder when there's more than one!) }
  708.  
  709.   if (artfn=getuniqfile(folderdir)) and willupdatej then
  710.     begin
  711.       warn('join file will not be updated for this group');
  712.       willupdatej := false;
  713.     end;
  714.  
  715. {$endif}
  716.  
  717.   moreselecting := true;
  718.   donebrowse := true;
  719. end;
  720.  
  721. procedure browsemovetofolder;
  722.  
  723. var
  724.   folder: string;
  725.   folderdir: string;
  726.  
  727. begin
  728.   if not ismailgroup(currsource) then
  729.     begin
  730.       warn('not a mail group -- using Copy instead');
  731.       browsecopytofolder;
  732.     end
  733.   else
  734.     begin
  735.  
  736.       if not quiet then
  737.         begin
  738.           xclreolxy(1,lpp-3);
  739.           xclreolxy(1,lpp-2);
  740.           xwritelns('Enter a folder (e.g., `=misc'') as a destination for');
  741.           xclreolxy(1,lpp-1);
  742.           xwritelns('this article, or leave blank to abort');
  743.         end;
  744.  
  745.       folder := lastfolder;
  746.  
  747.       xclreolxy(1,lpp);
  748.       xwrites('Move to Folder: ');
  749.       xreadlnse(folder,max(cols-20,70),yespreserve,endkeyswithspace);
  750.       xclreolxy(1,lpp);
  751.  
  752.       if (folder<>'') and (numoccur('\',unslash(folder))=0) and
  753.        (numoccur(':',folder)=0) and (pos('..',folder)=0) then
  754.         begin
  755.           actuallymovetofolder(folder);
  756.         end
  757.       else
  758.         begin
  759.           browserefresh;
  760.         end;
  761.     end;
  762. end;
  763.  
  764. procedure browsedeletemail;
  765.  
  766. var
  767.   deleteoops: char;
  768.  
  769. begin
  770.   if not ismailgroup(currsource) then
  771.     begin
  772.       warn('not a mail group -- cannot delete');
  773.       browserefresh;
  774.     end
  775.   else
  776.     begin
  777.       xclreolxy(1,lpp-1);
  778.       deleteoops := onekeydef('{d}elete (move to =trash), {o}ops','do','o');
  779.  
  780.       xclreolxy(1,lpp);
  781.  
  782.       if deleteoops='o' then
  783.         browserefresh
  784.       else
  785.         actuallymovetofolder('trash');
  786.     end;
  787. end;
  788.  
  789. procedure followtoart(newfollowupto: string);
  790.  
  791. var
  792.   followupto: string;
  793.   newsgroups: string;
  794.   originalnewsgroups: string;
  795.   originalauthor: string;
  796.   shouldmail: boolean;
  797.   shouldfollow: boolean;
  798.   replyfollow: char;
  799.   subject: string;
  800.   messageid: string;
  801.   references: string;
  802.   inreplyto: string;
  803.   author: string;
  804.   refline: string;
  805.   ref1, ref2: string;
  806.   mightbearef: string;
  807.  
  808. begin
  809.   xclreolxy(1,lpp);
  810.   xwrites('Follow...');
  811.  
  812.   artclose;
  813.   followupto := getheaderline(artfn,'followup-to:');
  814.   newsgroups := artnewsgroups;
  815.   originalnewsgroups := newsgroups;
  816.  
  817. {`sender' isn't legal, but I've seen it}
  818.   shouldfollow := true;
  819.   shouldmail := (followupto='poster') or (followupto='sender') or 
  820.    (pos('@',followupto)<>0) or (pos('!',followupto)<>0) or
  821.    (pos('%',followupto)<>0);
  822.  
  823.   if shouldmail or
  824.    (currsourcekind<>sourcegroup) or
  825.    ismailgroup(currsource) then
  826.     begin
  827.       followupto := newsgroups;
  828.  
  829.       xclreolxy(1,lpp-2);
  830.       xclreolxy(1,lpp-1);
  831.  
  832.       if ismailgroup(currsource) then
  833.         xwritelns('this is a private mail folder.')
  834.       else if currsourcekind<>sourcegroup then
  835.         xwritelns('this is not a real newsgroup.')
  836.       else
  837.         xwritelns('author seemed to want replies by mail only.');
  838.  
  839.       replyfollow := 
  840.        onekey('{r}eply by mail, {f}ollowup anyway, {q}uit','rfq');
  841.  
  842.       if replyfollow='r' then
  843.         xwrites('reply...')
  844.       else if replyfollow='f' then
  845.         xwrites('follow...');
  846.  
  847.       shouldmail := (replyfollow='r');
  848.       shouldfollow := (replyfollow='f');
  849.     end;
  850.  
  851.   if not maypost then
  852.     shouldmail := true;
  853.  
  854.   if shouldmail then
  855.     begin
  856.       artreset; {replytoart closes it immediately}
  857.       replytoart;
  858.     end
  859.   else if shouldfollow then
  860.     begin
  861.  
  862. { don't propogate errors in the Newsgroups: line if you can help it }
  863.  
  864.       newsgroups := unspace(newsgroups);
  865.       followupto := unspace(followupto);
  866.  
  867. {ignore Followup-To: when supplying new followup group explicitly}
  868.  
  869.       if newfollowupto<>'' then
  870.         followupto := '';
  871.  
  872.       if followupto='' then
  873.         begin
  874.           followupto := newsgroups;
  875.           followupto := default(newfollowupto,followupto);
  876.           followupto := default(currsource,followupto);
  877.         end
  878.       else
  879.         begin
  880.  
  881. { give the user a warning, to avoid blind Followup-To: misc.test,talk.bizarre }
  882.           if followupto<>newsgroups then
  883.             warn('followups have been changed');
  884.         end;
  885.  
  886. {followups redirected to /dev/null - but there are some local groups}
  887. {with no . -- but there shouldn't be, since crossposting is a pain then}
  888.  
  889.       if pos('.',followupto)=0 then
  890.         begin
  891.           warn('new groups list has no `.'' -- looks suspicious');
  892.         end;
  893.  
  894. { always warn again if there's a .test group in user's post }
  895.  
  896.       if pos( '.test,' , followupto+',' )<>0 then
  897.         begin
  898.           warn3
  899.            (
  900.            'there is a .test group on this post -- it may result in',
  901.            'you getting a lot of automated mail from around the world.',
  902.            'remove it unless you _really_ know what you are doing.'
  903.            );
  904.         end;
  905.  
  906. {}{}{} {should warn user if there are unknown groups that might be errors}
  907.  
  908. {}{}{} {should do each group individually!}
  909.  
  910.       if newfollowupto<>'' then
  911.         if newfollowupto<>'poster' then
  912.           if pos(','+newfollowupto+',',','+followupto+',')=0 then
  913.             followupto := followupto+','+newfollowupto;
  914.  
  915. {currsource isn't necessarily even in the followupto list, so don't warn}
  916. {about moderation when people post followups to, say,}
  917. {news.announce.newgroups where followups are always redirected to news.groups}
  918.  
  919. {}{}{} {should check if _any_ group in the list is moderated, and give warning}
  920.       if pos(','+currsource+',',','+followupto+',')<>0 then
  921.         if ismoderated(currsource) then
  922.           warn('this group is moderated');
  923.  
  924. {}{}{} {should check if _any_ of the groups is marked as /solo => strip it out}
  925.       if groupbattr(currsource,'/solo') and (pos(',',followupto)<>0) then
  926.         begin
  927.           warn('warning: /solo group - crosspost removed');
  928.           followupto := currsource;
  929.         end;
  930.  
  931. { warn on 5 or more groups, suggest followupto of first group }
  932. { but don't overwrite previous :follow direction! }
  933.  
  934.       if (numoccur(',',followupto)>3) and (newfollowupto='') then
  935.         begin
  936.           warn('massive crossposting--edit or delete the Followup-To: line');
  937.           newfollowupto := copy(followupto,1,pos(',',followupto)-1);
  938.           if currsourcekind=sourcegroup then
  939.             if pos(','+currsource+',' , ','+followupto+',')<>0 then
  940.               newfollowupto := currsource;
  941.         end;
  942.  
  943.       subject := artsubject;
  944.       subject := 'Re: '+nore(subject);
  945.       messageid := artmessageid;
  946.       references := getheaderline(artfn,'references:');
  947.  
  948. {Andrew system non-compliance, looks like}
  949.  
  950.       inreplyto := getheaderline(artfn,'in-reply-to:');
  951.       inreplyto := getfirstw(inreplyto);
  952.  
  953.       if length(references)+length(inreplyto)<250 then
  954.         if enclosedin(inreplyto,'<','>') then
  955.           if pos(inreplyto,references)=0 then
  956.             references := references+' '+inreplyto;
  957.  
  958.       author := getheaderline(artfn,'reply-to:');
  959.  
  960. {`sender' isn't legal, but I've seen it}
  961.       if (author='poster') or (author='sender') then
  962.         author := '';
  963.  
  964.       if (author<>'') and (pos('!',author)=0) and (pos('@',author)=0) then
  965.         begin
  966.           warn('invalid Reply-To:  - using From:');
  967.           author := '';
  968.         end;
  969.  
  970.       if author='' then
  971.         author := getheaderline(artfn,'from:');
  972.  
  973. {handle case where Reply-To: is same as From:, but without name - keep the}
  974. {name if you can}
  975.  
  976.       if getfromname(author)='' then
  977.         if upper(author)=upper(getfromaddr(artfrom)) then
  978.           if getfromname(artfrom)<>'' then
  979.             author := getfromaddr(author)+' ('+getfromname(artfrom)+')';
  980.  
  981.       originalauthor := '';
  982.       if getfromaddr(author)<>getfromaddr(artfrom) then
  983.         originalauthor := artfrom;
  984.  
  985.  
  986. { special-casing in getheaderline() makes sure we get the last few }
  987. { references always.  well, except on >255 char References: lines }
  988.  
  989.       ref1 := '';
  990.       ref2 := '';
  991.  
  992.       refline := references;
  993.       ref1 := chopfirstw(refline);
  994.       if not enclosedin(ref1,'<','>') then
  995.         ref1 := '';
  996.  
  997.       if refline<>'' then
  998.         begin
  999.           ref2 := chopfirstw(refline);
  1000.           if not enclosedin(ref2,'<','>') then
  1001.             ref2 := '';
  1002.         end;
  1003.  
  1004.       while numoccur('>',refline)>0 do
  1005.         begin
  1006.           mightbearef := chopfirstw(refline);
  1007.           if enclosedin(mightbearef,'<','>') then
  1008.             begin
  1009.               if ref2<>'' then
  1010.                 ref1 := ref2;
  1011.               ref2 := mightbearef;
  1012.             end;
  1013.         end;
  1014.  
  1015.       refline := '';
  1016.       if ref1<>'' then
  1017.         refline := refline+ref1+' ';
  1018.       if ref2<>'' then
  1019.         refline := refline+ref2+' ';
  1020.  
  1021.       refline := refline+messageid;
  1022.  
  1023.       createpost(followupto,originalnewsgroups,newfollowupto,subject,
  1024.        refline,author,originalauthor,'');
  1025.  
  1026.       editandinjnews(followupto,originalnewsgroups,author);
  1027.  
  1028.     end;
  1029.   artreset;
  1030.   browserefresh;
  1031. end;
  1032.  
  1033. procedure follow;
  1034.  
  1035. var
  1036.   newfollowupto: string;
  1037.  
  1038. begin
  1039.   if not maypost then
  1040.     begin
  1041.       warn('you do not have access to post this way');
  1042.       browserefresh;
  1043.     end
  1044.   else
  1045.     begin
  1046.       newfollowupto := internalcmdlineparams;
  1047.  
  1048.       if newfollowupto='' then
  1049.         newfollowupto := currsource;
  1050.  
  1051.       xclreolxy(1,lpp);
  1052.       xwrites('Followup-To: ');
  1053.       xreadlnse(newfollowupto,max(cols-20,70),yespreserve,endkeyswithspace);
  1054.  
  1055.       if newfollowupto='' then
  1056.         newfollowupto := currsource;
  1057.  
  1058. {possibly expand the group}
  1059. {explicitly does not expand to a mail folder -- that wouldn't make sense}
  1060. {neither expand the magic word `poster'}
  1061.  
  1062. {}{}{} {should expand _each_ group separately}
  1063.  
  1064.       if newfollowupto<>'poster' then
  1065.         if numoccur(',',newfollowupto)=0 then
  1066.           if not isavalidgroup(newfollowupto) then
  1067.             if joinedtogroup(newfollowupto) then
  1068.               ;
  1069.  
  1070.       followtoart(newfollowupto);
  1071.  
  1072. {followtoart does a browserefresh}
  1073.  
  1074.     end;
  1075.  
  1076. end;
  1077.  
  1078. procedure cancel;
  1079.  
  1080. var
  1081.   yn: char;
  1082.   newsubj: string;
  1083.  
  1084. begin
  1085.   if not (trusted and maypost) then
  1086.     begin
  1087.       warn('you do not have access to cancel posts');
  1088.     end
  1089.   else
  1090.     begin
  1091.       xclreolxy(1,lpp-8);
  1092.       xclreolxy(1,lpp-7);
  1093.       xwritelnss('          you are: ',newsfrom);
  1094.       xclreolxy(1,lpp-6);
  1095.       xwritelnss('this article from: ',artfrom);
  1096.       xclreolxy(1,lpp-5);
  1097.       xclreolxy(1,lpp-4);
  1098.       if newsfrom=artfrom then
  1099.         xwritelns('(looks the same)')
  1100.       else
  1101.         xwritelns('NOT THE SAME!');
  1102.       xclreolxy(1,lpp-3);
  1103.       xclreolxy(1,lpp-2);
  1104.       xwritelns('cancel will remove this article from every system');
  1105.       xclreolxy(1,lpp-1);
  1106.       xwritelns('world-wide.  do not do this unless authorized.');
  1107.       xclreolxy(1,lpp);
  1108.  
  1109.       yn := onekeydef('are you SURE you are authorized {Y}/{n}',
  1110.        'Yn','n');
  1111.  
  1112.       if yn='Y' then
  1113.         begin
  1114.           newsubj := 'cmsg cancel '+artmessageid;
  1115.           createcancel(artnewsgroups,newsubj,artmessageid,artfrom);
  1116.           editandinjnews(artnewsgroups,'','');
  1117.         end;
  1118.  
  1119.     end;
  1120.  
  1121. {caller must refresh}
  1122.  
  1123. end;
  1124.  
  1125. procedure killart;
  1126.  
  1127. var
  1128.   subjectfromoops: char;
  1129.   whichart: integer;
  1130.   killstring: string;
  1131.  
  1132. begin
  1133.   subjectfromoops := onekeydef(
  1134.    'kill: this group: {s}ubject {f}rom; always {S}ubject {F}rom; {o}ops',
  1135.    'sfSFo','o');
  1136.  
  1137.   if (subjectfromoops<>'o') and (currsourcekind<>sourcegroup) then
  1138.     begin
  1139.       if subjectfromoops<>upcase(subjectfromoops) then
  1140.         warn('only global kills can be done from here');
  1141.       subjectfromoops := upcase(subjectfromoops);
  1142.     end;
  1143.  
  1144.   if subjectfromoops<>'o' then
  1145.     begin
  1146.       if (subjectfromoops='s') or (subjectfromoops='S') then
  1147.         begin
  1148.           xwrites(cr);
  1149.           xclreol;
  1150.  
  1151.           killstring := articles[artnum]^.basesubject;
  1152.  
  1153.           xwrites('Working...');
  1154.  
  1155.           addtokill('Subject',killstring,(subjectfromoops='S'));
  1156.  
  1157. {}{} {overrides antikill-even-killed!}
  1158.  
  1159. {too much checking here - won't hurt anything but the clock}
  1160.           for whichart := 1 to numarts do
  1161.             if selected[whichart] then
  1162.               if artkilled(articles[whichart]^.basesubject,
  1163.                articles[whichart]^.from,'',artfn) then
  1164.                 setselected(whichart,false);
  1165.  
  1166.           xwrites(cr);
  1167.           xclreol;
  1168.         end
  1169.       else
  1170.         begin
  1171.           xwrites(cr);
  1172.           xclreol;
  1173.           xwrites('Working...');
  1174.  
  1175.           killstring := getfromaddr(artfrom);
  1176.  
  1177.           addtokill('From',killstring,(subjectfromoops='F'));
  1178.  
  1179. {}{} {overrides antikill-even-killed!}
  1180.  
  1181. {too much checking here - won't hurt anything but the clock}
  1182.           for whichart := 1 to numarts do
  1183.             if selected[whichart] then
  1184.               if artkilled(articles[whichart]^.basesubject,
  1185.                articles[whichart]^.from,'',artfn) then
  1186.                 setselected(whichart,false);
  1187.  
  1188.           xwrites(cr);
  1189.           xclreol;
  1190.         end;
  1191.       donebrowse := true;
  1192.     end;
  1193.  
  1194.   if not quiet then
  1195.     selrefreshbotline;
  1196. end;
  1197.  
  1198. procedure antikillart;
  1199.  
  1200. var
  1201.   subjectfromoops: char;
  1202.   whichart: integer;
  1203.   antikillstring: string;
  1204.  
  1205. begin
  1206.   subjectfromoops := onekeydef(
  1207.    'antikill: this group: {s}ubject {f}rom; always {S}ubject {F}rom; {o}ops',
  1208.    'sfSFo','o');
  1209.  
  1210.   if (subjectfromoops<>'o') and (currsourcekind<>sourcegroup) then
  1211.     begin
  1212.       if subjectfromoops<>upcase(subjectfromoops) then
  1213.         warn('only global antikills can be done from here');
  1214.       subjectfromoops := upcase(subjectfromoops);
  1215.     end;
  1216.  
  1217.   if subjectfromoops<>'o' then
  1218.     begin
  1219.       if (subjectfromoops='s') or (subjectfromoops='S') then
  1220.         begin
  1221.           xwrites(cr);
  1222.           xclreol;
  1223.           xwrites('Working...');
  1224.  
  1225.           antikillstring := articles[artnum]^.basesubject;
  1226.  
  1227.           addtoantikill('Subject',antikillstring,(subjectfromoops='S'));
  1228.  
  1229. {too much checking here - won't hurt anything but the clock}
  1230.  
  1231.           for whichart := 1 to numarts do
  1232.             if artantikilled(articles[whichart]^.basesubject,
  1233.              articles[whichart]^.from,'',artfn) then
  1234.               begin
  1235.                 setselected(whichart,true);
  1236.                 articles[whichart]^.indents :=
  1237.                  articles[whichart]^.indents or 128;
  1238.               end;
  1239.  
  1240.           xwrites(cr);
  1241.           xclreol;
  1242.         end
  1243.       else
  1244.         begin
  1245.           xwrites(cr);
  1246.           xclreol;
  1247.           xwrites('Working...');
  1248.  
  1249.           antikillstring := getfromaddr(artfrom);
  1250.           addtoantikill('From',antikillstring,(subjectfromoops='F'));
  1251.  
  1252. {too much checking here - won't hurt anything but the clock}
  1253.  
  1254.           for whichart := 1 to numarts do
  1255.             if artantikilled(articles[whichart]^.basesubject,
  1256.              articles[whichart]^.from,'',artfn) then
  1257.               begin
  1258.                 setselected(whichart,true);
  1259.                 articles[whichart]^.indents :=
  1260.                  articles[whichart]^.indents or 128;
  1261.               end;
  1262.  
  1263.           xwrites(cr);
  1264.           xclreol;
  1265.         end;
  1266.     end;
  1267.  
  1268.   if not quiet then
  1269.     selrefreshbotline;
  1270. end;
  1271.  
  1272. procedure toggleheaders;
  1273.  
  1274. begin
  1275.   showallheaders := not showallheaders;
  1276.   firstemptyline := maxint;
  1277.   rewindtopline(0);
  1278. end;
  1279.  
  1280. procedure rereadkillfiles;
  1281.  
  1282. begin
  1283.   readinkill(nobackupkill);
  1284.   readinantikill(nobackupkill);
  1285.   browserefresh;
  1286. end;
  1287.  
  1288. procedure quitbrowsenoupdate;
  1289.  
  1290. var
  1291.   doit: boolean;
  1292.  
  1293. begin
  1294.   doit := true;
  1295.  
  1296.   if confirmquit then
  1297.     doit := (onekey('are you SURE you want to quit?  {y}/{n}','yn')='y');
  1298.  
  1299.   if not doit then
  1300.     browserefresh
  1301.   else
  1302.     begin
  1303.       xwriteln;
  1304.       if willupdatej then
  1305.         xwritelnss('quitting without updating join file for ',currsource);
  1306.  
  1307.       xwriteln;
  1308.  
  1309.       donebrowse := true;
  1310.       donegroup := true;
  1311.  
  1312.       if willupdatej then
  1313.         begin
  1314.           currsource := '';
  1315.           needtofindnextgroup := false;
  1316.           willupdatej := false;
  1317.         end;
  1318.     end;
  1319. end;
  1320.  
  1321. procedure nextgroupnoupdate;
  1322.  
  1323. var
  1324.   doit: boolean;
  1325.  
  1326. begin
  1327.   doit := true;
  1328.  
  1329.   if confirmnextgroup then
  1330.     doit := (onekey(
  1331.      'are you SURE you want to jump directly to the next group?  {y}/{n}',
  1332.      'yn')='y');
  1333.  
  1334.   if doit then
  1335.     begin
  1336.       if willupdatej then
  1337.         begin
  1338.           xwriteln;
  1339.           xwritelns('join file not updated');
  1340.           xwriteln;
  1341.           willupdatej := false;
  1342.         end;
  1343.       donebrowse := true;
  1344.       donegroup := true;
  1345.     end
  1346.   else
  1347.     browserefresh;
  1348. end;
  1349.  
  1350. {}{} {might want to start at top of screen for this?}
  1351.  
  1352. procedure browsesearchnext;
  1353.  
  1354. var
  1355.   s: string;
  1356.   foundatline: integer;
  1357.   oldlastline: integer;
  1358.  
  1359. begin
  1360.   if arteof then
  1361.     browserefresh
  1362.   else
  1363.     begin
  1364.       xclreolxy(1,lpp);
  1365.       xwrites('Searching...');
  1366.  
  1367.       oldlastline := lastlineshown;
  1368.       foundatline := -1;
  1369.       while not arteof and (foundatline<0) do
  1370.         begin
  1371.           getartl(s,cols-1,yestoscreen);
  1372.           inc(lastlineshown);
  1373.  
  1374. {doesn't catch strings split over when you have long, wrapped lines! }
  1375. { but this is not a big problem when breaking at work boundaries }
  1376.  
  1377.           if textintext(browseuppersearchstring,upper(s)) then
  1378.             foundatline := lastlineshown;
  1379.         end;
  1380.  
  1381.       if foundatline<0 then
  1382.         begin
  1383.           warn('not found past this screen');
  1384.           lastlineshown := oldlastline;
  1385.           browserefresh;
  1386.         end
  1387.       else
  1388.         rewindtopline(foundatline-2);
  1389.     end;
  1390. end;
  1391.  
  1392. procedure browsesearch;
  1393.  
  1394. var
  1395.   newsearchstring: string;
  1396.  
  1397. begin
  1398.   xclreolxy(1,lpp);
  1399.   xwrites('/');
  1400.   xreadlns(newsearchstring,max(cols-4,76),nopreserve);
  1401.  
  1402.   if newsearchstring<>'' then
  1403.     browseuppersearchstring := upper(newsearchstring);
  1404.  
  1405.   if browseuppersearchstring='' then
  1406.     browserefresh
  1407.   else
  1408.     begin
  1409.       highlightsearchhits := true;
  1410.       browsesearchnext;
  1411.     end;
  1412. end;
  1413.  
  1414. procedure browsesearchagain;
  1415.  
  1416. begin
  1417.   if browseuppersearchstring<>'' then
  1418.     begin
  1419.       highlightsearchhits := true;
  1420.       browsesearchnext;
  1421.     end;
  1422. end;
  1423.  
  1424. procedure searchdigest;
  1425.  
  1426. var
  1427.   s: string;
  1428.   foundatline: integer;
  1429.   oldlastline: integer;
  1430.  
  1431. begin
  1432.   if arteof then
  1433.     donebrowse := true
  1434.   else
  1435.     begin
  1436.       oldlastline := lastlineshown;
  1437.       foundatline := -1;
  1438.       while not arteof and (foundatline<0) do
  1439.         begin
  1440.           getartl(s,cols-1,yestoscreen);
  1441.           inc(lastlineshown);
  1442.           if pos('--------',s)=1 then
  1443.             foundatline := lastlineshown;
  1444.         end;
  1445.       if foundatline<0 then
  1446.         begin
  1447.           warn('no more digest markers found');
  1448.           lastlineshown := oldlastline;
  1449.           browserefresh;
  1450.         end
  1451.       else
  1452.         rewindtopline(foundatline-2);
  1453.     end;
  1454. end;
  1455.  
  1456. procedure viewart;
  1457.  
  1458. begin
  1459.   if trusted then
  1460.     begin
  1461.       xclreolxy(1,lpp);
  1462.       xwritelns('running '+viewcommand+'...');
  1463.  
  1464.       mouseshutdown;
  1465.       execviacomspec(viewcommand+' '+artfn);
  1466. {}{}{}{}{ignore execresult?}
  1467.       mouseinit;
  1468.  
  1469.       browserefresh;
  1470.     end;
  1471. end;
  1472.  
  1473. procedure extractart;
  1474.  
  1475. begin
  1476.  
  1477. {
  1478.   warn('extract not yet completed...');
  1479.  
  1480.   finfoheader := getheaderline(artfn,'x-finfo:');
  1481.   if finfoheader='' then
  1482.     warn('no X-Finfo: header found');
  1483.  
  1484.   warn('X-Finfo: header is '+finfoheader);
  1485. }
  1486.  
  1487.   if trusted then
  1488.     begin
  1489.       xclreolxy(1,lpp);
  1490.       xwritelns('running '+extractcommand+'...');
  1491.  
  1492.       mouseshutdown;
  1493.       execviacomspec(extractcommand+' '+artfn);
  1494. {}{}{}{}{ignore execresult?}
  1495.       mouseinit;
  1496.  
  1497.       browserefresh;
  1498.     end;
  1499. end;
  1500.  
  1501. procedure printart;
  1502.  
  1503. begin
  1504.   if trusted then
  1505.     begin
  1506.       xclreolxy(1,lpp);
  1507.       xwritelns('running '+printcommand+'...');
  1508.  
  1509.       mouseshutdown;
  1510.       execviacomspec(printcommand+' '+artfn);
  1511. {}{}{}{}{ignore execresult?}
  1512.       mouseinit;
  1513.  
  1514.       browserefresh;
  1515.     end;
  1516. end;
  1517.  
  1518. procedure browsecommand;
  1519.  
  1520. var
  1521.   commandline: string;
  1522.   commandverb: string;
  1523.  
  1524. begin
  1525.   xclreolxy(1,lpp);
  1526.   xwrites(':');
  1527.   xreadlns(commandline,max(cols-4,76),nopreserve);
  1528.  
  1529.   internalcmdlineparams := commandline;
  1530.   commandverb := lower(chopfirstw(internalcmdlineparams));
  1531.  
  1532.   if commandline='' then
  1533.     browserefresh
  1534.   else
  1535.     begin
  1536.       if partialmatch(commandverb,'help','h') then
  1537.         browsehelppage
  1538.       else if partialmatch(commandverb,'?','?') then
  1539.         browsehelppage
  1540.       else if partialmatch(commandverb,'next','n') then
  1541.         donebrowse := true
  1542.       else if partialmatch(commandverb,'postfile','postf') then
  1543.         begin postfile; browserefresh; end
  1544.       else if partialmatch(commandverb,'post','p') then
  1545.         begin post; browserefresh; end
  1546.       else if partialmatch(commandverb,'mailfile','mailf') then
  1547.         begin mailfile; browserefresh; end
  1548.       else if partialmatch(commandverb,'mail','m') then
  1549.         begin mail; browserefresh; end
  1550.       else if partialmatch(commandverb,'follow','f') then
  1551.         follow
  1552.       else if partialmatch(commandverb,'cancel','can') then
  1553.         begin cancel; browserefresh; end
  1554. {ugh} else if partialmatch(commandverb,'print','pr') then
  1555.         printart
  1556.       else if partialmatch(commandverb,'set','set') then
  1557.         begin
  1558.           justhandleset(internalcmdlineparams,issuspicious);
  1559.           browserefresh;
  1560.         end
  1561.       else if partialmatch(commandverb,'unset','unset') then
  1562.         begin
  1563.           justhandleunset(internalcmdlineparams,issuspicious);
  1564.           browserefresh;
  1565.         end
  1566.       else if partialmatch(commandverb,'show','sho') then
  1567.         begin
  1568.           usershow(internalcmdlineparams);
  1569.           browserefresh;
  1570.         end
  1571.       else if partialmatch(commandverb,'version','v') then
  1572.         begin
  1573.           showversion;
  1574.           browserefresh;
  1575.         end
  1576.       else if partialmatch(commandverb,'quit','q') then
  1577.         quitbrowsenoupdate
  1578.       else
  1579.         begin
  1580.           warn('unrecognized command');
  1581.           browserefresh;
  1582.         end;
  1583.     end;
  1584. end;
  1585.  
  1586. procedure geteopkey;
  1587.  
  1588.  
  1589. var
  1590.   ch: char;
  1591.   needakey: boolean;
  1592.   dataline: string;
  1593.   throughart: real;
  1594.  
  1595.   footermousechars: string;
  1596.  
  1597. begin
  1598.   footermousechars := '=<>npu^$''';
  1599.   if hasmouse then
  1600.     footermousechars := ' '+footermousechars+' '
  1601.   else
  1602.     footermousechars := '';
  1603.  
  1604.   repeat
  1605.     needakey := false;
  1606.  
  1607.     dataline := '--'+currenttimestring+'--?=help'+footermousechars+
  1608.      '--'+numlefts+'--';
  1609.  
  1610.     if arteof then
  1611.       begin
  1612.         dataline := dataline+'(Bottom)--';
  1613.         totlines := lastlineshown;
  1614.       end
  1615.     else if totlines>0 then
  1616.       begin
  1617.         throughart := lastlineshown;
  1618.         throughart := 100*throughart;
  1619.         throughart := throughart/totlines;
  1620.  
  1621.         dataline := dataline+itoa(round(throughart))+'%--';
  1622.       end;
  1623.  
  1624.  
  1625.     if length(dataline)+length(currsource)>(cols-6) then
  1626.       dataline := '--'+
  1627.        copy(currsource,length(currsource)-((cols-6)-length(dataline)),255)+
  1628.        dataline
  1629.     else
  1630.       dataline := '--'+currsource+dataline;
  1631.  
  1632.     xwritess(dataline,' ');
  1633.     ch := xreadkey;
  1634.     xwrites(^M);
  1635.     xclreol;
  1636.     ch := browsemap[ch];
  1637.  
  1638.     case ch of
  1639.       '?': browsehelppage;
  1640.       'n': donebrowse := true;
  1641.       'p': begin donebrowse := true; browsedir := -1; end;
  1642.       'a': browseahead;
  1643.       'b': browseback;
  1644.       'u': rewindtopline(lastlineshown-lpp-(lpp div 2));
  1645.       '<': rewindtopline(lastlineshown-lpp-(lpp div 2));
  1646.       ^B : rewindtopline(lastlineshown-lpp-(lpp div 2));
  1647.       '^': rewindtopline(0);
  1648.       ^A : rewindtopline(0);
  1649.       '$': showlastline;
  1650.       ^E : showlastline;
  1651.        ^N: morelines(1);
  1652.        ^P: rewindtopline(lastlineshown-lpp);
  1653.        cr: morelines(1);
  1654.       ' ': morelines(lpp-3);
  1655.       '>': morelines(lpp-3);
  1656.       ^F : morelines(lpp-3);
  1657.       'd': morelines(lpp div 2);
  1658.       'w': begin writeart; needakey := true; browserefresh; end;
  1659.       's': begin saveart; needakey := true; browserefresh; end;
  1660.       'r': begin replytoart; needakey := true; browserefresh; end;
  1661.       'm': begin mailart; needakey := true; browserefresh; end;
  1662.       'C': begin browsecopytofolder; needakey := true; end;
  1663.       'M': begin browsemovetofolder; needakey := true; end;
  1664.       'f': begin followtoart(''); needakey := true; end;
  1665.       'k': begin killart; needakey := true; browserefresh; end;
  1666.       'K': begin antikillart; needakey := true; browserefresh; end;
  1667.       'e': begin editart; needakey := true; end;
  1668.       'D': begin rot13ing := not rot13ing; browserefresh; end;
  1669.       'c': begin compactspaces := not compactspaces; browserefresh; end;
  1670.       'h': toggleheaders;
  1671.       ^L : browserefresh;
  1672.       ^R : begin rereadkillfiles; needakey := true; end;
  1673.       ^D : begin browsedeletemail; needakey := true; end;
  1674.       'Q': quitbrowsenoupdate;
  1675.       'N': nextgroupnoupdate;
  1676.       '!': begin shellout; browserefresh; end;
  1677.       '=': begin moreselecting := true; donebrowse := true; end;
  1678.       tab: begin skipsection := true; donebrowse := true; end;
  1679.       '/': browsesearch;
  1680.       '.': browsesearchagain;
  1681.       ':': browsecommand;
  1682.       'A': begin addalias(artfrom); browserefresh; end;
  1683.       ^G : searchdigest;
  1684.       'v': viewart;
  1685.       'x': extractart;
  1686.       else needakey := true;
  1687.     end; {case}
  1688.   until donebrowse or not needakey;
  1689. end;
  1690.  
  1691. begin {browseart}
  1692.   internalcmdlineparams := '';
  1693.  
  1694.   bodyaddress := '';
  1695.  
  1696.   rot13ing := false;
  1697.   compactspaces := false;
  1698.   showallheaders := false;
  1699.  
  1700.   numlefts := itoa(numleft)+' more';
  1701.   if numleft=0 then
  1702.     numlefts := 'LAST';
  1703.  
  1704.   if not willupdatej then
  1705.     numlefts := numlefts+' (no update)';
  1706.  
  1707.   newbrowsescreen;
  1708.   quitatarteof := true;
  1709.   lastlineshown := 0;
  1710.  
  1711.   totlines := -1;
  1712.   sawendofarticle := false;
  1713.  
  1714.   highlightsearchhits := false;
  1715.  
  1716.   usingalternatecolor := true;  {it gets toggled immediately after headers}
  1717.   shouldswitchcolor := false;
  1718.  
  1719.   artfn := withbackslash(currdir)+articles[artnum]^.filename;
  1720.   assign(artf,artfn);
  1721.  
  1722.   donebrowse := false;  {artreset could change this}
  1723.  
  1724.   quotechar := '>';
  1725.  
  1726.   if dotsonreset then
  1727.     begin
  1728.       xgotoxy(1,1);
  1729.       xwrites('...');
  1730.     end;
  1731.  
  1732.   if not fexists(artfn) then
  1733.     begin
  1734.       artfrom := '(expired)';
  1735.  
  1736.       artsubject := '(expired)';
  1737.       artmessageid := '(expired)';
  1738.       artnewsgroups := '(expired)';
  1739.     end
  1740.   else
  1741.     begin
  1742.  
  1743. {don't get from articles[]^.from - need full name _and_ address for kill file}
  1744.       artfrom := getheaderline(artfn,'from:');
  1745.  
  1746.       artsubject := getheaderline(artfn,'subject:');
  1747.       artmessageid := getheaderline(artfn,'message-id:');
  1748.       artnewsgroups := getheaderline(artfn,'newsgroups:');
  1749.  
  1750. {$ifdef charset}
  1751.       if uselocalcharset then
  1752.         setreadencoding(
  1753.          getheaderline(artfn,'content-type:'),
  1754.          getheaderline(artfn,'content-transfer-encoding:'));
  1755. {$endif}
  1756.  
  1757.       if findquotechar then
  1758.         quotechar := bestquotechar;
  1759.     end;
  1760.   
  1761.   if dotsonreset then
  1762.     begin
  1763.       xgotoxy(1,1);
  1764.       xwrites('   ');
  1765.       xgotoxy(1,1);
  1766.     end;
  1767.  
  1768.   firstemptyline := maxint;  {do it here, since bestquotechar changed it}
  1769.  
  1770.   browserefresh;  {does an artreset itself}
  1771.  
  1772.   while not donebrowse do
  1773.     begin
  1774.  
  1775. {if we've already read off the end of the article, it's time for a new one}
  1776.       if arteof and quitatarteof then
  1777.         donebrowse := true
  1778.       else
  1779.         begin
  1780.  
  1781. {otherwise indicate all has been seen and get a key}
  1782.  
  1783.           quitatarteof := true;
  1784.           geteopkey;
  1785.         end;
  1786.     end;
  1787.  
  1788.   artclose;
  1789. end;
  1790.  
  1791. procedure viewarts(lowest,highest: integer; updatehighestread: boolean);
  1792.  
  1793. var
  1794.   numleft: integer;
  1795.   whichart: integer;
  1796.   willupdatej: boolean;
  1797.   currsubj: subjstringt;
  1798.   foundselected: boolean;
  1799.  
  1800. begin
  1801.   willupdatej := updatehighestread;
  1802.  
  1803.   currart := lowest;
  1804.   donegroup := false;
  1805.   browsedir := 1;
  1806.   browseonlysel := true;
  1807.   while not moreselecting and not donegroup do
  1808.     begin
  1809.  
  1810.       if skipsection then
  1811.         begin
  1812.  
  1813.           currsubj := articles[currart]^.basesubject;
  1814.           while
  1815.            (currart<=highest)
  1816.            and
  1817.            subjseq(currsubj,articles[currart]^.basesubject) do
  1818.             inc(currart);
  1819.  
  1820. {short-circuit}
  1821.  
  1822.            foundselected := false;
  1823.            while not foundselected do
  1824.              begin
  1825.                if currart>highest then
  1826.                  foundselected := true
  1827.                else if selected[currart] then
  1828.                  foundselected := true
  1829.                else
  1830.                  inc(currart);
  1831.              end;
  1832.         end
  1833.  
  1834.       else
  1835.  
  1836.         if browseonlysel then
  1837.           while (currart>=lowest) and (currart<=highest) and
  1838.            not selected[currart] do
  1839.             inc(currart,browsedir);
  1840.  
  1841.  
  1842.       skipsection := false;
  1843.       browseonlysel := true;
  1844.  
  1845.       if currart>highest then
  1846.         donegroup := true;
  1847.       if currart<lowest then
  1848.         browsedir := 1;
  1849.       if (currart>=lowest) and (currart<=highest) then
  1850.         begin
  1851.           browsedir := 1;
  1852.  
  1853. {using k/b/a can mess this up any nice and simple way, so do it this way}
  1854. {will still show LAST when you're past the last selected one - so what}
  1855.  
  1856.           numleft := 0;
  1857.           for whichart := currart+1 to highest do
  1858.             if selected[whichart] then
  1859.               inc(numleft);
  1860.  
  1861.           browseart(currart,numleft,willupdatej);
  1862.           if (atol(articles[currart]^.filename)>highestread) and
  1863.            willupdatej then
  1864.             highestread := atol(articles[currart]^.filename);
  1865.         end;
  1866.  
  1867. { handle case of going to non-selected-also articles on extremes (first/last) }
  1868.  
  1869. { allow `n' etc. to indicate finished reading, but not `a',`p',`b' }
  1870.  
  1871.       if ((browsedir>0) or (currart>lowest)) and
  1872.        (browseonlysel or (browsedir<0) or (currart<highest)) and
  1873.        not skipsection then
  1874.         inc(currart,browsedir);
  1875.  
  1876.     end;
  1877.  
  1878. {willupdatej can change to false part way through}
  1879.  
  1880.   if not willupdatej then
  1881.     highestread := 0;
  1882.  
  1883. end;
  1884.  
  1885. procedure selectandbrowse;
  1886.  
  1887. var
  1888.   whichart: integer;
  1889.   donepagesel: boolean;
  1890.   donegroupsel: boolean;
  1891.   selsubjs: array[1..maxlpp] of string;
  1892.   inkey,lastinkey: char;
  1893.   lastselected: boolean;
  1894.   highestlegalsellet: char;
  1895.   highestlegalseldig: char;
  1896.   currsourcedesc: string;
  1897.   selheaderline: string;
  1898.  
  1899. function isselchar(ch: char): boolean;
  1900.  
  1901. begin
  1902.   isselchar :=
  1903.    ((ch<=highestlegalsellet) and (islower(ch))) or
  1904.    ((ch<=highestlegalseldig) and (isdigit(ch)));
  1905. end;
  1906.  
  1907. function selcharnum(ch: char): integer;
  1908.  
  1909. begin
  1910.   if isdigit(ch) then
  1911.     selcharnum := ord(ch)-ord('0')+1+26
  1912.   else
  1913.     selcharnum := ord(ch)-ord('a')+1;
  1914. end;
  1915.  
  1916. function whichselchar(onetothirtysix: integer): char;
  1917.  
  1918. begin
  1919.   if onetothirtysix<=26 then
  1920.     whichselchar := chr(ord('a')+(onetothirtysix-1))
  1921.   else
  1922.     whichselchar := chr(ord('0')+(onetothirtysix-1)-26);
  1923. end;
  1924.  
  1925. procedure writetotalselln(lineno: integer; c: char);
  1926.  
  1927. var
  1928.   subjwidth: integer;
  1929.   ycoord: integer;
  1930.   printsubj: string;
  1931.   countindents: integer;
  1932.   stringedsize: string;
  1933.   lensofar: integer;
  1934.   showndate: string;
  1935.  
  1936.   nondatecolor: byte;
  1937.  
  1938. begin
  1939.   lensofar := 0;
  1940.  
  1941.   showndate := dateformat;
  1942.   if showndate='-' then
  1943.     showndate := '';
  1944.   if showndate<>'' then
  1945.     showndate := showndate+' ';
  1946.  
  1947.   subjwidth := max(1,cols-1-(4+fromwidth+sizewidth+1+length(showndate)));
  1948.  
  1949.   if selected[lineno] then
  1950.     nondatecolor := highcolor
  1951.   else if odd(lineno) then
  1952.     nondatecolor := lowcolor
  1953.   else
  1954.     nondatecolor := alternatecolor;
  1955.  
  1956.   xsetcolor(nondatecolor);
  1957.  
  1958.   ycoord := lineno-topline+1+selheaderlines+1;
  1959.  
  1960. {other code keeps lineno-topline<36}
  1961.  
  1962.   writexy(1,ycoord,whichselchar(lineno-topline+1));
  1963.   inc(lensofar);
  1964.  
  1965.   xwrites(c);
  1966.   inc(lensofar);
  1967.  
  1968.   if layout=layoutnormal then
  1969.     begin
  1970.       xwrites(nonastychar(copy(articles[lineno]^.from,1,fromwidth)));
  1971.       inc(lensofar,fromwidth);
  1972. {fromwidth is always>0}
  1973.       xwrites(' ');
  1974.       inc(lensofar);
  1975.     end;
  1976.  
  1977. {$ifdef testsort}
  1978.   if showdebug('sort') then
  1979.     writexy(3,ycoord,articles[lineno]^.filename+' ');
  1980. {$endif}
  1981.  
  1982.   stringedsize := itoa(articles[lineno]^.sizeink);
  1983.   if articles[lineno]^.sizeink=255 then
  1984.     stringedsize := 'huge';
  1985.  
  1986. {length is guaranteed to be 4 or less}
  1987.   if length(stringedsize)>sizewidth then
  1988.     begin
  1989.       if sizewidth<1 then
  1990.         stringedsize := ''
  1991.       else if sizewidth=1 then
  1992.         stringedsize := '!'
  1993.       else if sizewidth=2 then
  1994.         stringedsize := 'lg'
  1995.       else if sizewidth=3 then
  1996.         stringedsize := 'big';
  1997.     end;
  1998.  
  1999.   xgotoxy(1+lensofar,ycoord);
  2000.   xwritesw(stringedsize,sizewidth);
  2001.   inc(lensofar,sizewidth);
  2002.  
  2003.   xwrites(' ');
  2004.   inc(lensofar);
  2005.  
  2006.   if (layout=layoutnormal) and (showndate<>'') then
  2007.     begin
  2008.       showndate :=
  2009.        dateformatted(articles[lineno]^.date div 16384,
  2010.        (articles[lineno]^.date mod 16384) div 1024,
  2011.        (articles[lineno]^.date mod 1024) div 32,
  2012.        dateformat);
  2013.  
  2014.       showndate := showndate+' ';
  2015.  
  2016.       if datecolor<>255 then
  2017.         xsetcolor(datecolor);
  2018.  
  2019.       xwrites(showndate);
  2020.       inc(lensofar,length(showndate));
  2021.  
  2022.       if datecolor<>255 then
  2023.         xsetcolor(nondatecolor);
  2024.  
  2025.     end;
  2026.  
  2027.   printsubj := '';
  2028.   for countindents := 1 to (articles[lineno]^.indents and $f) do
  2029.     printsubj := printsubj+'>';
  2030.  
  2031.   printsubj := printsubj+selsubjs[lineno-topline+1];
  2032.   printsubj := nonastychar(printsubj);
  2033.  
  2034.   xgotoxy(1+lensofar,ycoord);
  2035.  
  2036.   if printsubj='' then
  2037.     xwrites('-')
  2038.   else if layout=layoutnormal then
  2039.     xwrites(copy(printsubj,1,subjwidth))
  2040.   else
  2041.     xwrites(copy(printsubj,1,max(subjwidth,cols-lensofar-2)));
  2042.  
  2043.   if nondatecolor<>lowcolor then
  2044.     xsetcolor(lowcolor);
  2045.  
  2046.   xgotoxy(1,sellpp+selheaderlines+4);
  2047. end;
  2048.  
  2049. procedure clearstar;
  2050.  
  2051. begin
  2052.   if starbeside<>0 then
  2053.     begin
  2054.       writexy(2,starbeside-topline+1+selheaderlines+1,' ');
  2055.       starbeside := 0;
  2056.     end;
  2057. end;
  2058.  
  2059. procedure writeselln(lineno: integer);
  2060.  
  2061. begin
  2062.   clearstar;
  2063.   writetotalselln(lineno,' ');
  2064. end;
  2065.  
  2066. procedure writesellnstar(lineno: integer);
  2067.  
  2068. begin
  2069.   clearstar;
  2070.   writetotalselln(lineno,'*');
  2071.   starbeside := lineno;
  2072. end;
  2073.  
  2074. procedure setupselheaderline;
  2075.  
  2076. begin
  2077.   selheaderline := '';
  2078.  
  2079. {$ifdef old}
  2080.   if not hasmouse then
  2081.     xwritessis(currsource,' Articles: ',numarts,'                      ')
  2082.   else
  2083.     begin
  2084.       xwritess(currsource,' ');
  2085.       xwrites(mousecharsheader);
  2086.       xwrites('  Articles: ');
  2087.       xwritei(numarts);
  2088.       xclreol;
  2089.     end;
  2090. {$endif}
  2091.  
  2092.   if not hasmouse then
  2093.     selheaderline := currsource
  2094.   else
  2095.     selheaderline := currsource+' '+mousecharsheader+'  ';
  2096.  
  2097.   selheaderline := selheaderline+' Articles: '+ltoa(numarts);
  2098.  
  2099.   if length(selheaderline)<cols-6 then
  2100.     begin
  2101.       selheaderline := selheaderline+'  ';
  2102.       while length(selheaderline)+length(currsourcedesc)<cols-4 do
  2103.         selheaderline := selheaderline+' ';
  2104.       selheaderline := selheaderline+
  2105.        copy(currsourcedesc,1,cols-length(selheaderline)-1);
  2106.     end;
  2107.  
  2108. end;
  2109.  
  2110. procedure selrefresh;
  2111.  
  2112. var
  2113.   whichart: integer;
  2114.   prevsubj: subjstringt;
  2115.  
  2116. begin
  2117.   prevsubj := '';
  2118.   xclrscr;
  2119.  
  2120.   xgotoxy(1,1);
  2121.  
  2122.   xwrites(selheaderline);
  2123.   xclreol;
  2124.  
  2125.   botline := topline-1;
  2126.  
  2127.   for whichart := topline to min(topline+sellpp-1,numarts) do
  2128.     begin
  2129.       if subjseq(articles[whichart]^.basesubject,prevsubj) then
  2130.         selsubjs[whichart-topline+1] := ''
  2131.       else
  2132.         selsubjs[whichart-topline+1] := articles[whichart]^.basesubject;
  2133.  
  2134.       prevsubj := articles[whichart]^.basesubject;
  2135.       writeselln(whichart);
  2136.       botline := whichart;
  2137.     end;
  2138.  
  2139.   selrefreshbotline;
  2140.  
  2141. {there will always be at least one letter, but not always any digits}
  2142.  
  2143.   highestlegalsellet := 'z';
  2144.   highestlegalseldig := chr(ord('0')-1);  {i.e., none are legal}
  2145.  
  2146.   if botline-topline<26 then
  2147.     highestlegalsellet := whichselchar(botline-topline+1)
  2148.   else
  2149.     highestlegalseldig := whichselchar(botline-topline+1);
  2150. end;
  2151.  
  2152. procedure togglekey(inkey: char);
  2153.  
  2154. var
  2155.   artnum: integer;
  2156.  
  2157. begin
  2158.   artnum := topline+selcharnum(inkey)-1;
  2159.   if artnum<=botline then
  2160.     begin
  2161.       setselected(artnum,not selected[artnum]);
  2162.       lastselected := selected[artnum];
  2163.       if hasmouse and selected[artnum] then
  2164.         writesellnstar(artnum)
  2165.       else
  2166.         writeselln(artnum)
  2167.     end;
  2168.  
  2169.   if not quiet then
  2170.     selrefreshbotline;
  2171. end;
  2172.  
  2173. procedure selreversepage;
  2174.  
  2175. var
  2176.   artnum: integer;
  2177.  
  2178. begin
  2179.   for artnum := topline to botline do
  2180.     begin
  2181.       setselected(artnum,not selected[artnum]);
  2182.       writeselln(artnum);
  2183.     end;
  2184.  
  2185.   if not quiet then
  2186.     selrefreshbotline;
  2187. end;
  2188.  
  2189. procedure selselectornotall(selected: boolean);  {caller must refresh}
  2190.  
  2191. var
  2192.   artnum: integer;
  2193.  
  2194. begin
  2195.   for artnum := 1 to numarts do
  2196.     setselected(artnum,selected);
  2197. end;
  2198.  
  2199. procedure selselectall;  {caller must refresh}
  2200.  
  2201. begin
  2202.   selselectornotall(true);
  2203. end;
  2204.  
  2205. procedure selclearall;  {caller must refresh}
  2206.  
  2207. begin
  2208.   selselectornotall(false);
  2209. end;
  2210.  
  2211. procedure dostar(inkey: char);
  2212.  
  2213. var
  2214.   artnum: integer;
  2215.   currsubj: subjstringt;
  2216.  
  2217. begin
  2218.   artnum := topline+selcharnum(inkey)-1;
  2219.   currsubj := articles[artnum]^.basesubject;
  2220.  
  2221.   while
  2222.    (artnum<=numarts)
  2223.    and
  2224.    subjseq(currsubj,articles[artnum]^.basesubject) do
  2225.     begin
  2226.       if selected[artnum]<>lastselected then
  2227.         begin
  2228.           setselected(artnum,lastselected);
  2229.           if artnum<=botline then
  2230.             writeselln(artnum);
  2231.         end;
  2232.       inc(artnum);
  2233.     end;
  2234.  
  2235.   if not quiet then
  2236.     selrefreshbotline;
  2237. end;
  2238.  
  2239. procedure dodash(inkey: char);
  2240.  
  2241. var
  2242.   inkeyint: integer;
  2243.   artnum: integer;
  2244.   newkey: char;
  2245.   newkeyint: integer;
  2246.  
  2247. begin
  2248.   inkeyint := selcharnum(inkey)-1;
  2249.   xclreolxy(1,lpp);
  2250.   xwritess(inkey,'-');
  2251.  
  2252.   newkey := xreadkeyextended(0,0,
  2253.    2+selheaderlines,(botline-topline)+2+selheaderlines);
  2254.   newkey := selmap[newkey];
  2255.  
  2256.   if isselchar(newkey) then
  2257.     begin
  2258.       newkeyint := selcharnum(newkey)-1;
  2259.       if (newkeyint<botline-topline+1) and (newkeyint>=inkeyint) then
  2260.         for artnum := topline+inkeyint+1 to topline+newkeyint do {+1=togl once}
  2261.           begin
  2262.             if selected[artnum]<>lastselected then
  2263.               begin
  2264.                 setselected(artnum,lastselected);
  2265.                 writeselln(artnum);
  2266.               end;
  2267.           end;
  2268.     end;
  2269.   xclreolxy(1,lpp);
  2270.  
  2271.   if not quiet then
  2272.     selrefreshbotline;
  2273. end;
  2274.  
  2275. procedure backpage;
  2276.  
  2277. begin
  2278.   if (topline<>1) or (botline<>numarts) then
  2279.     begin
  2280.       if topline=1 then
  2281.         topline := numarts-((numarts-1) mod sellpp)
  2282.       else
  2283.         dec(topline,sellpp);
  2284.       selrefresh;
  2285.     end;
  2286. end;
  2287.  
  2288. procedure forwardpage;
  2289.  
  2290. begin
  2291.   if (topline<>1) or (botline<>numarts) then
  2292.     begin
  2293.       if botline=numarts then
  2294.         topline := 1
  2295.       else
  2296.         inc(topline,sellpp);
  2297.       selrefresh;
  2298.     end;
  2299. end;
  2300.  
  2301. procedure firstpage;
  2302.  
  2303. begin
  2304.   if topline<>1 then
  2305.     begin
  2306.       topline := 1;
  2307.       selrefresh;
  2308.     end;
  2309. end;
  2310.  
  2311. procedure lastpage;
  2312.  
  2313. begin
  2314.   if botline<>numarts then
  2315.     begin
  2316.       topline := numarts-((numarts-1) mod sellpp);
  2317.       selrefresh;
  2318.     end;
  2319. end;
  2320.  
  2321. procedure browseandreturn;
  2322.  
  2323. var
  2324.   anyselected: boolean;
  2325.   currart: integer;
  2326.  
  2327. begin
  2328.   anyselected := false;
  2329.   for currart := topline to botline do
  2330.     if selected[currart] then
  2331.       anyselected := true;
  2332.  
  2333.   if anyselected then
  2334.     begin
  2335.       viewarts(topline,botline,false);
  2336.  
  2337. { if hit `=', don't reset selected[] }
  2338.       if not moreselecting then
  2339.         for currart := topline to botline do
  2340.           if selected[currart] then
  2341.             setselected(currart,false);
  2342.  
  2343.       if currsource<>'' then
  2344.         selrefresh;
  2345.     end;
  2346.  
  2347.   moreselecting := false;
  2348. end;
  2349.  
  2350. procedure savewritehighlightedarts(fullheaders: boolean);
  2351.  
  2352. var
  2353.   thisallforgetit: char;
  2354.   lowart, highart: integer;
  2355.   anyselected: boolean;
  2356.   currart: integer;
  2357.   outfilen: string;
  2358.   outfile: text;
  2359.   outfileisopen: boolean;
  2360.   illegal: boolean;
  2361.   doit: boolean;
  2362.   appendoverwriteforgetit: char;
  2363.   infn: string;
  2364.   inf: text;
  2365.   firstart: boolean;
  2366.   s: string;
  2367.  
  2368. {$ifdef charset}
  2369.   yn: char;
  2370.   foundblank: boolean;
  2371.   saveusinglocal: boolean;
  2372. {$endif}
  2373.  
  2374. begin
  2375.   thisallforgetit := 'a';
  2376.  
  2377. {don't ask unless there's more than one page}
  2378.   if (topline<>1) or (botline<>numarts) then
  2379.     begin
  2380.       if fullheaders then
  2381.         thisallforgetit := onekeydef(
  2382.          'Save: {t}his page, {a}ll pages, {f}orget it','taf','f')
  2383.       else
  2384.         thisallforgetit := onekeydef(
  2385.          'Write: {t}his page, {a}ll pages, {f}orget it','taf','f');
  2386.     end;
  2387.  
  2388.   if thisallforgetit='t' then
  2389.     begin
  2390.       lowart := topline;
  2391.       highart := botline;
  2392.     end
  2393.   else
  2394.     begin
  2395.       lowart := 1;
  2396.       highart := numarts;
  2397.     end;
  2398.  
  2399.   anyselected := false;
  2400.   for currart := lowart to highart do
  2401.     if selected[currart] then
  2402.       anyselected := true;
  2403.  
  2404.   if not anyselected and (thisallforgetit<>'f') then
  2405.     begin
  2406.       warn('none selected');
  2407.       thisallforgetit := 'f';
  2408.     end;
  2409.  
  2410.   if thisallforgetit<>'f' then
  2411.     begin
  2412.       getfilename(outfilen,'file name (blank to abort):',lastfilen);
  2413.  
  2414.       outfilen := ltrim(trim(outfilen));
  2415.  
  2416.       if outfilen<>'' then
  2417.         lastfilen := outfilen;
  2418.  
  2419.       if tildehome then
  2420.         if copy(outfilen,1,2)='~/' then
  2421.           outfilen := home+copy(outfilen,2,255);
  2422.  
  2423.       outfilen := unslash(outfilen);
  2424.  
  2425.       illegal := illegalfn(outfilen);
  2426.       doit := (outfilen<>'');
  2427.  
  2428.       if doit and not trusted then
  2429.         begin
  2430.           illegal := suspiciousfn(outfilen);
  2431.         end;
  2432.  
  2433.       if doit and illegal then
  2434.         begin
  2435.           warn('unable to use that filename');
  2436.         end;
  2437.  
  2438.       if doit and not illegal then
  2439.         begin
  2440.           if not trusted then
  2441.             outfilen := withbackslash(home)+outfilen;
  2442.  
  2443.           appendoverwriteforgetit := 'o';
  2444.  
  2445.           if fexists(outfilen) then
  2446.             begin
  2447.               xclreolxy(1,lpp);
  2448.               appendoverwriteforgetit :=
  2449.                onekeydef('{O}verwrite {a}ppend {f}orget it','Oaf','f');
  2450.             end;
  2451.  
  2452.           if appendoverwriteforgetit<>'f' then
  2453.             begin
  2454.  
  2455.  
  2456. {$ifdef charset}
  2457.               saveusinglocal := false;
  2458.               if uselocalcharset then
  2459.                 begin
  2460.                   yn := onekey('Use local charset {y}/{n}','yn');
  2461.                   saveusinglocal := (yn = 'y');
  2462.                 end;
  2463. {$endif}
  2464.  
  2465.               outfileisopen := false;
  2466.  
  2467.               assign(outfile,outfilen);
  2468.  
  2469.               if appendoverwriteforgetit='a' then
  2470.                 begin
  2471. {$I-}
  2472.                   append(outfile);
  2473. {$I+}
  2474.                   if ioresult<>0 then
  2475.                     begin
  2476.                       warn('could not append to '+outfilen);
  2477.                     end
  2478.                   else
  2479.                     begin
  2480.                       outfileisopen := true;
  2481.                       writeln(outfile);
  2482.                       writeln(outfile,outputseparator);
  2483.                       writeln(outfile);
  2484.                     end;
  2485.                 end
  2486.               else
  2487.                 begin
  2488. {$I-}
  2489.                   rewrite(outfile);
  2490. {$I+}
  2491.                   if ioresult<>0 then
  2492.                     begin
  2493.                       warn('could not write to '+outfilen);
  2494.                     end
  2495.                   else
  2496.                     begin
  2497.                       outfileisopen := true;
  2498.                     end;
  2499.                 end;
  2500.  
  2501.               firstart := true;
  2502.  
  2503. {the outfileisopen test is inside the loop since it was added later, and}
  2504. {this is the least painful formatting-wise way to do it...}
  2505.  
  2506.               for currart := lowart to highart do
  2507.                 if outfileisopen and selected[currart] then
  2508.                   begin
  2509.                     xclreolxy(1,lpp);
  2510.                     xwritesss('Working on ',articles[currart]^.filename,'...');
  2511.  
  2512.                     infn := withbackslash(currdir)+articles[currart]^.filename;
  2513.  
  2514. {$ifdef charset}
  2515.                     if (uselocalcharset and saveusinglocal) then
  2516.                       setreadencoding(
  2517.                        getheaderline(infn,'content-type:'),
  2518.                        getheaderline(infn,'content-transfer-encoding:'));
  2519. {$endif}
  2520.  
  2521.                     safereset(inf,infn);
  2522.  
  2523.                     if fileresult<>0 then
  2524.                       warn('could not open '+infn)
  2525.                     else
  2526.                       begin
  2527.  
  2528.                         if not firstart then
  2529.                           begin
  2530.                             writeln(outfile);
  2531.                             writeln(outfile,outputseparator);
  2532.                             writeln(outfile);
  2533.                           end;
  2534.                         firstart := false;
  2535.  
  2536. {$ifdef charset}
  2537.                         foundblank:= false;
  2538. {$endif}
  2539.  
  2540.                         while not eof(inf) do
  2541.                           begin
  2542.  
  2543.               {need to check fullheaders here!}
  2544.  
  2545. {}{}{}   {currently, this chops lines at 255 characters!  would need:}
  2546. {}{}{}   {while not eoln read(inf,s); write(outf,s); readln(inf);writeln(outf);}
  2547.  
  2548.                             readln(inf,s);
  2549.  
  2550. {$ifdef charset}
  2551.                             if foundblank and saveusinglocal then
  2552.                               linetolocal(s)
  2553.                             else
  2554.                               if s='' then
  2555.                                 foundblank := true;
  2556. {$endif}
  2557.  
  2558.                             writeln(outfile,s);
  2559.                           end;
  2560.                         close(inf);
  2561.                       end;
  2562.                 end;
  2563.  
  2564.               if outfileisopen then
  2565.                 close(outfile);
  2566.  
  2567.             end;  {appendoverwriteforgetit}
  2568.  
  2569.         end;  {doit and not illegal}
  2570.  
  2571.     end;  {thisallforgetit}
  2572.  
  2573.   selrefresh;
  2574.  
  2575. end;
  2576.  
  2577. procedure writehighlightedarts;
  2578.  
  2579. begin
  2580.   savewritehighlightedarts(nofullheaders);
  2581. end;
  2582.  
  2583. procedure savehighlightedarts;
  2584.  
  2585. begin
  2586.   savewritehighlightedarts(yesfullheaders);
  2587. end;
  2588.  
  2589. procedure copyhighlightedarts;
  2590.  
  2591. var
  2592.   folder: string;
  2593.   folderdir: string;
  2594.  
  2595.   thisallforgetit: char;
  2596.   lowart, highart: integer;
  2597.   anyselected: boolean;
  2598.   currart: integer;
  2599.  
  2600.   infn: string;
  2601.  
  2602. begin
  2603.   thisallforgetit := 'a';
  2604.  
  2605. {don't ask unless there's more than one page}
  2606.   if (topline<>1) or (botline<>numarts) then
  2607.     thisallforgetit := onekeydef(
  2608.      'Copy: {t}his page, {a}ll pages, {f}orget it','taf','f');
  2609.  
  2610.   if thisallforgetit='t' then
  2611.     begin
  2612.       lowart := topline;
  2613.       highart := botline;
  2614.     end
  2615.   else
  2616.     begin
  2617.       lowart := 1;
  2618.       highart := numarts;
  2619.     end;
  2620.  
  2621.   anyselected := false;
  2622.   for currart := lowart to highart do
  2623.     if selected[currart] then
  2624.       anyselected := true;
  2625.  
  2626.   if not anyselected and (thisallforgetit<>'f') then
  2627.     begin
  2628.       warn('none selected');
  2629.       thisallforgetit := 'f';
  2630.     end;
  2631.  
  2632.   if thisallforgetit<>'f' then
  2633.     begin
  2634.       if not quiet then
  2635.         begin
  2636.           xclreolxy(1,lpp-3);
  2637.           xclreolxy(1,lpp-2);
  2638.           xwritelns('Enter a folder (e.g., `=misc'') as a destination for');
  2639.           xclreolxy(1,lpp-1);
  2640.           xwritelns('these articles, or leave blank to abort');
  2641.         end;
  2642.  
  2643.       folder := lastfolder;
  2644.  
  2645.       xclreolxy(1,lpp);
  2646.       xwrites('Copy to Folder: ');
  2647.       xreadlnse(folder,max(cols-20,70),yespreserve,endkeyswithspace);
  2648.       xclreolxy(1,lpp);
  2649.  
  2650.       if folder<>'' then
  2651.         if (numoccur('\',unslash(folder))<>0) or
  2652.          (numoccur(':',folder)<>0) or (pos('..',folder)<>0) then
  2653.           warn('illegal folder name: '+folder)
  2654.         else
  2655.           begin
  2656.             if folder[1]<>'=' then
  2657.               folder := '='+folder;
  2658.  
  2659.             lastfolder := folder;
  2660.  
  2661.             xwritesss('Copying to ',folder,'...');
  2662.  
  2663.             unfoldergroup(folder);
  2664.  
  2665.             if not joinedtoexactgroup(folder) then
  2666.               begin
  2667.                 xclreolxy(1,lpp-1);
  2668.                 xclreolxy(1,lpp);
  2669.                 xwritesss('Not joined to ',folder,', doing so now...');
  2670.                 addnewmailgroup(folder);
  2671.               end;
  2672.  
  2673.             folderdir := getgroupdir(folder);
  2674.             mkhier(folderdir);
  2675.           
  2676.             for currart := lowart to highart do
  2677.               if selected[currart] then
  2678.                 begin
  2679.                   xclreolxy(1,lpp);
  2680.                   xwritesss('Working on ',articles[currart]^.filename,'...');
  2681.  
  2682.                   infn := withbackslash(currdir)+articles[currart]^.filename;
  2683.  
  2684.                   copyfile(infn,getuniqfile(folderdir));
  2685.                 end;
  2686.  
  2687.           end;  {legal folder}
  2688.  
  2689.     end;  {thisallforgetit}
  2690.  
  2691.   selrefresh;
  2692.  
  2693. end;
  2694.  
  2695. procedure movehighlightedarts;
  2696.  
  2697. var
  2698.   folder: string;
  2699.   folderdir: string;
  2700.  
  2701.   thisallforgetit: char;
  2702.   lowart, highart: integer;
  2703.   anyselected: boolean;
  2704.   currart: integer;
  2705.  
  2706.   infn: string;
  2707.  
  2708. begin
  2709.   thisallforgetit := 'a';
  2710.  
  2711.   if not ismailgroup(currsource) then
  2712.     begin
  2713.       warn('not a mail group -- using Copy instead');
  2714.       copyhighlightedarts;
  2715.       thisallforgetit := 'f';
  2716.     end
  2717.   else
  2718.     begin
  2719.  
  2720. {don't ask unless there's more than one page}
  2721.       if (topline<>1) or (botline<>numarts) then
  2722.         thisallforgetit := onekeydef(
  2723.          'Move: {t}his page, {a}ll pages, {f}orget it','taf','f');
  2724.  
  2725.       if thisallforgetit='t' then
  2726.         begin
  2727.           lowart := topline;
  2728.           highart := botline;
  2729.         end
  2730.       else
  2731.         begin
  2732.           lowart := 1;
  2733.           highart := numarts;
  2734.         end;
  2735.     end;
  2736.  
  2737.   anyselected := false;
  2738.   for currart := lowart to highart do
  2739.     if selected[currart] then
  2740.       anyselected := true;
  2741.  
  2742.   if not anyselected and (thisallforgetit<>'f') then
  2743.     begin
  2744.       warn('none selected');
  2745.       thisallforgetit := 'f';
  2746.     end;
  2747.  
  2748.   if thisallforgetit<>'f' then
  2749.     begin
  2750.       if not quiet then
  2751.         begin
  2752.           xclreolxy(1,lpp-3);
  2753.           xclreolxy(1,lpp-2);
  2754.           xwritelns('Enter a folder (e.g., `=misc'') as a destination for');
  2755.           xclreolxy(1,lpp-1);
  2756.           xwritelns('these articles, or leave blank to abort');
  2757.         end;
  2758.  
  2759.       folder := lastfolder;
  2760.  
  2761.       xclreolxy(1,lpp);
  2762.       xwrites('Move to Folder: ');
  2763.       xreadlnse(folder,max(cols-20,70),yespreserve,endkeyswithspace);
  2764.       xclreolxy(1,lpp);
  2765.  
  2766.       if folder<>'' then
  2767.         if (numoccur('\',unslash(folder))<>0) or
  2768.          (numoccur(':',folder)<>0) or (pos('..',folder)<>0) then
  2769.           warn('illegal folder name: '+folder)
  2770.         else
  2771.           begin
  2772.             if folder[1]<>'=' then
  2773.               folder := '='+folder;
  2774.  
  2775.             lastfolder := folder;
  2776.  
  2777.             xwritesss('Moving to ',folder,'...');
  2778.  
  2779.             unfoldergroup(folder);
  2780.  
  2781.             if not joinedtoexactgroup(folder) then
  2782.               addnewmailgroup(folder);
  2783.  
  2784.             folderdir := getgroupdir(folder);
  2785.             mkhier(folderdir);
  2786.           
  2787.             for currart := lowart to highart do
  2788.               if selected[currart] then
  2789.                 begin
  2790.                   xclreolxy(1,lpp);
  2791.                   xwritesss('Working on ',articles[currart]^.filename,'...');
  2792.  
  2793.                   infn := withbackslash(currdir)+articles[currart]^.filename;
  2794.  
  2795.                   copyfilethenempty(infn,getuniqfile(folderdir));
  2796.                 end;
  2797.  
  2798.           end;  {legal folder}
  2799.  
  2800.     end;  {thisallforgetit}
  2801.  
  2802.   selrefresh;
  2803.  
  2804. end;
  2805.  
  2806. procedure selnextnoupdate;
  2807.  
  2808. begin
  2809.   donegroupsel := true;
  2810.   highestread := 0;
  2811. end;
  2812.  
  2813. procedure selquitnoupdate;
  2814.  
  2815. var
  2816.   doit: boolean;
  2817.   whichart: integer;
  2818.  
  2819. begin
  2820.   doit := true;
  2821.   if confirmquit then
  2822.     doit := (onekey('are you SURE you want to quit?  {y}/{n}','yn')='y');
  2823.  
  2824.   if not doit then
  2825.     selrefresh
  2826.   else
  2827.     begin
  2828.       needtofindnextgroup := false;
  2829.       currsource := '';
  2830.       selclearall;
  2831.       selnextnoupdate;
  2832.     end;
  2833. end;
  2834.  
  2835. procedure unsubscribe;
  2836.  
  2837. var
  2838.   doit: boolean;
  2839.   nextgroup: string;
  2840.  
  2841. begin
  2842.   doit :=
  2843.    (onekey('are you SURE you want to unsubscribe?  {y}/{n}','yn')='y');
  2844.  
  2845.   if not doit then
  2846.     selrefresh
  2847.   else
  2848.     begin
  2849.       nextgroup := getnextgroup;  {empty string is ok, too}
  2850.       xclreolxy(1,lpp);
  2851.       updatejoinunsubscribe;
  2852.       selclearall;
  2853.       currsource := nextgroup;
  2854.       needtofindnextgroup := false;
  2855.       selnextnoupdate;
  2856.     end;
  2857. end;
  2858.  
  2859. procedure gotonewsource;
  2860.  
  2861. var
  2862.   trysource: string;
  2863.   trysourcekind: sourcetype;
  2864.  
  2865. begin
  2866.   trysource := '';
  2867.   pickasource(trysource,trysourcekind);
  2868.   xclreolxy(1,lpp);
  2869.  
  2870.   if trysource='' then
  2871.     begin
  2872.       selrefresh;
  2873.     end
  2874.   else
  2875.     begin
  2876.       selclearall;
  2877.       currsource := trysource;
  2878.       currsourcekind := trysourcekind;
  2879.       needtofindnextgroup := false;
  2880.       selnextnoupdate;
  2881.     end;
  2882. end;
  2883.  
  2884. procedure showgrouplist;
  2885.  
  2886. var
  2887.   done: boolean;
  2888.   quitout: boolean;
  2889.   whichgroupi: integer;
  2890.   ypos: integer;
  2891.   maxypos: integer;
  2892.  
  2893.   toplineused: integer;
  2894.   bottomlineused: integer;
  2895.  
  2896.   agroup: string;
  2897.   unreadcount: articlefilenametype;
  2898.   unreadstring: string;
  2899.  
  2900.   tempstr: string;
  2901.  
  2902.   wastec: char;
  2903.   inkey: char;
  2904.  
  2905.   groupwidth: integer;
  2906.   unreadwidth: integer;
  2907.   descwidth: integer;
  2908.  
  2909. { 36 is max 26 letters+max 10 digits }
  2910.   showngroupindices: array[1..36] of integer;
  2911.  
  2912.   didgoto: boolean;
  2913.  
  2914.   procedure showgroupnewscreen;
  2915.  
  2916.   var
  2917.     tempint: integer;
  2918.  
  2919.   begin {showgroupnewscreen}
  2920.     xclrscr;
  2921.     xgotoxy(1,1);
  2922.  
  2923. { gets overwritten _immediately_, before disk i/o }
  2924. {
  2925.     xwrites('Reading...');
  2926. }
  2927.  
  2928.     ypos := 0;
  2929.  
  2930.     toplineused := ypos+2;  {clearly too large}
  2931.     bottomlineused := ypos;
  2932.  
  2933.     for tempint := 1 to 36 do
  2934.       showngroupindices[tempint] := -1;
  2935.  
  2936.   end; {showgroupnewscreen}
  2937.  
  2938. begin
  2939.   maxypos := min(lpp-3,36);
  2940.   didgoto := false;
  2941.  
  2942.   groupwidth := max(cols div 2-10,10);
  2943.   unreadwidth := 4;
  2944.  
  2945. {
  2946. cols-1 to prevent screen overflow
  2947. 2 for selection letter/digit and space
  2948. groupwidth+1 for group name and space
  2949. unreadwidth+1 for unread count and space
  2950. }
  2951.   descwidth := cols-1  - 2 - (groupwidth+1) - (unreadwidth+1);
  2952.  
  2953.   whichgroupi := 0;
  2954.  
  2955.   showgroupnewscreen;
  2956.  
  2957.   quitout := false;
  2958.   done := false;
  2959.  
  2960.   if descwidth<10 then
  2961.     begin
  2962.       xwrites('screen too small, sorry');
  2963.       done := true;
  2964.     end;
  2965.  
  2966.   while not done and not quitout do
  2967.     begin
  2968.       inc(whichgroupi);
  2969.       done := whichgroupi>numjoined;
  2970.  
  2971.       if not done then
  2972.         begin
  2973.           inc(ypos);
  2974.           showngroupindices[ypos] := whichgroupi;
  2975.  
  2976.           xclreolxy(1,ypos);
  2977.           xwritess(whichselchar(ypos),' ');
  2978.           toplineused := min(toplineused,ypos);
  2979.           bottomlineused := max(bottomlineused,ypos);
  2980.  
  2981.           agroup := joinedgroups[showngroupindices[ypos]];
  2982.  
  2983.           tempstr := leftjustify(right(agroup,groupwidth),groupwidth,' ');
  2984.  
  2985.           xwritess(tempstr,' ');  {clearer than leftjustify with width +1}
  2986.  
  2987.           xwrites('...');
  2988.  
  2989.           unreadcount := unreadarticlesin(agroup,sourcegroup);
  2990.  
  2991. { backspace over 3 dots earlier }
  2992.           xwrites(^H^H^H);
  2993.  
  2994.           tempstr := ltoa(unreadcount);
  2995.           if length(tempstr)>unreadwidth then
  2996.             tempstr := '!';
  2997.           tempstr := rightjustify(tempstr,unreadwidth,' ');
  2998.  
  2999.           xwritess(tempstr,' ');
  3000.  
  3001.           xwrites(copy(sourcedesc(agroup,sourcegroup),1,descwidth));
  3002.         end;
  3003.  
  3004.       if xkeypressed or (ypos>=maxypos) or done then
  3005.         begin
  3006.           if xkeypressed then
  3007.             wastec := xreadkey;
  3008.  
  3009.           xclreolxy(1,lpp);
  3010.           if done then
  3011.             xwritehighlights(
  3012.              'select, or any other key to quit ')
  3013.           else
  3014.             xwritehighlights(
  3015.              'select, or {space} to continue, any other key to quit ');
  3016.  
  3017. {
  3018.           inkey := xreadkey;
  3019. }
  3020.           inkey := xreadkeyextended(0,0,toplineused,bottomlineused);
  3021.           xclreolxy(1,lpp);
  3022.  
  3023.           if islower(inkey) or isdigit(inkey) then
  3024.             if showngroupindices[selcharnum(inkey)]>0 then
  3025.               begin
  3026.                 currsource :=
  3027.                  joinedgroups[showngroupindices[selcharnum(inkey)]];
  3028.                 currsourcekind := sourcegroup;
  3029.                 needtofindnextgroup := false;
  3030.                 selnextnoupdate;
  3031.                 selclearall;
  3032.                 didgoto := true;
  3033.  
  3034.                 quitout := true; {redundant, but clear}
  3035.               end;
  3036.  
  3037.           if inkey<>' ' then
  3038.             quitout := true;
  3039.  
  3040.           if ypos>=maxypos then
  3041.             if not quitout then
  3042.               if not done then
  3043.                 showgroupnewscreen;
  3044.         end;
  3045.     end;
  3046.  
  3047.   if not didgoto then
  3048.     selrefresh;
  3049. end;
  3050.  
  3051. procedure selprevgroup;
  3052.  
  3053. var
  3054.   prevgroup: string;
  3055.   foundgroup: string;
  3056.  
  3057. begin
  3058.   selclearall;
  3059.   prevgroup := '..invalid..';
  3060.   foundgroup := prevgroup;
  3061.  
  3062.   reset(joinf);
  3063.   repeat
  3064.     prevgroup := foundgroup;
  3065.     readln(joinf,foundgroup);
  3066.     foundgroup := getfirstw(foundgroup);
  3067.   until foundgroup=currsource;
  3068.  
  3069. {}{should check eof - I guess}
  3070.   if prevgroup<>'..invalid..' then
  3071.     begin
  3072.       currsource := prevgroup;
  3073.       donegroupsel := true;
  3074.       needtofindnextgroup := false;
  3075.     end;
  3076. {}{need more error checking here...}
  3077. {}{also, it just goes back 1 group - not to the one with something to read!}
  3078. end;
  3079.  
  3080. procedure selsearch;
  3081.  
  3082. var
  3083.   searchstring: string;
  3084.   upselsearchstring: string;
  3085.   whichart: integer;
  3086.  
  3087. begin
  3088.   xclreolxy(1,lpp);
  3089.   xwrites('=');
  3090.  
  3091.   searchstring := selsearchstring;
  3092.   xreadlns(searchstring,max(cols-4,76),yespreserve);
  3093.  
  3094.   if searchstring<>'' then
  3095.     begin
  3096.       selsearchstring := searchstring;
  3097.  
  3098.       upselsearchstring := upper(searchstring);
  3099.       for whichart := 1 to numarts do
  3100.         if textintext(upselsearchstring,upper(articles[whichart]^.from)) then
  3101.           setselected(whichart,true)
  3102.         else if textintext(upselsearchstring,
  3103.          upper(articles[whichart]^.basesubject)) then
  3104.           setselected(whichart,true);
  3105.     end;
  3106.   selrefresh;
  3107. end;
  3108.  
  3109. procedure selnextlayout;
  3110.  
  3111. begin
  3112.   layout := succ(layout);
  3113.   if layout=layoutlast then
  3114.     layout := succ(layoutfirst);
  3115.   selrefresh;
  3116. end;
  3117.  
  3118. procedure selhelppage;
  3119.  
  3120. var
  3121.   wastec: char;
  3122.  
  3123. begin
  3124.   xclrscr;
  3125.  
  3126.   hwritexy(1,1,
  3127.    newsreadername+' '+newsreaderversion+' - newsreader-under-development');
  3128.   hwritexy(1,2,
  3129.    'Russell_Schulz@locutus.ofB.ORG ('+releasedate+')');
  3130.  
  3131.   hwritexy(1,4,
  3132.    '{letter},{digit} - toggle whether or not to read that article');
  3133.   hwritexy(1,5,
  3134.    '{c-f} - toggle c through and including f');
  3135.   hwritexy(1,6,
  3136.    '{g*}  - toggle g and all with same subject');
  3137.   hwritexy(1,7,
  3138.    '{space},{enter} - go to next screen (or start browsing if at end)');
  3139.   hwritexy(1,8,
  3140.    '{N}   - go to next group (but browse selected articles first)');
  3141.   hwritexy(1,9,
  3142.    '{X}   - like {N}, but mark {all} articles as read after');
  3143.   hwritexy(1,10,
  3144.    '{P}   - go to previous group     {+} select antikilled articles');
  3145.   hwritexy(1,11,
  3146.    '{@}   - toggle all on this page  {=} select ones that match some word');
  3147.   hwritexy(1,12,
  3148.    '{%},{~} - select, unselect all articles (all pages)');
  3149.   hwritexy(1,13,
  3150.    '{^L}  - refresh screen    {^R} - reread kill and antikill files');
  3151.   hwritexy(1,14,
  3152.    '{<},{>} - go back, forward a page (wraps around if you go too far)');
  3153.   hwritexy(1,15,
  3154.    '{^},{$} - first, last page   {!} shell escape  {"} change layout');
  3155.   hwritexy(1,16,
  3156.    '{Z}   - browse articles on this page (but do {not} mark as read)');
  3157.   hwritexy(1,17,
  3158.    '{W} {S} - write/save selected articles (this or all pages)');
  3159.   hwritexy(1,18,
  3160.    '{C} {M} - copy/move selected articles to folder (this or all pages)');
  3161.   hwritexy(1,19,
  3162.    '{G}   - goto group; can shorten each word (eg. c.o.m.ma)');
  3163.   hwritexy(1,20,
  3164.    '{U}   - unsubscribe from this group   {Y} - show group list');
  3165.   hwritexy(1,21,
  3166.    '{?}   - help               {:} command mode  {Q} - quit');
  3167.  
  3168.   hwritexy(1,23,
  3169.    'press any key to return ');
  3170.  
  3171.   wastec := xreadkey;
  3172.   selrefresh;
  3173. end;
  3174.  
  3175. procedure selcommand;
  3176.  
  3177. var
  3178.   commandline: string;
  3179.   commandverb: string;
  3180.  
  3181. begin
  3182.   xclreolxy(1,lpp);
  3183.   xwrites(':');
  3184.   xreadlns(commandline,max(cols-4,76),nopreserve);
  3185.  
  3186.   internalcmdlineparams := commandline;
  3187.   commandverb := lower(chopfirstw(internalcmdlineparams));
  3188.  
  3189.   if commandline='' then
  3190.     selrefresh
  3191.   else
  3192.     begin
  3193.       if partialmatch(commandverb,'help','h') then
  3194.         selhelppage
  3195.       else if partialmatch(commandverb,'?','?') then
  3196.         selhelppage
  3197.       else if partialmatch(commandverb,'next','n') then
  3198.         begin xwriteln; selnextnoupdate; end
  3199.       else if partialmatch(commandverb,'postfile','postf') then
  3200.         begin postfile; selrefresh; end
  3201.       else if partialmatch(commandverb,'post','p') then
  3202.         begin post; selrefresh; end
  3203.       else if partialmatch(commandverb,'mailfile','mailf') then
  3204.         begin mailfile; selrefresh; end
  3205.       else if partialmatch(commandverb,'mail','m') then
  3206.         begin mail; selrefresh; end
  3207.       else if partialmatch(commandverb,'set','set') then
  3208.         begin
  3209.           justhandleset(internalcmdlineparams,issuspicious);
  3210.           selrefresh;
  3211.         end
  3212.       else if partialmatch(commandverb,'unset','unset') then
  3213.         begin
  3214.           justhandleunset(internalcmdlineparams,issuspicious);
  3215.           selrefresh;
  3216.         end
  3217.       else if partialmatch(commandverb,'show','sho') then
  3218.         begin
  3219.           usershow(internalcmdlineparams);
  3220.           selrefresh;
  3221.         end
  3222.       else if partialmatch(commandverb,'version','v') then
  3223.         begin
  3224.           showversion;
  3225.           selrefresh;
  3226.         end
  3227.       else if partialmatch(commandverb,'quit','q') then
  3228.         begin xwriteln; selquitnoupdate; end
  3229.       else
  3230.         begin
  3231.           warn('unrecognized command');
  3232.           selrefresh;
  3233.         end;
  3234.     end;
  3235. end;
  3236.  
  3237. procedure selcatch;
  3238.  
  3239. begin
  3240.   donegroupsel := true;
  3241.   highestread := highestart;
  3242. end;
  3243.  
  3244. procedure selspace;
  3245.  
  3246. var
  3247.   artnum: integer;
  3248.   anyselected: boolean;
  3249.  
  3250. begin
  3251.   if makespacelikex and (botline=numarts) then
  3252.     begin
  3253.       anyselected := false;
  3254.       for artnum := 1 to numarts do
  3255.         if selected[artnum] then
  3256.           anyselected := true;
  3257.       if anyselected then
  3258.         selcatch
  3259.       else
  3260.         donepagesel := true;
  3261.     end
  3262.   else
  3263.     donepagesel := true;
  3264. end;
  3265.  
  3266. procedure selantikill(firstantikill: boolean);
  3267.  
  3268. var
  3269.   whichart: integer;
  3270.   firstnew: integer;  {firstnew=maxint <=> no articles antikilled}
  3271.  
  3272. begin
  3273.   firstnew := maxint;
  3274.  
  3275.   for whichart := 1 to numarts do
  3276.     if ((articles[whichart]^.indents and 128)<>0)
  3277.      and
  3278.      not selected[whichart] then
  3279.       begin
  3280.         firstnew := min(firstnew,whichart);
  3281.         setselected(whichart,true);
  3282.       end;
  3283.  
  3284.   if firstnew<=numarts then
  3285.     begin
  3286.       if (firstnew>sellpp) and warnautoantikill then
  3287.         begin
  3288.           if firstantikill then
  3289.             selrefresh;
  3290.           warn('at least one article antikilled');
  3291.         end;
  3292.  
  3293. {do we need this anymore with the selrefreshbotline?}
  3294.  
  3295.       if not firstantikill then
  3296.         selrefresh;
  3297.  
  3298.     end;
  3299.  
  3300.   selrefreshbotline;
  3301. end;
  3302.  
  3303. procedure rereadkillfiles;
  3304.  
  3305. begin
  3306.   readinkill(nobackupkill);
  3307.   readinantikill(nobackupkill);
  3308.  
  3309.   if autoantikill then
  3310.     selantikill(nofirstantikill);
  3311.  
  3312.   selrefresh;
  3313. end;
  3314.  
  3315.  
  3316.  
  3317.  
  3318. begin {selectandbrowse}
  3319.   if ismailgroup(currsource) then
  3320.     dateformat := maildateformat
  3321.   else
  3322.     dateformat := newsdateformat;
  3323.  
  3324.   for whichart := 1 to numarts do
  3325.     selected[whichart] := false;
  3326.  
  3327.   numselected := 0;
  3328.  
  3329.   donegroupsel := (numarts=0);
  3330.   lastinkey := ' ';
  3331.   lastselected := false;
  3332.   topline := 1;
  3333.  
  3334.   selsearchstring := '';
  3335.  
  3336.   currsourcedesc := sourcedesc(currsource,currsourcekind);
  3337.  
  3338.   setupselheaderline;
  3339.  
  3340.   if not donegroupsel and autoantikill then
  3341.     selantikill(yesfirstantikill);
  3342.  
  3343.   moreselecting := false;
  3344.   skipsection := false;
  3345.   starbeside := 0;
  3346.  
  3347.   while not moreselecting and not donegroupsel and (currsource<>'') do
  3348.     begin
  3349.       donepagesel := false;
  3350.       selrefresh;
  3351.       while not donegroupsel and not donepagesel and (currsource<>'') do
  3352.         begin
  3353.           inkey := xreadkeyextended(0,0,
  3354.            2+selheaderlines,(botline-topline)+2+selheaderlines);
  3355.           inkey := selmap[inkey];
  3356.  
  3357.           if isselchar(inkey) then
  3358.             togglekey(inkey)
  3359.           else if (inkey='*') and isselchar(lastinkey) then
  3360.             dostar(lastinkey)
  3361.           else if (inkey='-') and isselchar(lastinkey) then
  3362.             dodash(lastinkey)
  3363.           else
  3364.             case inkey of
  3365.               '?': selhelppage;
  3366.               '<': backpage;
  3367.               ^B : backpage;
  3368.               '>': forwardpage;
  3369.               ^F : forwardpage;
  3370.               '^': firstpage;
  3371.               ^A : firstpage;
  3372.               '$': lastpage;
  3373.               ^E : lastpage;
  3374.               ' ': selspace;
  3375.                cr: selspace;
  3376.               'Z': browseandreturn;
  3377.               'W': writehighlightedarts;
  3378.               'S': savehighlightedarts;
  3379.               'C': copyhighlightedarts;
  3380.               'M': movehighlightedarts;
  3381.               'U': unsubscribe;
  3382.               'G': gotonewsource;
  3383.               'Y': showgrouplist;
  3384.               'N': selnextnoupdate;
  3385.               '@': selreversepage;
  3386.               '%': begin selselectall; selrefresh; end;
  3387.               '~': begin selclearall; selrefresh; end;
  3388.               'X': selcatch;
  3389.               'P': selprevgroup;
  3390.               ^L : selrefresh;
  3391.               ^R : rereadkillfiles;
  3392.               '!': begin shellout; selrefresh; end;
  3393.               '+': selantikill(nofirstantikill);
  3394.               '=': selsearch;
  3395.               ':': selcommand;
  3396.               '"': selnextlayout;
  3397.               'Q': selquitnoupdate;
  3398.             end; {case}
  3399.  
  3400.           lastinkey := inkey;
  3401.         end;
  3402.  
  3403.       if botline<numarts then
  3404.         inc(topline,sellpp)
  3405.       else
  3406.         donegroupsel := true;
  3407.  
  3408. {moreselecting is always false here}
  3409.  
  3410.       if donegroupsel then
  3411.         viewarts(1,numarts,true);
  3412.  
  3413.       if moreselecting then
  3414.         donegroupsel := false;
  3415.  
  3416.       moreselecting := false;
  3417.  
  3418.     end;
  3419.  
  3420. end;
  3421.  
  3422. end.
  3423.