home *** CD-ROM | disk | FTP | other *** search
/ DP Tool Club 31 / CDASC_31_1996_juillet_aout.iso / internet / rnr214.zip / RNRFUNC.PAS < prev    next >
Pascal/Delphi Source File  |  1996-04-01  |  68KB  |  2,892 lines

  1. unit rnrfunc;
  2.  
  3. {
  4.  
  5. rnrfunc.pas - rnr functions
  6.  
  7. also see genericf.pas - split off into a separate unit to get around code
  8.   segment size limitation
  9.  
  10. }
  11.  
  12. {$I rnr-def.pas}
  13.  
  14. interface
  15.  
  16. uses dos,genericf,rnrglob,rnrconf,rnrio,rnrfile,rnrnov;
  17.  
  18. const
  19.   yeselevenchars=true;
  20.   noelevenchars=false;
  21.  
  22.   yesheadersearch=true;
  23.   noheadersearch=false;
  24.  
  25.   couldnotreadfilecookie='(could not read file)';
  26.  
  27. function basesitename(s: string): string;
  28. function newseqnumber: integer;
  29. function newmessageid: string;
  30. function getalreadyread(s: string): articlefilenametype;
  31. function joinedtogroup(var group: string): boolean;
  32. function expandsource(var source: string; var sourcekind: sourcetype): boolean;
  33. function joinedtoexactgroup(group: string): boolean;
  34. function parseheadername(s: string): string;
  35. function parseheadervalue(s: string): string;
  36. function wafflegetconfig(tag: string): string;
  37. function uupcgetconfig(tag: string): string;
  38. function getconfig(tag: string): string;
  39. function getheaderline(infilename, fieldname: string): string;
  40. function rfcdateheadertodate(datestr: string): datet;
  41. function ymdtodate(yyyymmdd: string): datet;
  42. function datetostring(adate: datet): string;
  43.  
  44. {var only for efficiency}
  45. function xsubjseq(c1,c2: char; var s1,s2: subjstringt): boolean;
  46. function xfirstsubjg(c1,c2: char; var s1,s2: subjstringt): boolean;
  47.  
  48. function subjseq(var s1,s2: subjstringt): boolean;
  49. function firstsubjg(var s1,s2: subjstringt): boolean;
  50. function canonicalfirstchar(var subject: subjstringt): char;
  51.  
  52. function firstartfirst(a,b: integer): boolean;  {assuming subjseq() is true}
  53. function isavalidgroup(group: string): boolean;
  54. function getgroupdir(group: string): string;
  55. function groupsattr(group: string; attr: string): string;
  56. function groupbattr(group: string; attr: string): boolean;
  57. function sourcedesc(source: string; sourcekind: sourcetype): string;
  58. function getnextgroup: string;
  59. function importantgroup(group: string): boolean;
  60. function alreadyseen(newsgroups: string): boolean;
  61. function getpwinfoforuser(field164,field165,fieldunix: integer;
  62.  someuser: string): string;
  63. function getpwinfo(field164,field165,fieldunix: integer): string;
  64. function getpwinfo164foruser(field: integer; someuser: string): string;
  65. function getpwinfo165foruser(field: integer; someuser: string): string;
  66. function getpwinfounixforuser(field: integer; someuser: string): string;
  67. function getfullnameforuser(someuser: string): string;
  68. function extwafexpand(s: string; percenti: string; percentf: string): string;
  69. function wafexpand(s: string): string;
  70. function makesame(var s: string; prefix,shouldbe: string): boolean;
  71. function chopfirstaddr(var addresses: string): string;
  72. function expandonemail(address: string): string;
  73. function expandmail(addresses: string): string;
  74. function screenline(s: string): string;
  75. function onekey(prompt: string; validkeys: string): char;
  76. function nonhighlightonekey(prompt: string; validkeys: string): char;
  77. function onekeydef(prompt: string; validkeys: string; default: char): char;
  78. function ismailgroup(group: string): boolean;
  79. function isnormalgroup(group: string): boolean;
  80. function getsyscmd(cmd: string): string;
  81. function searchart(filename: string; upsearchtext: string;
  82.  headersearch: boolean): boolean;
  83. function searchnov(filename: string; upsearchtext: string): boolean;
  84. function ismoderated(group: string): boolean;
  85. function isheaderinlist(header, uheaderlist: string): boolean;
  86. function getaddressfromline(s: string): string;
  87. function isreasonableaddress(addr: string): boolean;
  88. function nthlayout(whichlayout: integer): layoutt;
  89.  
  90. {var only for efficiency}
  91. function isabreakline(var s: string): boolean;
  92.  
  93. function findproblemwithmessage(messagefn: string): string;
  94. function toomuchquoting(messagefn: string): boolean;
  95. function toolongline(messagefn: string; maxlen: integer): boolean;
  96. function showdebug(s: string): boolean;
  97. function unreadarticlesin(asource: string; sourcekind: sourcetype):
  98.  articlefilenametype;
  99. function highestreadin(asource: string; sourcekind: sourcetype):
  100.  articlefilenametype;
  101. function textintext(asubtext: string; awholetext: string): boolean;
  102.  
  103.  
  104. implementation
  105.  
  106. function pathizegroup(group: string; elevenchars: boolean): string;
  107.  
  108. var
  109.   result: string;
  110.   mangledgroup: string;
  111.   component: string;
  112.  
  113. begin
  114.   result := '';
  115.  
  116.   mangledgroup := crepl(group,'.',' ');
  117.  
  118.   while mangledgroup<>'' do
  119.     begin
  120.       component := chopfirstw(mangledgroup);
  121.  
  122.       if length(component)<=8 then
  123.         result := result+component
  124.       else if elevenchars then
  125.         result := result+copy(component,1,8)+'.'+
  126.          copy(component, max(9,length(component)-2), 3)
  127.       else
  128.         result := result+copy(component,1,8);
  129.  
  130.       if mangledgroup<>'' then
  131.         result := result+'\';
  132.     end;
  133.  
  134.   pathizegroup := result;
  135. end;
  136.  
  137. function basesitename;
  138.  
  139. var
  140.   result: string;
  141.   atbang: integer;
  142.   atpercent: integer;
  143.   atat: integer;
  144.   work: string;
  145.   atdot: integer;
  146.  
  147. begin
  148.   result := uucpname;
  149.  
  150.   atbang := pos('!',s);
  151.   atpercent := pos('%',s);
  152.   atat := pos('@',s);
  153.   if atbang>0 then
  154.     begin
  155.       work := s;
  156.       while atbang>0 do
  157.         begin
  158.           result := copy(work,1,atbang-1);
  159.           work := copy(work,atbang+1,255);
  160.           atbang := pos('!',work);
  161.         end;
  162.     end
  163.   else if atpercent>0 then
  164.     begin
  165.       result := copy(s,atpercent+1,255);
  166.       atat := pos('@',result);
  167.       if atat>0 then
  168.         result := copy(result,1,atat-1);
  169.     end
  170.   else if atat>0 then
  171.     begin
  172.       result := copy(s,atat+1,255);
  173.     end;
  174.  
  175.   atdot := pos('.',result);
  176.   if atdot>0 then
  177.     result := copy(result,1,atdot-1);
  178.  
  179.   basesitename := result;
  180. end;
  181.  
  182. function newseqnumber;
  183.  
  184. var
  185.   seqf: text;
  186.   seqfn: string;
  187.   seqdn: string;
  188.   newseq: integer;
  189.  
  190. begin
  191.   newseq := 42;
  192.   if xiface=ifacewaffle then
  193.     if (ifaceversion='1.64') or (ifaceversion=ifaceversionunix) then
  194.       begin
  195.         seqdn := configdir+'\system';
  196.         seqfn := 'seqf';
  197.       end
  198.     else
  199.       begin
  200.         seqdn := configdir+'\uucp';
  201.         seqfn := 'sequence';
  202.       end
  203.   else if xiface=ifaceuupc then
  204.     begin
  205.       seqdn := configdir+'\uucp';
  206.       seqfn := 'sequence';
  207.     end
  208.   else if xiface=ifaceuufree then
  209.     begin
  210.       seqdn := configdir+'\uucp';
  211.       seqfn := 'sequence';
  212.       if not fexists(seqfn) then
  213.         begin
  214.           seqdn := configdir+'\system';
  215.           seqfn := 'seqf';
  216.         end;
  217.     end;
  218.  
  219. {} {I think this should be maybemkhier but it's a pain from this low-level code}
  220. {
  221.   maybemkhier(seqdn);
  222. }
  223.   mkhier(seqdn);
  224.  
  225.   seqfn := withbackslash(seqdn)+seqfn;
  226.  
  227.   safereset(seqf,seqfn);
  228.   if fileresult=0 then
  229.     begin
  230.       readln(seqf,newseq);
  231.       close(seqf);
  232.     end;
  233.  
  234.   rewrite(seqf);
  235.   writeln(seqf,integertozstring(newseq+1,4));
  236.   close(seqf);
  237.  
  238.   newseqnumber := newseq;
  239. end;
  240.  
  241. function newmessageid;
  242.  
  243. var
  244.   result: string;
  245.  
  246. begin
  247.   result := '<'+itoa(year mod 100)+integertozstring(month,2)+
  248.    integertozstring(dayofmonth,2)+'.'+currenttimedigits+'.'+
  249.    randomdigit+randomletter+randomdigit+'.'+newsreadername;
  250.  
  251. {preserve waffle's indicator mechanism}
  252.   if (xiface=ifacewaffle) and (ifaceversion<>ifaceversionunix) then
  253.     result := result+'.'+
  254.      'w'+copy(ifaceversion,1,1)+copy(ifaceversion,3,2)+'w'
  255.   else
  256.     result := result+'.'+fromuserid;
  257.  
  258.   result := result+'@'+fqdn+'>';
  259.  
  260.   newmessageid := result;
  261. end;
  262.  
  263. function getalreadyread;
  264.  
  265. begin
  266.   getalreadyread := atol(ltrim(trim(copy(s,pos(' ',s)+1,255))));
  267. end;
  268.  
  269. function closegroup(partial,full: string): boolean;
  270.  
  271. var
  272.   result: boolean;
  273.   partwork, fullwork: string;
  274.   partat, fullat: integer;
  275.  
  276. begin
  277.   result := false;
  278.  
  279.   if (numoccur('.',partial)=numoccur('.',full)) then
  280.     begin
  281.       result := true;
  282.  
  283.       partwork := partial+'.';
  284.       fullwork := full+'.';
  285.  
  286.       while result and (pos('.',partwork)>0) do
  287.         begin
  288.           partat := pos('.',partwork);
  289.           fullat := pos('.',fullwork);
  290.  
  291.           result := result and
  292.            (copy(partwork,1,partat-1)=copy(fullwork,1,partat-1));
  293.  
  294.           if result then
  295.             begin
  296.               partwork := copy(partwork,partat+1,255);
  297.               fullwork := copy(fullwork,fullat+1,255);
  298.             end;
  299.         end;
  300.     end;
  301.  
  302.   closegroup := result;
  303. end;
  304.  
  305. {joinedtogroup changes the parameter if and only if it isn't joined}
  306. {to, and something else could be found that _is_ joined to}
  307.  
  308. {it looks for a initials group, or if not a substring group, or if}
  309. {neither a mail folder}
  310.  
  311. function joinedtogroup;
  312.  
  313. var
  314.   result: boolean;
  315.   eachg: string;
  316.   newname: string;
  317.   subname: string;
  318.   mailname: string;
  319.  
  320. begin
  321.   result := false;
  322.  
  323.   newname := '';
  324.   subname := '';
  325.   mailname := '';
  326.  
  327.   reset(joinf);
  328.  
  329.   while not eof(joinf) and not result do
  330.     begin
  331.       readln(joinf,eachg);
  332.       eachg := getfirstw(eachg);
  333.  
  334.       if eachg=group then
  335.         result := true
  336.       else
  337.         begin
  338.           if ismailgroup(eachg) then
  339.             begin
  340.             if mailname='' then
  341.               if closegroup(group,eachg) then
  342.                 mailname := eachg;
  343.             if mailname='' then
  344.               if pos(group,eachg)<>0 then
  345.                 mailname := eachg;
  346.             end
  347.           else
  348.             if newname='' then
  349.               if closegroup(group,eachg) then
  350.                 newname := eachg
  351.               else if subname='' then
  352.                 if pos(group,eachg)<>0 then
  353.                   subname := eachg;
  354.         end;
  355.     end;
  356.  
  357.   if not result and (newname<>'') then
  358.     begin
  359.       group := newname;
  360.       result := true;
  361.     end;
  362.  
  363.   if not result and (subname<>'') then
  364.     begin
  365.       group := subname;
  366.       result := true;
  367.     end;
  368.  
  369.   if not result and (mailname<>'') then
  370.     begin
  371.       group := mailname;
  372.       result := true;
  373.     end;
  374.  
  375.   joinedtogroup := result;
  376. end;
  377.  
  378. function expandsource;
  379.  
  380. var
  381.   result: boolean;
  382.   unslashed: string;
  383.  
  384. begin
  385.   result := false;
  386.  
  387.   unslashed := unslash(source);
  388.  
  389.   if joinedtogroup(source) then
  390.     begin
  391.       sourcekind := sourcegroup;
  392.       result := true;
  393.     end
  394.   else if trusted then
  395.     if dexists(unslashed) then
  396.       begin
  397.         source := unslashed;
  398.         sourcekind := sourcedir;
  399.         result := true;
  400.       end;
  401.  
  402.   expandsource := result;
  403. end;
  404.  
  405. function joinedtoexactgroup;
  406.  
  407. var
  408.   result: boolean;
  409.   eachg: string;
  410.  
  411. begin
  412.   result := false;
  413.  
  414.   reset(joinf);
  415.  
  416.   while not eof(joinf) and not result do
  417.     begin
  418.       readln(joinf,eachg);
  419.       eachg := getfirstw(eachg);
  420.  
  421.       if eachg=group then
  422.         result := true
  423.     end;
  424.  
  425.   joinedtoexactgroup := result;
  426. end;
  427.  
  428. function parseheadername;
  429.  
  430. begin
  431.   parseheadername := copy(s,1,pos(':',s)-1);
  432. end;
  433.  
  434. function parseheadervalue;
  435.  
  436. begin
  437.   parseheadervalue := copy(s,pos(':',s)+2,255);
  438. end;
  439.  
  440. function wafflegetconfig;
  441.  
  442. var
  443.   result: string;
  444.   infile: text;
  445.   s: string;
  446.   foundtag: string;
  447.  
  448. begin
  449.   result := '';
  450.  
  451.   oldfilemode := filemode;
  452.   if not nofilemode then
  453.     filemode := $40;   {read only, deny none}
  454.  
  455.   if customstatic<>'' then
  456.     begin
  457.       safereset(infile,customstatic);
  458.       if fileresult=0 then
  459.         begin
  460.           while (result='') and not eof(infile) do
  461.             begin
  462.               readln(infile,s);
  463.               if s<>'' then
  464.                 if copy(s,1,1)<>'#' then
  465.                   begin
  466.                     foundtag := trim(ltrim(lower(parseheadername(s))));
  467.                     if foundtag=tag then
  468.                       begin
  469.                         result := trim(ltrim(parseheadervalue(s)));
  470.                       end;
  471.                   end;
  472.             end;
  473.           close(infile);
  474.         end;
  475.     end;
  476.  
  477.   if result='' then
  478.     begin
  479.  
  480.       safereset(infile,wafenv);
  481.  
  482.       if fileresult=0 then
  483.         begin
  484.           while (result='') and not eof(infile) do
  485.             begin
  486.               readln(infile,s);
  487.               if s<>'' then
  488.                 if copy(s,1,1)<>'#' then
  489.                   begin
  490.                     foundtag := lower(trim(ltrim(parseheadername(s))));
  491.                     if foundtag=tag then
  492.                       begin
  493.                         result := trim(ltrim(parseheadervalue(s)));
  494.                       end;
  495.                   end;
  496.             end;
  497.           close(infile);
  498.         end;
  499.     end;
  500.  
  501.   filemode := oldfilemode;
  502.  
  503.   wafflegetconfig := result;
  504. end;
  505.  
  506. function uupcgetoneconfig(fn: string; tag: string): string;
  507.  
  508. var
  509.   result: string;
  510.   infile: text;
  511.   s: string;
  512.   foundtag: string;
  513.  
  514. begin
  515.   result:= '';
  516.  
  517.   if fn<>'' then
  518.     begin
  519.       safereset(infile,fn);
  520.       if fileresult=0 then
  521.         begin
  522.           while (result='') and not eof(infile) do
  523.             begin
  524.               readln(infile,s);
  525.               if s<>'' then
  526.                 if copy(s,1,1)<>'#' then
  527.                   begin
  528.                     foundtag := trim(ltrim(copy(s,1,pos('=',s)-1)));
  529.                     if lower(foundtag)=tag then
  530.                       result := trim(ltrim(copy(s,pos('=',s)+1,255)));
  531.                   end;
  532.             end;
  533.           close(infile);
  534.         end;
  535.     end;
  536.  
  537.   uupcgetoneconfig:= result;
  538.  
  539. end;
  540.  
  541. function uupcgetconfig(tag: string): string;
  542.  
  543. var
  544.   result: string;
  545.  
  546. begin
  547.   result := '';
  548.   result := uupcgetoneconfig(uupcusr,tag);
  549.   if result='' then
  550.     result := uupcgetoneconfig(uupcsys,tag);
  551.   uupcgetconfig := result;
  552. end;
  553.  
  554. procedure changetag(var changed: boolean; var tag: string;
  555.  basetag: string; waffletag, uupctag, othertag: string);
  556.  
  557. begin
  558.   if not changed then
  559.     if tag=basetag then
  560.       begin
  561.         tag := othertag;
  562.         if xiface=ifacewaffle then
  563.           tag := waffletag
  564.         else if xiface=ifaceuupc then
  565.           tag := uupctag
  566.         else if xiface=ifaceuufree then
  567.           tag := waffletag;
  568.       end;
  569. end;
  570.  
  571. function getconfig;
  572.  
  573. const
  574.   x='';
  575.  
  576. var
  577.   result: string;
  578.   n: string;
  579.   c: boolean;
  580.  
  581. begin
  582.   result := x;
  583.  
  584.   n := tag;
  585.   c := false;
  586.  
  587. {   changed,base tag     ,waffle tag ,uupc tag      ,other tag }
  588.  
  589. changetag(c,n,'tempdir'  ,'temporary','tempdir'     ,x);
  590. changetag(c,n,'mailbox'  ,x          ,'mailbox'     ,x);
  591. changetag(c,n,'fqdn'     ,'node'     ,'domain'      ,x);
  592. changetag(c,n,'uucpname' ,'uucpname' ,'nodename'    ,x);
  593. changetag(c,n,'spooldir' ,'spool'    ,'spool'       ,x);
  594. changetag(c,n,'userdir'  ,'user'     ,'user'        ,x);
  595. changetag(c,n,'outboxdir','outbox'   ,'outbox'      ,x);
  596. changetag(c,n,'configdir','waffle'   ,'confdir'     ,x);
  597. changetag(c,n,'fullname' ,x          ,'name'        ,x);
  598. changetag(c,n,'home'     ,x          ,'home'        ,x);
  599. changetag(c,n,'smarthost','smarthost','mailserv'    ,x);
  600. changetag(c,n,'backbone' ,'backbone' ,'backbone'    ,x);
  601. changetag(c,n,'organ'    ,'organ'    ,'organization',x);
  602. changetag(c,n,'replyto'  ,'replyto'  ,'replyto'     ,x);
  603. changetag(c,n,'newsroot' ,x          ,'newsdir'     ,x);
  604.  
  605.   if n<>x then
  606.     if xiface=ifacewaffle then
  607.       result := wafflegetconfig(n)
  608.     else if xiface=ifaceuupc then
  609.       result := uupcgetconfig(n)
  610.     else if xiface=ifaceuufree then
  611.       result := wafflegetconfig(n);
  612.  
  613.   getconfig := result;
  614. end;
  615.  
  616. function getheaderline;
  617.  
  618. var
  619.   result: string;
  620.   infile: file;
  621.   foundline: boolean;
  622.   s: string;
  623.   ufieldname: string;
  624.   headerbytesseen: integer;
  625.   morelinesinheader: boolean;
  626.   wastes: string;
  627.   i,j: integer;
  628.  
  629. function nextlinefrombuf: string;
  630.  
  631. var
  632.   result: string;
  633.   gotlf: boolean;
  634.   c: char;
  635.  
  636.     begin {nextlinefrombuf}
  637.       result := '';
  638.  
  639.       gotlf := false;
  640.  
  641.       while (headerbytesseen<headerbytesinmem) and not gotlf do
  642.         begin
  643.           inc(headerbytesseen);
  644.           c := headerbuf[headerbytesseen];
  645.  
  646.           if (c=lf) then
  647.             gotlf := true
  648.           else if c<>cr then
  649.             result := result+c;
  650.  
  651.         end;
  652.  
  653.       nextlinefrombuf := result;
  654.     end; {nextlinefrombuf}
  655.  
  656. begin
  657.   result := '';
  658.   ufieldname := upper(fieldname);
  659.  
  660.   foundline := false;
  661.  
  662.   if headerinmem<>infilename then
  663.     begin
  664.  
  665.       oldfilemode := filemode;
  666.       if not nofilemode then
  667.         filemode := $40;   {read only, deny none}
  668.  
  669.       assign(infile,infilename);
  670.       {$I-}
  671.       reset(infile,1);
  672.       {$I+}
  673.  
  674.       if ioresult=0 then
  675.         begin
  676.           blockread(infile,headerbuf,headerbufsize,headerbytesinmem);
  677.           headerinmem := infilename;
  678.           close(infile);
  679.         end
  680.       else
  681.         begin
  682.           for i := 1 to headerbufsize do
  683.             headerbuf[i] := ' ';
  684.           result := couldnotreadfilecookie;
  685.           foundline := true;
  686.         end;
  687.  
  688.       filemode := oldfilemode;
  689.  
  690.       for i := 1 to headertlsize do
  691.         begin
  692.           headertrackedlines[i].first := #0;
  693.           headertrackedlines[i].offset := -1;
  694.         end;
  695.  
  696.       headertrackedlines[1].first := upcase(headerbuf[1]);
  697.       headertrackedlines[1].offset := 1;
  698.  
  699.       j := 1;
  700.       i := 0;
  701.       while (i<headerbufsize-2) and (j<headertlsize) do
  702.         begin
  703.           inc(i);
  704.           if headerbuf[i]=lf then
  705.             if headerbuf[i+2]=lf then
  706.               i := headerbufsize {found the empty line}
  707.             else
  708.               begin
  709.                 inc(j);
  710.                 headertrackedlines[j].first := upcase(headerbuf[i+1]);
  711.                 headertrackedlines[j].offset := i+1;
  712.               end;
  713.         end;
  714.  
  715. {$ifdef testfastheaders}
  716. for i := 1 to min(10,headertlsize) do
  717.   writeln(headertrackedlines[i].offset:3,' ',headertrackedlines[i].first);
  718. delay(1000);
  719. {$endif}
  720.  
  721.     end;
  722.  
  723. {$ifdef veryoldheader}
  724.  
  725.   foundblank := false;
  726.  
  727.   while not eof(f) and not foundblank and not foundline do
  728.     begin
  729.       readln(f,s);
  730.       if copy(upper(ltrim(s)),1,length(fieldname))=ufieldname then
  731.         begin
  732.           foundline := true;
  733.           result := ltrim(trim(copy(trim(s),length(fieldname)+1,255)));
  734.           if not eof(f) then
  735.             begin
  736.               readln(f,s);
  737.               if copy(s,1,1)=' ' then
  738.                 result := result+s;
  739.             end;
  740.         end
  741.       else if s='' then
  742.         foundblank := true;
  743.     end;
  744.   close(f);
  745. {$endif}
  746.  
  747. {$ifdef oldheader}
  748.  
  749.   foundblank := false;
  750.  
  751.   headerbytesseen := 0;
  752.   while (headerbytesseen<headerbytesinmem) and
  753.    not foundblank and not foundline do
  754.     begin
  755.       s := nextlinefrombuf;
  756.       if copy(upper(ltrim(s)),1,length(fieldname))=ufieldname then
  757.         begin
  758.           foundline := true;
  759.           result := ltrim(trim(copy(trim(s),length(fieldname)+1,255)));
  760.           if headerbytesseen<headerbytesinmem then
  761.             begin
  762.               morelinesinheader := true;
  763.               while morelinesinheader do
  764.                 begin
  765.                   s := nextlinefrombuf;
  766.                   if (copy(s,1,1)=' ') or (copy(s,1,1)=tab) then
  767.                     begin
  768.                       s := ltrim(s);
  769.  
  770. {handle References: line specially - always get the last part}
  771.  
  772.                       if ufieldname='REFERENCES:' then
  773.                         begin
  774.                           if length(s)>200 then
  775.                             result := s
  776.                           else
  777.                             begin
  778.                               if length(result)+length(s)>200 then
  779.                                 wastes := chopfirstw(result);
  780.                               if length(result)+length(s)>200 then
  781.                                 wastes := chopfirstw(result);
  782.                               if length(result)+length(s)>200 then
  783.                                 wastes := chopfirstw(result);
  784.                               if length(result)+length(s)>200 then
  785.                                 wastes := chopfirstw(result);
  786.                               result := result+' '+s;
  787.                             end;
  788.                         end
  789.                       else
  790.                         result := result+' '+s;
  791.                     end
  792.                   else
  793.                     morelinesinheader := false;
  794.                 end;
  795.             end;
  796.         end
  797.       else if s='' then
  798.         foundblank := true;
  799.     end;
  800.  
  801. {$endif}
  802.  
  803.   j := 0;
  804.   while (j<headertlsize) and not foundline do
  805.     begin
  806.       inc(j);
  807.       if headertrackedlines[j].first=ufieldname[1] then
  808.         begin
  809.           headerbytesseen := headertrackedlines[j].offset-1;
  810.           s := nextlinefrombuf;
  811.           if copy(upper(ltrim(s)),1,length(fieldname))=ufieldname then
  812.             begin
  813.               foundline := true;
  814.               result := ltrim(copy(trim(s),length(fieldname)+1,255));
  815.               if headerbytesseen<headerbytesinmem then
  816.                 begin
  817.                   morelinesinheader := true;
  818.                   while morelinesinheader do
  819.                     begin
  820.                       s := nextlinefrombuf;
  821.                       if (copy(s,1,1)=' ') or (copy(s,1,1)=tab) then
  822.                         begin
  823.                           s := ltrim(s);
  824.  
  825. {handle References: line specially - always get the last part}
  826.  
  827.                           if ufieldname='REFERENCES:' then
  828.                             begin
  829.                               if length(result)+length(s)>200 then
  830.                                 wastes := chopfirstw(result);
  831.                               result := result+' '+s;
  832.                             end
  833.                           else
  834.                             result := result+' '+s;
  835.                         end
  836.                       else
  837.                         morelinesinheader := false;
  838.                     end;
  839.                 end;
  840.             end;
  841.         end;
  842.     end;
  843.  
  844.   getheaderline := result;
  845. end;
  846.  
  847. {}{} {doesn't handle time zones at all - but at least when a user}
  848.      {posts twice on the same day, the tz will be the same each time}
  849.      {and thus correctly ordered for that user's posts}
  850.  
  851. function rfcdateheadertodate;
  852.  
  853. var
  854.   result: datet;
  855.   workstr: string;
  856.   dayofmonth: longint;
  857.   monthstr: string;
  858.   month: longint;
  859.   year: longint;
  860.   gmthour: longint;
  861.  
  862. begin
  863.   result := 9999*16384;
  864.  
  865.   if datestr<>'' then
  866.     begin
  867.       workstr := datestr;
  868.       dayofmonth := snatchint(workstr);
  869.       workstr := ltrim(workstr);
  870.       monthstr := copy(workstr,1,3);
  871.       month := monthstringtointeger(monthstr);
  872.       workstr := ltrim(lchop(workstr,4));
  873.       year := snatchint(workstr);
  874.       if year<100 then
  875.         inc(year,1900);
  876.       gmthour := snatchint(workstr);
  877.       result := year*16384+month*1024+dayofmonth*32+gmthour;
  878.     end;
  879.  
  880.   rfcdateheadertodate := result;
  881. end;
  882.  
  883. function ymdtodate;
  884.  
  885. var
  886.   result: datet;
  887.  
  888.   workstr: string;
  889.   dayofmonth: longint;
  890.   month: longint;
  891.   year: longint;
  892.   gmthour: longint;
  893.  
  894. begin
  895.   result := 9999*16384;
  896.  
  897.   if yyyymmdd<>'' then
  898.     begin
  899.       workstr := yyyymmdd;
  900.       year := snatchint(workstr);
  901.       month := snatchint(workstr);
  902.       dayofmonth := snatchint(workstr);
  903.       if year<100 then
  904.         inc(year,1900);
  905.       gmthour := 0;
  906.       result := year*16384+month*1024+dayofmonth*32+gmthour;
  907.     end;
  908.  
  909.   ymdtodate := result;
  910. end;
  911.  
  912. function datetostring(adate: datet): string;
  913.  
  914. var
  915.   result: string;
  916.  
  917. begin
  918.   datetostring := ymdtostring(
  919.    adate div 16384,
  920.    (adate mod 16384) div 1024,
  921.    (adate mod 1024) div 32);
  922. end;
  923.  
  924. {var only for efficiency}
  925.  
  926. function canonicalsubj(var subject: subjstringt): subjstringt;
  927.  
  928. var
  929.   result: subjstringt;
  930.   tempstr: string;
  931.   i: integer;
  932.  
  933. begin
  934.   result := '';
  935.  
  936.   if subjectlength=255 then
  937.     result := subject
  938.   else
  939.     result := copy(subject,1,subjectlength);
  940.  
  941.   if subjectscaseinsensitive then
  942.     result := upper(result);
  943.  
  944.   if squashspaces then
  945.     begin
  946.       tempstr := '';
  947.       for i := 1 to length(result) do
  948.         if (result[i]<>' ') and (result[i]<>tab) then
  949.           tempstr := tempstr+result[i];
  950.       result := tempstr;
  951.     end;
  952.  
  953.   canonicalsubj := result;
  954. end;
  955.  
  956. {var only for efficiency}
  957.  
  958. {no longer used, since every time I call it I already have canonicalsubj}
  959.  
  960. function canonicalfirstchar;
  961.  
  962. var
  963.   result: char;
  964.   tempi: integer;
  965.  
  966. begin
  967.   result := ' ';
  968.  
  969.   if subject<>'' then
  970.     begin
  971.       if not squashspaces then
  972.         result := subject[1]
  973.       else
  974.         begin
  975.  
  976. {$ifdef slow}
  977.           tempstr := ltrim(subject)+' ';  {if it's empty, return space}
  978.           result := tempstr[1];
  979. {$endif}
  980.  
  981.           for tempi := 1 to length(subject) do
  982.             if (result=' ') and (subject[tempi]<>tab) then
  983.               result := subject[tempi];
  984.         end;
  985.  
  986.       if subjectscaseinsensitive then
  987.         result := upcase(result);
  988.     end;
  989.  
  990.   canonicalfirstchar := result;
  991. end;
  992.  
  993. {var only for efficiency}
  994.  
  995. { string comparison for the shorter string -- unless it's empty }
  996.  
  997. function subjshortequal(var s1,s2: subjstringt): boolean;
  998.  
  999. var
  1000.   result: boolean;
  1001.  
  1002.   len1: integer;
  1003.   len2: integer;
  1004.  
  1005. begin
  1006.   result := false;
  1007.  
  1008.   len1 := length(s1);
  1009.   len2 := length(s2);
  1010.  
  1011.   if (len1=0) and (len2=0) then
  1012.     result := true
  1013.   else if (len1=0) or (len2=0) then
  1014.     result := false
  1015.   else if len1=len2 then
  1016.     result := (s1=s2)
  1017.   else if (len1<len2) and (len1>=equatetruncated) then
  1018.     result := (s1=copy(s2,1,len1))
  1019.   else if (len2<len1) and (len2>=equatetruncated) then
  1020.     result := (copy(s1,1,len2)=s2)
  1021.   else
  1022.     result := false;  {lengths aren't the same, so can't be equal}
  1023.  
  1024.   subjshortequal := result;
  1025. end;
  1026.  
  1027. {s1 and s2 var for efficiency}
  1028. procedure copytocanon(var s1,s2: subjstringt; var canon1,canon2: subjstringt);
  1029.  
  1030. begin
  1031.   canon1 := canonicalsubj(s1);
  1032.   canon2 := canonicalsubj(s2);
  1033. end;
  1034.  
  1035. function xsubjseq;
  1036.  
  1037. var
  1038.   result: boolean;
  1039.   canon1,canon2: subjstringt;
  1040.  
  1041. begin
  1042.   result := false;
  1043.  
  1044.   if equatetruncated<>0 then
  1045.     begin
  1046.       if (s1='') or (s2='') or (c1=c2) then
  1047.         begin
  1048.           copytocanon(s1,s2,canon1,canon2);
  1049.           result := subjshortequal(canon1,canon2);
  1050.         end
  1051.       else
  1052.         result := false;
  1053.     end
  1054.   else
  1055.     begin
  1056.       if (s1='') or (s2='') or (c1=c2) then
  1057.         begin
  1058.           copytocanon(s1,s2,canon1,canon2);
  1059.           result := (canon1=canon2);
  1060.         end
  1061.       else
  1062.         result := false;
  1063.     end;
  1064.  
  1065.   xsubjseq := result;
  1066. end;
  1067.  
  1068. function xfirstsubjg;
  1069.  
  1070. var
  1071.   result: boolean;
  1072.   canon1: subjstringt;
  1073.   canon2: subjstringt;
  1074.  
  1075. begin
  1076.   result := false;
  1077.  
  1078.   if (s1='') or (s2='') then
  1079.     begin
  1080.       copytocanon(s1,s2,canon1,canon2);
  1081.       result := (canon1>canon2);
  1082.     end
  1083.   else if c1<c2 then
  1084.     result := false
  1085.   else
  1086.     begin
  1087.       copytocanon(s1,s2,canon1,canon2);
  1088.       result := (canon1>canon2);
  1089.       if equatetruncated<>0 then
  1090.         result := result and not subjshortequal(canon1,canon2);
  1091.     end;
  1092.  
  1093.   xfirstsubjg := result;
  1094. end;
  1095.  
  1096. function subjseq;
  1097.  
  1098. var
  1099.   c1,c2: char;
  1100.  
  1101. begin
  1102.   c1 := canonicalfirstchar(s1);
  1103.   c2 := canonicalfirstchar(s2);
  1104.  
  1105.   subjseq := xsubjseq(c1,c2,s1,s2);
  1106. end;
  1107.  
  1108. function firstsubjg;
  1109.  
  1110. var
  1111.   c1,c2: char;
  1112.  
  1113. begin
  1114.   c1 := canonicalfirstchar(s1);
  1115.   c2 := canonicalfirstchar(s2);
  1116.  
  1117.   firstsubjg := xfirstsubjg(c1,c2,s1,s2);
  1118. end;
  1119.  
  1120. function hasheq(h1,h2: hashedt): boolean;
  1121.  
  1122. begin
  1123.   hasheq := (h1[1]=h2[1]) and (h1[2]=h2[2]);
  1124. end;
  1125.  
  1126. function firstartfirst;
  1127.  
  1128. var
  1129.   result: boolean;
  1130.  
  1131. begin
  1132.   result := true;
  1133.  
  1134. {$ifdef testhash}
  1135.  
  1136. if true then
  1137.   begin
  1138.     writeln('#',a,' mes=',hmessageidsp^[a,1]:5,' ',hmessageidsp^[a,2]:5);
  1139.     writeln('#',a,' ref=',
  1140.      hreferencesp[1]^[a,1]:5,' ',hreferencesp[1]^[a,2]:5,' ',
  1141.      hreferencesp[2]^[a,1]:5,' ',hreferencesp[2]^[a,2]:5,' ',
  1142.      hreferencesp[3]^[a,1]:5,' ',hreferencesp[3]^[a,2]:5,' ',
  1143.      hreferencesp[4]^[a,1]:5,' ',hreferencesp[4]^[a,2]:5);
  1144.     writeln('#',b,' mes=',hmessageidsp^[b,1]:5,' ',hmessageidsp^[b,2]:5);
  1145.     writeln('#',b,' ref=',
  1146.      hreferencesp[1]^[b,1]:5,' ',hreferencesp[1]^[b,2]:5,' ',
  1147.      hreferencesp[2]^[b,1]:5,' ',hreferencesp[2]^[b,2]:5,' ',
  1148.      hreferencesp[3]^[b,1]:5,' ',hreferencesp[3]^[b,2]:5,' ',
  1149.      hreferencesp[4]^[b,1]:5,' ',hreferencesp[4]^[b,2]:5);
  1150.  
  1151.   if hasheq(hmessageidsp^[b],hreferencesp[1]^[a]) then
  1152.     writeln('#',b,' ',filenamesp^[b],' before #',a,' ',filenamesp^[a])
  1153.   else if hasheq(hmessageidsp^[b],hreferencesp[2]^[a]) then
  1154.     writeln('#',b,' ',filenamesp^[b],' before #',a,' ',filenamesp^[a])
  1155.   else if hasheq(hmessageidsp^[b],hreferencesp[3]^[a]) then
  1156.     writeln('#',b,' ',filenamesp^[b],' before #',a,' ',filenamesp^[a])
  1157.   else if hasheq(hmessageidsp^[b],hreferencesp[4]^[a]) then
  1158.     writeln('#',b,' ',filenamesp^[b],' before #',a,' ',filenamesp^[a])
  1159.   else
  1160.     writeln('#',b,' ',filenamesp^[b],' not refd by #',a,' ',filenamesp^[a]);
  1161.  
  1162.   
  1163.   if hasheq(hmessageidsp^[a],hreferencesp[1]^[b]) then
  1164.     writeln('#',a,' ',filenamesp^[a],' before #',b,' ',filenamesp^[b])
  1165.   else if hasheq(hmessageidsp^[a],hreferencesp[2]^[b]) then
  1166.     writeln('#',a,' ',filenamesp^[a],' before #',b,' ',filenamesp^[b])
  1167.   else if hasheq(hmessageidsp^[a],hreferencesp[3]^[b]) then
  1168.     writeln('#',a,' ',filenamesp^[a],' before #',b,' ',filenamesp^[b])
  1169.   else if hasheq(hmessageidsp^[a],hreferencesp[4]^[b]) then
  1170.     writeln('#',a,' ',filenamesp^[a],' before #',b,' ',filenamesp^[b])
  1171.   else
  1172.     writeln('#',a,' ',filenamesp^[a],' not refd by #',b,' ',filenamesp^[b]);
  1173.  
  1174.   end;
  1175.  
  1176. {$endif}
  1177.  
  1178.   if hasheq(articles[b]^.hmessageid,articles[a]^.hreferences[1]) then
  1179.     result := false
  1180.   else if hasheq(articles[b]^.hmessageid,articles[a]^.hreferences[2]) then
  1181.     result := false
  1182.   else if hasheq(articles[b]^.hmessageid,articles[a]^.hreferences[3]) then
  1183.     result := false
  1184.   else if hasheq(articles[b]^.hmessageid,articles[a]^.hreferences[4]) then
  1185.     result := false
  1186.   else
  1187.     if not hasheq(articles[a]^.hmessageid,articles[b]^.hreferences[1]) then
  1188.       if not hasheq(articles[a]^.hmessageid,articles[b]^.hreferences[2]) then
  1189.         if not hasheq(articles[a]^.hmessageid,articles[b]^.hreferences[3]) then
  1190.           if not hasheq(articles[a]^.hmessageid,articles[b]^.hreferences[4]) then
  1191.             begin
  1192.  
  1193. {no conclusive proof - just guess}
  1194.  
  1195.               if articles[a]^.date>articles[b]^.date then
  1196.                 result := false
  1197.               else if articles[a]^.date=articles[b]^.date then
  1198.                 if
  1199.                  (articles[a]^.indents and $f)
  1200.                  >
  1201.                  (articles[b]^.indents and $f) then
  1202.                   result := false;
  1203.             end;
  1204.  
  1205. {$ifdef testsort}
  1206.   if showdebug('sort') then
  1207.     begin
  1208.       write('firstartfirst(',a,'(',articles[a]^.filename:5,')',',',
  1209.        b,'(',articles[b]^.filename:5,')',')=');
  1210.       if result then writeln('true') else writeln('false');
  1211. {$ifdef pauseintestsort}
  1212.       xwrites('pausing...');
  1213.       xwritelns(xreadkey);
  1214. {$endif}
  1215.     end;
  1216.   
  1217. {$endif}
  1218.  
  1219.   firstartfirst := result;
  1220. end;
  1221.  
  1222. {need to use an ACTIVE file on those which have them}
  1223. function isavalidgroup;
  1224.  
  1225. begin
  1226.   isavalidgroup := (getgroupdir(group)<>'');
  1227. end;
  1228.  
  1229. function wafflefogetgroupdir(group: string; forumset: string): string;
  1230.  
  1231. var
  1232.   result: string;
  1233.   infilen: string;
  1234.   infile: text;
  1235.   s: string;
  1236.   foundgroup: boolean;
  1237.   default: string;
  1238.   defaultdir: string;
  1239.  
  1240. begin
  1241.   result := '';
  1242.  
  1243.   foundgroup := false;
  1244.   default := '';
  1245.  
  1246.   oldfilemode := filemode;
  1247.   if not nofilemode then
  1248.     filemode := $40;   {read only, deny none}
  1249.  
  1250.   infilen := configdir+'\system\'+forumset;
  1251.  
  1252.   safereset(infile,infilen);
  1253.  
  1254.   if fileresult=0 then
  1255.     begin
  1256.       while not foundgroup and not eof(infile) do
  1257.         begin
  1258.           readln(infile,s);
  1259.           foundgroup := (getfirstw(s)=group);
  1260.           if pos('/dir=',s)>0 then
  1261.             begin
  1262.               if getfirstw(s)=group then
  1263.                 begin
  1264.                   result := trim(ltrim(copy(s,pos('/dir=',s)+5,255)));
  1265.                   result := unquote(getfirstw(unslash(result)));
  1266.                 end
  1267.               else if getfirstw(s)='DEFAULT' then
  1268.                 default := s;
  1269.             end;
  1270.         end;
  1271.       close(infile);
  1272.     end;
  1273.  
  1274.   filemode := oldfilemode;
  1275.  
  1276.   if (result='') and (default<>'') and foundgroup then
  1277.     begin
  1278.  
  1279.       defaultdir := trim(ltrim(copy(default,pos('/dir=',default)+5,255)));
  1280.       defaultdir := unquote(getfirstw(unslash(defaultdir)));
  1281.  
  1282. {waffle treats /dir=x: to mean /dir=x:\ anyway}
  1283.  
  1284.       defaultdir := withbackslash(defaultdir);
  1285.       result := defaultdir+pathizegroup(group,noelevenchars);
  1286.  
  1287.     end;
  1288.  
  1289.   wafflefogetgroupdir := result;
  1290. end;
  1291.  
  1292. function secondarygetgroupdir(group: string): string;
  1293.  
  1294. var
  1295.   result: string;
  1296.   forumset: string;
  1297.   mungedl: string;
  1298.  
  1299. begin
  1300.   result := '';
  1301.  
  1302.   if (xiface=ifacewaffle) or (xiface=ifaceuufree) then
  1303.     begin
  1304.       mungedl := forumsetl;
  1305.       while (result='') and (mungedl<>'') do
  1306.         begin
  1307.           forumset := chopfirstw(mungedl);
  1308.           result := wafflefogetgroupdir(group,forumset);
  1309.         end;
  1310.     end
  1311.   else if xiface=ifaceuupc then
  1312.     begin
  1313.       result := withbackslash(getconfig('newsroot'))+
  1314.        pathizegroup(group,yeselevenchars);
  1315.     end;
  1316.  
  1317.   secondarygetgroupdir := result;
  1318. end;
  1319.  
  1320. function getgroupdir;
  1321.  
  1322. var
  1323.   result: string;
  1324.   nonprefix: string;
  1325.   partialprefix: string;
  1326.   i: integer;
  1327.  
  1328. begin
  1329.   result := '';
  1330.  
  1331.   if ismailgroup(group) then
  1332.     begin
  1333.  
  1334. {partialprefix is mailprefix without the `.userid' bits}
  1335.  
  1336.       partialprefix := copy(group,1,length(mailprefix)-1-length(userid));
  1337.  
  1338.       if group=mailprefix then
  1339.         begin
  1340.  
  1341. {look for just partialprefix, and add individual user ids on after}
  1342.  
  1343.           result := secondarygetgroupdir(partialprefix);
  1344.           if result<>'' then
  1345.             result := withbackslash(result)+userid;
  1346.         end
  1347.  
  1348.       else
  1349.  
  1350.         begin
  1351.  
  1352. {must be a folder}
  1353.  
  1354. {look for user's home mail directory, then add folders onto end}
  1355.  
  1356.           nonprefix := copy(group,length(mailprefix)+2,255);  { lose the . }
  1357.  
  1358.           nonprefix := crepl(nonprefix,'.','\');
  1359.           result := getgroupdir(partialprefix);
  1360.           if result<>'' then
  1361.             result := withbackslash(result)+userid+'\'+nonprefix;
  1362.         end;
  1363.  
  1364.     end;
  1365.  
  1366.   if result='' then
  1367.     result := secondarygetgroupdir(group);
  1368.  
  1369.   getgroupdir := result;
  1370. end;
  1371.  
  1372. {}{}{}{} {need to make sure it's not inside some option's path}
  1373.  
  1374. function fogroupsattr(group: string; attr: string; forumset: string): string;
  1375.  
  1376. var
  1377.   result: string;
  1378.   infilen: string;
  1379.   infile: text;
  1380.   s: string;
  1381.   foundgroup: boolean;
  1382.   default: string;
  1383.  
  1384. begin
  1385.   result := '';
  1386.  
  1387.   foundgroup := false;
  1388.   default := '';
  1389.  
  1390.   oldfilemode := filemode;
  1391.   if not nofilemode then
  1392.     filemode := $40;   {read only, deny none}
  1393.  
  1394.   infilen := configdir+'\system\'+forumset;
  1395.  
  1396.   safereset(infile,infilen);
  1397.  
  1398.   if fileresult=0 then
  1399.     begin
  1400.       while not foundgroup and not eof(infile) do
  1401.         begin
  1402.           readln(infile,s);
  1403.           foundgroup := (getfirstw(s)=group);
  1404.           if pos(attr,s)>0 then
  1405.             begin
  1406.               if foundgroup then
  1407.                 result := 
  1408.                  getfirstw(trim(ltrim(copy(s,pos(attr,s)+length(attr),255))))
  1409.               else if getfirstw(s)='DEFAULT' then
  1410.                 default := s;
  1411.             end;
  1412.         end;
  1413.       close(infile);
  1414.     end;
  1415.  
  1416.   filemode := oldfilemode;
  1417.  
  1418.   if (result='') and (default<>'') and foundgroup then
  1419.     result :=
  1420.      getfirstw(trim(ltrim(copy(default,pos(attr,default)+length(attr),255))));
  1421.  
  1422.   fogroupsattr := result;
  1423. end;
  1424.  
  1425. function groupsattr;
  1426.  
  1427. var
  1428.   result: string;
  1429.   forumset: string;
  1430.   mungedl: string;
  1431.  
  1432. begin
  1433.   result := '';
  1434.  
  1435.   if (xiface=ifacewaffle) or (xiface=ifaceuufree) then
  1436.     begin
  1437.       mungedl := forumsetl;
  1438.       while (result='') and (mungedl<>'') do
  1439.         begin
  1440.           forumset := chopfirstw(mungedl);
  1441.           result := fogroupsattr(group,attr,forumset);
  1442.         end;
  1443.     end;
  1444.  
  1445.   groupsattr := result;
  1446. end;
  1447.  
  1448. {}{}{}{} {need to make sure it's not inside some option's path}
  1449.  
  1450. function fogroupbattr(group: string; attr: string; forumset: string): boolean;
  1451.  
  1452. var
  1453.   result: boolean;
  1454.   infilen: string;
  1455.   infile: text;
  1456.   s: string;
  1457.   foundgroup: boolean;
  1458.   default: string;
  1459.  
  1460. begin
  1461.   result := false;
  1462.  
  1463.   foundgroup := false;
  1464.   default := '';
  1465.  
  1466.   oldfilemode := filemode;
  1467.   if not nofilemode then
  1468.     filemode := $40;   {read only, deny none}
  1469.  
  1470.   infilen := configdir+'\system\'+forumset;
  1471.  
  1472.   safereset(infile,infilen);
  1473.  
  1474.   if fileresult=0 then
  1475.     begin
  1476.       while not foundgroup and not eof(infile) do
  1477.         begin
  1478.           readln(infile,s);
  1479.           foundgroup := (getfirstw(s)=group);
  1480.           if pos(attr,s)>0 then
  1481.             begin
  1482.               if foundgroup then
  1483.                 result := true
  1484.               else if getfirstw(s)='DEFAULT' then
  1485.                 default := s;
  1486.             end;
  1487.         end;
  1488.       close(infile);
  1489.     end;
  1490.  
  1491.   filemode := oldfilemode;
  1492.  
  1493.   if (default<>'') and foundgroup then
  1494.     result := true;
  1495.  
  1496.   fogroupbattr := result;
  1497. end;
  1498.  
  1499. function groupbattr;
  1500.  
  1501. var
  1502.   result: boolean;
  1503.   forumset: string;
  1504.   mungedl: string;
  1505.  
  1506. begin
  1507.   result := false;
  1508.  
  1509.   if (xiface=ifacewaffle) or (xiface=ifaceuufree) then
  1510.     begin
  1511.       mungedl := forumsetl;
  1512.       while not result and (mungedl<>'') do
  1513.         begin
  1514.           forumset := chopfirstw(mungedl);
  1515.           result := fogroupbattr(group,attr,forumset);
  1516.         end;
  1517.     end;
  1518.  
  1519.   groupbattr := result;
  1520. end;
  1521.  
  1522. function fogroupdesc(group: string; forumset: string): string;
  1523.  
  1524. var
  1525.   result: string;
  1526.   infilen: string;
  1527.   infile: text;
  1528.   s: string;
  1529.   foundgroup: boolean;
  1530.  
  1531. begin
  1532.   result := '';
  1533.  
  1534.   foundgroup := false;
  1535.  
  1536.   oldfilemode := filemode;
  1537.   if not nofilemode then
  1538.     filemode := $40;   {read only, deny none}
  1539.  
  1540.   infilen := configdir+'\words\'+forumset;
  1541.  
  1542.   safereset(infile,infilen);
  1543.  
  1544.   if fileresult=0 then
  1545.     begin
  1546.       while not foundgroup and not eof(infile) do
  1547.         begin
  1548.           readln(infile,s);
  1549.           foundgroup := (chopfirstw(s)=group);
  1550.           if foundgroup then
  1551.             result := s;
  1552.         end;
  1553.       close(infile);
  1554.     end;
  1555.  
  1556.   filemode := oldfilemode;
  1557.  
  1558.   fogroupdesc := result;
  1559. end;
  1560.  
  1561. function groupdesc(group: string): string;
  1562.  
  1563. var
  1564.   result: string;
  1565.   forumset: string;
  1566.   mungedl: string;
  1567.  
  1568. begin
  1569.   result := '';
  1570.  
  1571.   if ismailgroup(group) then
  1572.     result := 'mail folder';
  1573.  
  1574.   if (xiface=ifacewaffle) or (xiface=ifaceuufree) then
  1575.     begin
  1576.       mungedl := forumsetl;
  1577.       while (result='') and (mungedl<>'') do
  1578.         begin
  1579.           forumset := chopfirstw(mungedl);
  1580.           result := fogroupdesc(group,forumset);
  1581.         end;
  1582.     end;
  1583.  
  1584.   if result='' then
  1585.     result := '(unknown description)';
  1586.  
  1587.   groupdesc := result;
  1588. end;
  1589.  
  1590. function sourcedesc;
  1591.  
  1592. begin
  1593.   if sourcekind=sourcegroup then
  1594.     sourcedesc := groupdesc(source)
  1595.   else if sourcekind=sourcedir then
  1596.     sourcedesc := 'directory'
  1597.   else if sourcekind=sourcefolder then
  1598.     sourcedesc := 'folder'
  1599.   else
  1600.     sourcedesc := '(internal error)'
  1601. end;
  1602.  
  1603. function getnextgroup: string;
  1604.  
  1605. var
  1606.   foundgroup: string;
  1607.   result: string;
  1608.  
  1609. begin
  1610.  
  1611. {}{} {this should use joinedgroups[] if possible}
  1612.  
  1613.   result := '';
  1614.   reset(joinf);
  1615.   foundgroup := '';
  1616.  
  1617.   if not eof(joinf) then
  1618.     begin
  1619.       if currsource='' then
  1620.         begin
  1621.           readln(joinf,foundgroup);
  1622.           result := getfirstw(foundgroup);
  1623.         end
  1624.       else
  1625.         begin
  1626.           while not eof(joinf) and (foundgroup<>currsource) do
  1627.             begin
  1628.               readln(joinf,foundgroup);
  1629.               foundgroup := getfirstw(foundgroup);
  1630.             end;
  1631.  
  1632. {if we were reading a group we weren't joined to, restart from top}
  1633.           if foundgroup<>currsource then
  1634.             reset(joinf);
  1635.  
  1636.           if not eof(joinf) then
  1637.             begin
  1638.               readln(joinf,foundgroup);
  1639.               result := getfirstw(foundgroup);
  1640.             end;
  1641.         end;
  1642.     end;
  1643.  
  1644.   getnextgroup := result;
  1645. end;
  1646.  
  1647. function importantgroup;
  1648.  
  1649. var
  1650.   result: boolean;
  1651.  
  1652. begin
  1653.   result := 
  1654.    (copy(group,1,14)='news.announce.') or
  1655.    ((numoccur('.',group)=1) and (right(group,8)='.answers'));
  1656.   importantgroup := result;
  1657. end;
  1658.  
  1659. function alreadyseen;
  1660.  
  1661. var
  1662.   result: boolean;
  1663.   i: integer;
  1664.   newsglist: string;
  1665.   found: boolean;
  1666.  
  1667. begin
  1668.   result := false;
  1669.  
  1670.   if (currsource<>'control') and (currsource<>'monitor') and
  1671.    not importantgroup(currsource) then
  1672.     begin
  1673.       newsglist := ','+newsgroups+',';
  1674.       if pos(','+currsource+',' , newsglist)<>0 then  {for news moved by hand}
  1675.         begin
  1676.           found := false;
  1677.           i := 1;
  1678.           while (i<numjoined) and not found do
  1679.             begin
  1680.               if not importantgroup(joinedgroups[i]) and
  1681.                (pos(','+joinedgroups[i]+',',newsglist)<>0) then
  1682.                 begin
  1683.                   found := true;
  1684.                   result := (joinedgroups[i]<>currsource);
  1685.                 end;
  1686.               inc(i);
  1687.             end;
  1688.         end;
  1689.     end;
  1690.  
  1691.   alreadyseen := result;
  1692. end;
  1693.  
  1694. function getpwinfo;
  1695.  
  1696. begin
  1697.   getpwinfo := getpwinfoforuser(field164,field165,fieldunix,userid);
  1698. end;
  1699.  
  1700. function getpwinfoforuser;
  1701.  
  1702. var
  1703.   result: string;
  1704.  
  1705. begin
  1706.   result := '{internal error}';
  1707.  
  1708.   if xiface=ifaceuufree then
  1709.     result := getpwinfounixforuser(fieldunix,someuser)
  1710.   else if ifaceversion=ifaceversionunix then
  1711.     result := getpwinfounixforuser(fieldunix,someuser)
  1712.   else if ifaceversion='1.64' then
  1713.     result := getpwinfo164foruser(field164,someuser)
  1714.   else if ifaceversion>='1.65' then
  1715.     result := getpwinfo165foruser(field165,someuser)
  1716.   else
  1717.     result := '{unknown ifaceversion: '+ifaceversion+'}';
  1718.  
  1719.   getpwinfoforuser := result;
  1720. end;
  1721.  
  1722. function getpwinfo164foruser;
  1723.  
  1724. const
  1725.   passwordblocksize=256;
  1726.  
  1727. type
  1728.   passwordbuft=array[1..passwordblocksize] of char;
  1729.  
  1730. var
  1731.   result: string;
  1732.   passwordbuf: passwordbuft;
  1733.   passwordf: file;
  1734.   found: boolean;
  1735.  
  1736. function passwordentry164(fieldnum: integer): string;
  1737.  
  1738. var
  1739.   i: integer;
  1740.   lfs: integer;
  1741.   result: string;
  1742.  
  1743. begin
  1744.   result := '';
  1745.   lfs := 0;
  1746.   for i := 1 to passwordblocksize do
  1747.     begin
  1748.       if passwordbuf[i]=lf then
  1749.         inc(lfs);
  1750.       if (lfs=fieldnum) and (passwordbuf[i]<>lf) then
  1751.         result := result+passwordbuf[i];
  1752.     end;
  1753.   passwordentry164 := result;
  1754. end;
  1755.  
  1756. begin
  1757.   result := '';
  1758.   found := false;
  1759.  
  1760.   oldfilemode := filemode;
  1761.   if not nofilemode then
  1762.     filemode := $40;   {read only, deny none}
  1763.  
  1764.   assign(passwordf,configdir+'\admin\'+'password');
  1765.   {$I-}
  1766.   reset(passwordf,1);
  1767.   {$I+}
  1768.  
  1769.   if ioresult=0 then
  1770.     begin
  1771.       blockread(passwordf,passwordbuf,passwordblocksize);
  1772.       while not found and not eof(passwordf) do
  1773.         begin
  1774.           blockread(passwordf,passwordbuf,passwordblocksize);
  1775.           if passwordentry164(0)=someuser then
  1776.             begin
  1777.               result := passwordentry164(field);
  1778.               found := true;
  1779.             end;
  1780.         end;
  1781.       close(passwordf);
  1782.     end;
  1783.  
  1784.   filemode := oldfilemode;
  1785.  
  1786.   getpwinfo164foruser := result;
  1787. end;
  1788.  
  1789. function getpwinfo165foruser;
  1790.  
  1791. const
  1792.   passwordblocksize=1024;
  1793.  
  1794. type
  1795.   passwordbuft=array[1..passwordblocksize] of char;
  1796.  
  1797. var
  1798.   result: string;
  1799.   passwordbuf: passwordbuft;
  1800.   passwordf: file;
  1801.   found: boolean;
  1802.  
  1803. function fieldsize165(fieldnum: integer): integer;
  1804.  
  1805. var
  1806.   result: integer;
  1807.  
  1808. begin
  1809.   result := 0;
  1810.   case fieldnum of
  1811.     1: result := 12; {name}
  1812.     2: result := 12; {pass}
  1813.     3: result := 24; {identity}         {I'm told _this_ is the one for %W}
  1814.     4: result := 24; {realname}
  1815.     5: result := 22; {phone}
  1816.     6: result := 40; {shell}
  1817.     7: result := 10; {editor}
  1818.     8: result := 10; {console}
  1819.     9: result := 66; {comment}
  1820.    10: result := 8;  {level}
  1821.    11: result := 10; {terminal}
  1822.    12: result := 10; {language}
  1823.    13: result := 10; {suite}
  1824.    14: result := 10; {account}
  1825.    15: result := 12; {group}
  1826.    16: result := 2;  {access}
  1827.    17: result := 8;  {priv}
  1828.    18: result := 12; {age}
  1829.    19: result := 2;  {color}
  1830.    20: result := 5;  {encryption}
  1831.    21: result := 8;  {help}
  1832.   end;
  1833.   fieldsize165 := result;
  1834. end;
  1835.  
  1836. function fieldstart165(fieldnum: integer): integer;
  1837.  
  1838. var
  1839.   i: integer;
  1840.   result: integer;
  1841.  
  1842. begin
  1843.   result := 0;
  1844.   for i := 1 to fieldnum-1 do
  1845.     inc(result,fieldsize165(i));
  1846.   fieldstart165 := result;
  1847. end;
  1848.  
  1849. function passwordentry165(fieldnum: integer): string;
  1850.  
  1851. var
  1852.   result: string;
  1853.   i: integer;
  1854.   start: integer;
  1855.   size: integer;
  1856.   ch: char;
  1857.   done: boolean;
  1858.  
  1859. begin
  1860.   result := '';
  1861.  
  1862.   size := fieldsize165(fieldnum);
  1863.   start := fieldstart165(fieldnum);
  1864.   done := false;
  1865.   i := 1;
  1866.   while (i<=size) and not done do
  1867.     begin
  1868.       ch := passwordbuf[i+start];
  1869.       if ch=#0 then
  1870.         done := true
  1871.       else
  1872.         result := result+ch;
  1873.       inc(i);
  1874.     end;
  1875.  
  1876.   passwordentry165 := result;
  1877. end;
  1878.  
  1879. begin
  1880.   result := '';
  1881.   found := false;
  1882.  
  1883.   oldfilemode := filemode;
  1884.   if not nofilemode then
  1885.     filemode := $40;   {read only, deny none}
  1886.  
  1887.   assign(passwordf,configdir+'\admin\'+'password');
  1888.   {$I-}
  1889.   reset(passwordf,1);
  1890.   {$I-}
  1891.  
  1892.   if ioresult=0 then
  1893.     begin
  1894.       blockread(passwordf,passwordbuf,passwordblocksize);
  1895.       while not found and not eof(passwordf) do
  1896.         begin
  1897.           blockread(passwordf,passwordbuf,passwordblocksize);
  1898.           if passwordentry165(1)=someuser then
  1899.             begin
  1900.               result := passwordentry165(field);
  1901.               found := true;
  1902.             end;
  1903.         end;
  1904.       close(passwordf);
  1905.     end;
  1906.  
  1907.   filemode := oldfilemode;
  1908.  
  1909.   getpwinfo165foruser := result;
  1910. end;
  1911.  
  1912. function getpwinfounixforuser;
  1913.  
  1914. var
  1915.   result: string;
  1916.   passwordf: text;
  1917.   passwordline: string;
  1918.   found: boolean;
  1919.   chopfieldcount: integer;
  1920.  
  1921. begin
  1922.   result := '';
  1923.   found := false;
  1924.  
  1925.   oldfilemode := filemode;
  1926.   if not nofilemode then
  1927.     filemode := $40;   {read only, deny none}
  1928.  
  1929.   safereset(passwordf,configdir+'\etc\'+'passwd');
  1930.  
  1931.   if fileresult=0 then
  1932.     begin
  1933.       while not found and not eof(passwordf) do
  1934.         begin
  1935.           readln(passwordf,passwordline);
  1936.           if copy(passwordline,1,length(someuser)+1)=someuser+':' then
  1937.             begin
  1938.               for chopfieldcount := 1 to field-1 do
  1939.                 passwordline :=
  1940.                  copy(passwordline,pos(':',passwordline)+1,255);
  1941.  
  1942.               passwordline := passwordline+':';
  1943.               result := copy(passwordline,1,pos(':',passwordline)-1);
  1944.  
  1945.               found := true;
  1946.             end;
  1947.         end;
  1948.       close(passwordf);
  1949.     end;
  1950.  
  1951.   filemode := oldfilemode;
  1952.  
  1953.   getpwinfounixforuser := result;
  1954. end;
  1955.  
  1956. {someuser needs to be lowercase for waffle, and probably uupc}
  1957. function getfullnameforuser(someuser: string): string;
  1958.  
  1959. var
  1960.   result: string;
  1961.  
  1962. begin
  1963.   result := '';
  1964.  
  1965.   if (result='') and ((xiface=ifacewaffle) or (xiface=ifaceuufree)) then
  1966.     result := trim(getpwinfoforuser(5,3,5,someuser));
  1967.  
  1968. {uupc only has full-name info for current user}
  1969.   if (result='') and (someuser=userid) and (xiface=ifaceuupc) then
  1970.     result := getconfig('fullname');
  1971.  
  1972. {environment only has full-name info for current user}
  1973.   if (result='') and (someuser=userid) and not ignoreenvironment then
  1974.     result := trim(ununderscore(getenv('FULLNAME')));
  1975.  
  1976.   getfullnameforuser := result;
  1977. end;
  1978.  
  1979. function extwafexpand;
  1980.  
  1981. var
  1982.   result: string;
  1983.   tempint: integer;
  1984.   tempchar: char;
  1985.  
  1986. begin
  1987.   if pos('%',s)=0 then
  1988.     result := s
  1989.   else
  1990.     begin
  1991.       result := '';
  1992.       tempint := 1;
  1993.       while tempint<=length(s) do
  1994.         begin
  1995.           if (s[tempint]<>'%') or (tempint=length(s)) then
  1996.             result := result+s[tempint]
  1997.           else
  1998.             begin
  1999.               inc(tempint);
  2000.               tempchar := s[tempint];
  2001.               case tempchar of
  2002.                 '%': result := result+'%';
  2003.                 '^': result := result+'^';
  2004.                 'A': result := result+userid;
  2005.                 'W': result := result+fullname;
  2006.                 'n': result := result+fqdn;
  2007.                 'u': result := result+uucpname;
  2008.                 'F': result := result+trim(getpwinfo(5,4,5));
  2009.                 'i': result := result+percenti;
  2010.  
  2011.               {%f is non-standard!}
  2012.                 'f': result := result+percentf;
  2013.  
  2014.               {%_ will be in waffle 1.66}
  2015.                 '_': result := result+crepl(fullname,' ','_');
  2016.  
  2017.                 else result := result+'{unknown flag %'+tempchar+'}';
  2018.               end;
  2019.             end;
  2020.  
  2021.           inc(tempint);
  2022.         end;
  2023.     end;
  2024.  
  2025.   extwafexpand := result;
  2026. end;
  2027.  
  2028. function wafexpand;
  2029.  
  2030. begin
  2031.   wafexpand := extwafexpand(s,'{error}','{error}');
  2032. end;
  2033.  
  2034. function makesame;
  2035.  
  2036. var
  2037.   result: boolean;
  2038.  
  2039. begin
  2040.   result := false;
  2041.  
  2042.   if copy(s,1,length(prefix))=prefix then
  2043.     if s<>prefix+shouldbe then
  2044.       begin
  2045.         s := prefix+shouldbe;
  2046.         result := true;
  2047.       end;
  2048.  
  2049.   makesame := result;
  2050. end;
  2051.  
  2052. function chopfirstaddr;
  2053.  
  2054. var
  2055.   result: string;
  2056.   inquote: boolean;
  2057.   charlookingat: integer;
  2058.   done: boolean;
  2059.  
  2060. begin
  2061.   result := '';
  2062.   inquote := false;
  2063.  
  2064.   charlookingat := 1;
  2065.  
  2066.   done := false;
  2067.   while not done do
  2068.     begin
  2069.       if charlookingat>length(addresses) then
  2070.         begin
  2071.  
  2072. {only one address in the list}
  2073.           done := true;
  2074.           result := addresses;
  2075.           addresses := '';
  2076.         end
  2077.       else if addresses[charlookingat]='"' then
  2078.         begin
  2079.  
  2080. {it's a quote}
  2081.           inquote := not inquote;
  2082.         end
  2083.       else if (addresses[charlookingat]=',') and not inquote then
  2084.         begin
  2085.  
  2086. {it's a non-quoted separator -- remove the separator and split}
  2087.           done := true;
  2088.           result := copy(addresses,1,charlookingat-1);
  2089.           addresses := copy(addresses,charlookingat+1,255);
  2090.         end;
  2091.  
  2092.       inc(charlookingat);
  2093.     end;
  2094.  
  2095.   if inquote then
  2096.     begin
  2097.       { there's definitely an error if the quote never got closed }
  2098.       {}{}{}{}
  2099.       writeln('error -- " never got closed');
  2100.     end;
  2101.  
  2102.   result := trim(ltrim(result));
  2103.  
  2104.   chopfirstaddr := result;
  2105. end;
  2106.  
  2107. function expandonemail;
  2108.  
  2109. var
  2110.   result: string;
  2111.   newaddressfn: string;
  2112.   newaddressf: text;
  2113.   changed: boolean;
  2114.   s: string;
  2115.  
  2116. begin
  2117.   result := address;
  2118.  
  2119.   changed := false;
  2120.   if (pos('@',address)=0) and
  2121.    (pos('!',address)=0) and
  2122.    (pos(' ',address)=0) then
  2123.     begin
  2124.  
  2125.       if (xiface=ifacewaffle) or (xiface=ifaceuufree) then
  2126.         newaddressfn := configdir+'\system\'+'aliases'
  2127.       else if xiface=ifaceuupc then
  2128.         begin
  2129.           newaddressfn := unslash(getconfig('aliases'));
  2130.           if newaddressfn='' then
  2131.             newaddressfn := home+'\aliases'
  2132.           else if numoccur('\',newaddressfn)=0 then
  2133.             newaddressfn := withbackslash(home)+newaddressfn;
  2134.         end
  2135.       else
  2136.         newaddressfn := 'aliases';
  2137.  
  2138.       oldfilemode := filemode;
  2139.       if not nofilemode then
  2140.         filemode := $40;   {read only, deny none}
  2141.  
  2142.       safereset(newaddressf,newaddressfn);
  2143.  
  2144.       if fileresult=0 then
  2145.         begin
  2146.           while not changed and not eof(newaddressf) do
  2147.             begin
  2148.               readln(newaddressf,s);
  2149.               if lower(chopfirstw(s))=lower(address) then
  2150.                 begin
  2151.                   changed := true;
  2152.                   result := s;
  2153.                 end;
  2154.             end;
  2155.           close(newaddressf);
  2156.         end;
  2157.  
  2158.       if not changed then
  2159.         begin
  2160.           newaddressfn := home+'\aliases';
  2161.  
  2162.           safereset(newaddressf,newaddressfn);
  2163.  
  2164.           if fileresult=0 then
  2165.             begin
  2166.               while not changed and not eof(newaddressf) do
  2167.                 begin
  2168.                   readln(newaddressf,s);
  2169.                   if lower(chopfirstw(s))=lower(address) then
  2170.                     begin
  2171.                       changed := true;
  2172.                       result := s;
  2173.                     end;
  2174.                 end;
  2175.               close(newaddressf);
  2176.             end;
  2177.         end;
  2178.  
  2179.       if not changed then
  2180.         begin
  2181.  
  2182. {make sure no chance of security hole - no .. or \ or / or : in address}
  2183.  
  2184. {don't need to make sure it's not a device - last part of name is always}
  2185. {the string 'forward'}
  2186.  
  2187.          if (pos('/',address)=0) and (pos(':',address)=0) and
  2188.           (pos('\',address)=0) and (pos('..',address)=0) then
  2189.            begin
  2190.              newaddressfn := withbackslash(userdir)+address+'\forward';
  2191.  
  2192.              safereset(newaddressf,newaddressfn);
  2193.  
  2194.              if fileresult=0 then
  2195.                begin
  2196.                  if not eof(newaddressf) then
  2197.                    begin
  2198.                      changed := true;
  2199.                      readln(newaddressf,result);
  2200.                    end;
  2201.                  close(newaddressf);
  2202.                end;
  2203.            end;
  2204.         end;
  2205.       filemode := oldfilemode;
  2206.     end;
  2207.  
  2208.   expandonemail := result;
  2209. end;
  2210.  
  2211. function expandmail;
  2212.  
  2213. var
  2214.   result: string;
  2215.   separator: string;
  2216.   mangledaddresses: string;
  2217.   oneaddress: string;
  2218.   onebareaddress: string;
  2219.   alladdresses: string;
  2220.  
  2221. begin
  2222.   result := '';
  2223.  
  2224.   alladdresses := addresses;
  2225.  
  2226. {}{} {not perfect if you have quoting, but fairly good considering it's}
  2227.      {illegal to begin with}
  2228.  
  2229. {change `chris pat' into `chris, pat' for expansion}
  2230. {change `chris pat,sam' into `chris, pat, sam' for expansion}
  2231.  
  2232.   if (pos('@',alladdresses)=0) and (pos('!',alladdresses)=0) and
  2233.    (pos('(',alladdresses)=0) and (pos('"',alladdresses)=0) then
  2234.     begin
  2235.       mangledaddresses := uncomma(alladdresses);
  2236.       alladdresses := '';
  2237.       separator := '';
  2238.       while mangledaddresses<>'' do
  2239.         begin
  2240.           oneaddress := chopfirstw(mangledaddresses);
  2241.           alladdresses := alladdresses+separator+oneaddress;
  2242.           separator := ', ';
  2243.         end;
  2244.     end;
  2245.  
  2246.   separator := '';
  2247.   mangledaddresses := alladdresses;
  2248.   while mangledaddresses<>'' do
  2249.     begin
  2250.       oneaddress := chopfirstaddr(mangledaddresses);
  2251.       onebareaddress := getfromaddr(oneaddress);
  2252.       result := result+separator+expandonemail(onebareaddress);
  2253.       separator := ', ';
  2254.     end;
  2255.  
  2256.   result := ltrim(trim(result));
  2257.  
  2258.   expandmail := result;
  2259. end;
  2260.  
  2261. function screenline;
  2262.  
  2263. begin
  2264.   screenline := trim(expand(s));
  2265. end;
  2266.  
  2267. function extonekey(highlight: boolean; prompt: string;
  2268.  validkeys: string): char;
  2269.  
  2270. var
  2271.   result: char;
  2272.   i: integer;
  2273.  
  2274. begin
  2275.   result := ' ';
  2276.  
  2277.   xclreolxy(1,lpp);
  2278.   if highlight then
  2279.     xwritehighlights(prompt)
  2280.   else
  2281.     xwrites(prompt);
  2282.  
  2283.   xwrites(' ');
  2284.   repeat
  2285.     result := xreadkey;
  2286.   until pos(result,validkeys)<>0;
  2287.  
  2288. {caller has to clear line after - might not want to right away}
  2289.   extonekey := result;
  2290. end;
  2291.  
  2292. function onekey;
  2293.  
  2294. begin
  2295.   onekey := extonekey(true,prompt,validkeys);
  2296. end;
  2297.  
  2298. function nonhighlightonekey;
  2299.  
  2300. begin
  2301.   nonhighlightonekey := extonekey(false,prompt,validkeys);
  2302. end;
  2303.  
  2304. function onekeydef;
  2305.  
  2306. var
  2307.   result: char;
  2308.   newprompt: string;
  2309.   newvalid: string;
  2310.  
  2311. begin
  2312.   newprompt := prompt+' ('+default+')';
  2313.   newvalid := validkeys+' '+chr(13);
  2314.   result := onekey(newprompt,newvalid);
  2315.  
  2316.   if result=' ' then
  2317.     result := default;
  2318.  
  2319.   if result=chr(13) then
  2320.     result := default;
  2321.  
  2322.   onekeydef := result;
  2323. end;
  2324.  
  2325. function ismailgroup;
  2326.  
  2327. begin
  2328.   ismailgroup := (copy(group,1,length(mailprefix))=mailprefix);
  2329. end;
  2330.  
  2331. function isnormalgroup;
  2332.  
  2333. begin
  2334.   isnormalgroup := not ismailgroup(group);
  2335. end;
  2336.  
  2337. function getsyscmd;
  2338.  
  2339. var
  2340.   result: string;
  2341.   infn: string;
  2342.   inf: text;
  2343.   s: string;
  2344.  
  2345. begin
  2346.   result := '';
  2347.  
  2348.   infn := withbackslash(configdir)+'extern'+'\'+'_system';
  2349.  
  2350.   safereset(inf,infn);
  2351.   if fileresult=0 then
  2352.     begin
  2353.       while not eof(inf) do
  2354.         begin
  2355.           readln(inf,s);
  2356.           s := ltrim(s);
  2357.           if getfirstw(s)=cmd then
  2358.             result := gettag('/command=',s);
  2359.         end;
  2360.     end;
  2361.  
  2362.   getsyscmd := result;
  2363. end;
  2364.  
  2365. function searchart;
  2366.  
  2367. var
  2368.   result: boolean;
  2369.   toofar: boolean;
  2370.   inf: text;
  2371.   inheaders: boolean;
  2372.   s: string;
  2373.   c: char;
  2374.   lineread: boolean;
  2375.   faqs: boolean;
  2376.  
  2377. begin
  2378.   result := false;
  2379.   faqs := (upsearchtext=faqcookie);
  2380.  
  2381.   safereset(inf,filename);
  2382.   if fileresult=0 then
  2383.     begin
  2384.       inheaders := true;
  2385.       toofar := false;
  2386.  
  2387.       while not eof(inf) and not result and not toofar do
  2388.         begin
  2389.           if crlf then
  2390.             readln(inf,s)
  2391.           else
  2392.             begin
  2393.               s := '';
  2394.               lineread := false;
  2395.  
  2396.               while not lineread do
  2397.                 begin
  2398.                   read(inf,c);
  2399.                   if c=lf then
  2400.                     lineread := true
  2401.                   else if c<>cr then
  2402.                     begin
  2403.                       s := s+c;
  2404.                       lineread := (length(s)>=255);
  2405.                     end;
  2406.                 end;
  2407.             end;
  2408.  
  2409.           if s='' then
  2410.             inheaders := false;
  2411.           s := upper(s);
  2412.  
  2413.           toofar := not inheaders and headersearch;
  2414.  
  2415.           if faqs then
  2416.             begin
  2417.               result := (pos('NEWS.ANSWERS',s)<>0) or
  2418.                 (pos('FAQ',s)<>0) or (pos('FREQUENTLY ASKED Q',s)<>0);
  2419.             end
  2420.           else if inheaders then
  2421.             begin
  2422.               if headersearch then
  2423.                 result := textintext(upsearchtext,s);
  2424.             end
  2425.           else
  2426.             begin
  2427.               if not headersearch then
  2428.                 result := textintext(upsearchtext,s);
  2429.             end;
  2430.         end;
  2431.  
  2432.       close(inf);
  2433.     end;
  2434.  
  2435.   searchart := result;
  2436. end;
  2437.  
  2438. function searchnov;
  2439.  
  2440. var
  2441.   result: boolean;
  2442.  
  2443. begin
  2444.   result := true;
  2445.   searchnov := result;
  2446. end;
  2447.  
  2448. function ismoderated;
  2449.  
  2450. var
  2451.   result: boolean;
  2452.  
  2453. begin
  2454.   result := false;
  2455.  
  2456.   if (xiface=ifacewaffle) or (xiface=ifaceuufree) then
  2457.     result := groupbattr(group,'/mod')
  2458.   else if xiface=ifaceuupc then
  2459.     result := {}{}{} false {need to handle this}
  2460.   else
  2461.     result := false;
  2462.  
  2463.   ismoderated := result;
  2464. end;
  2465.  
  2466. function isheaderinlist;
  2467.  
  2468. var
  2469.   result: boolean;
  2470.  
  2471. begin
  2472.   result := right(header,1)=':';
  2473.   if result then
  2474.     result := textintext(':'+upper(header),uheaderlist);
  2475.  
  2476.   isheaderinlist := result;
  2477. end;
  2478.  
  2479. function getaddressfromline(s: string): string;
  2480.  
  2481. var
  2482.   result: string;
  2483.  
  2484. begin
  2485.   result := wordwith('@',s);
  2486.  
  2487.   if result='' then
  2488.     result := wordwith('!',s);
  2489.  
  2490.   if (copy(result,1,1)='(') and (copy(result,length(result),1)=')') then
  2491.     result := copy(result,2,length(result)-2);
  2492.  
  2493.   if (copy(result,1,1)='<') and (copy(result,length(result),1)='>') then
  2494.     result := copy(result,2,length(result)-2);
  2495.  
  2496.   if copy(result,length(result),1)='.' then
  2497.     result := copy(result,1,length(result)-1);
  2498.  
  2499.   if copy(result,length(result),1)=',' then
  2500.     result := copy(result,1,length(result)-1);
  2501.  
  2502.   getaddressfromline := result;
  2503. end;
  2504.  
  2505. function isreasonableaddress(addr: string): boolean;
  2506.  
  2507. var
  2508.   result: boolean;
  2509.  
  2510. begin
  2511.   result := true;
  2512.  
  2513.   if (pos('!',addr)=0) and (pos('@',addr)=0) then
  2514.     result := false;
  2515.  
  2516.   if pos('@',addr)<>0 then
  2517.     if pos('.',addr)=0 then
  2518.       result := false;
  2519.  
  2520.   if pos('@.',addr)<>0 then
  2521.     result := false;
  2522.  
  2523.   isreasonableaddress := result;
  2524. end;
  2525.  
  2526. function nthlayout;
  2527.  
  2528. var
  2529.   result: layoutt;
  2530.   tempi: integer;
  2531.  
  2532. begin
  2533.   result := succ(layoutfirst);
  2534.  
  2535. {start at 2, since we already went 1 past the first}
  2536.   for tempi := 2 to whichlayout do
  2537.     begin
  2538.       if succ(result)<>layoutlast then
  2539.         result := succ(result);
  2540.     end;
  2541.  
  2542.   nthlayout := result;
  2543. end;
  2544.  
  2545. function isabreakline;
  2546.  
  2547. {either an empty line or all dashes}
  2548.  
  2549. var
  2550.   result: boolean;
  2551.   trimmeds: string;
  2552.   tempint: integer;
  2553.  
  2554. begin
  2555.   result := false;
  2556.   trimmeds := trim(ltrim(s));
  2557.  
  2558. {I realize the first is a special case of the second, but probably faster}
  2559.  
  2560.   if trimmeds='' then
  2561.     result := true
  2562.   else
  2563.     begin
  2564.       result := true;
  2565.       for tempint := 1 to length(trimmeds) do
  2566.         if trimmeds[tempint]<>'-' then
  2567.           result := false;
  2568.     end;
  2569.  
  2570.   isabreakline := result;
  2571. end;
  2572.  
  2573. function findproblemwithmessage;
  2574.  
  2575. var
  2576.   result: string;
  2577.   messagef: text;
  2578.   done: boolean;
  2579.   messageline: string;
  2580.   lineon: integer;
  2581.  
  2582. begin
  2583.   result := '';
  2584.  
  2585.   safereset(messagef,messagefn);
  2586.   if fileresult<>0 then
  2587.     result := 'could not open file!'
  2588.   else
  2589.     begin
  2590.       done := false;
  2591.       lineon := 0;
  2592.  
  2593.       while (result='') and not done do
  2594.         begin
  2595.  
  2596. {ran out of headers to check}
  2597.           if eof(messagef) then
  2598.             result := 'no body found (no empty line)'
  2599.           else
  2600.             begin
  2601.               readln(messagef,messageline);
  2602.               inc(lineon);
  2603.  
  2604. {once we hit the empty line, we know there's something past the headers}
  2605.               if messageline='' then
  2606.                 begin
  2607.                   done := true;
  2608.  
  2609. {make sure there's something IN the body!}
  2610.                   if eof(messagef) then
  2611.                     result := 'no body found (after empty line)'
  2612.                   else
  2613.                     begin
  2614.                       readln(messagef,messageline);
  2615.                       if messageline='-- ' then
  2616.                         result := 'no body found (just signature)';
  2617.                     end;
  2618.                 end
  2619.  
  2620. {all-blank lines are technically legal, but very dangerous to put in}
  2621.               else if trim(messageline)='' then
  2622.                 result := 'all-blank line needs to be empty instead'
  2623.  
  2624. {special-case for mail}
  2625.               else if (lineon=1) and (copy(messageline,1,5)='From ') then
  2626.                 result := ''
  2627.  
  2628. {check only non-continuation lines}
  2629.               else if messageline=ltrim(messageline) then
  2630.                 begin
  2631.                   if pos(':',messageline)=0 then
  2632.                     result :=
  2633.                      'invalid header line (no colon)  '+messageline
  2634.  
  2635.                   else if pos(' ',messageline)=0 then
  2636.                     result :=
  2637.                      'invalid header line (no space)  '+messageline
  2638.  
  2639.                   else if pos(' ',messageline)<pos(':',messageline) then
  2640.                     result :=
  2641.                      'invalid header line (space before colon)  '+messageline;
  2642.                 end;
  2643.             end;
  2644.         end;
  2645.  
  2646.       close(messagef);
  2647.     end;
  2648.  
  2649.   findproblemwithmessage := result;
  2650. end;
  2651.  
  2652. function toomuchquoting;
  2653.  
  2654. var
  2655.   result: boolean;
  2656.   totallines: longint;
  2657.   quotedlines: longint;
  2658.  
  2659.   messagef: text;
  2660.   messageline: string;
  2661.  
  2662.   attributionline: boolean;
  2663.   seenemptyline: boolean;
  2664.   seensigline: boolean;
  2665.  
  2666. begin
  2667.   result := false;
  2668.  
  2669.   attributionline := false;
  2670.  
  2671.   safereset(messagef,messagefn);
  2672.   if fileresult<>0 then
  2673.     result := true  {could not open file}
  2674.   else
  2675.     begin
  2676.       totallines := 0;
  2677.       quotedlines := 0;
  2678.  
  2679.       seenemptyline := false;
  2680.       seensigline := false;
  2681.  
  2682.       while not eof(messagef) do
  2683.         begin
  2684.           readln(messagef,messageline);
  2685.           if messageline='' then
  2686.             seenemptyline := true
  2687.           else if messageline='-- ' then
  2688.             seensigline := true;
  2689.  
  2690.           if seenemptyline and not seensigline then
  2691.             if messageline<>'' then
  2692.               begin
  2693.                 inc(totallines);
  2694.                 if copy(messageline,1,1)='>' then
  2695.                   inc(quotedlines);
  2696.                 if (totallines=1) and (quotedlines=0) then
  2697.                   attributionline := true;
  2698.               end;
  2699.         end;
  2700.  
  2701.       close(messagef);
  2702.  
  2703.       if (quotedlines>0) then
  2704.         begin
  2705.  
  2706. {ones with just quoted text}
  2707.           if totallines=quotedlines then
  2708.             result := true;
  2709.  
  2710. {ones with just the attribution line}
  2711.           if attributionline and (totallines=quotedlines+1) then
  2712.             result := true;
  2713.         end;
  2714.  
  2715.       if totallines>20 then  {don't check tiny messages}
  2716.         if quotedlines>2*totallines then
  2717.           result := true;
  2718.     end;
  2719.  
  2720.   toomuchquoting := result;
  2721. end;
  2722.  
  2723. function toolongline;
  2724.  
  2725. var
  2726.   result: boolean;
  2727.  
  2728.   messagef: text;
  2729.   messageline: string;
  2730.  
  2731.   seenblank: boolean;
  2732.  
  2733.   longlinechecknumber: integer;
  2734.  
  2735. begin
  2736.   result := false;
  2737.  
  2738.   safereset(messagef,messagefn);
  2739.  
  2740.   seenblank := false;
  2741.  
  2742.   for longlinechecknumber := 1 to 40 do
  2743.     if not result then
  2744.       if not eof(messagef) then
  2745.         begin
  2746.           read(messagef,messageline);
  2747.           if messageline='' then
  2748.             seenblank := true;
  2749.  
  2750.           if not eoln(messagef) then
  2751.             result := true;
  2752.  
  2753. {headers>80 chars are ok}
  2754.           if length(messageline)>maxlen then
  2755.             if seenblank then
  2756.               result := true;
  2757.  
  2758.           if not eof(messagef) then {a bit overcautious I think}
  2759.             readln(messagef);
  2760.         end;
  2761.  
  2762.   close(messagef);
  2763.  
  2764.   toolongline := result;
  2765. end;
  2766.  
  2767. function showdebug;
  2768.  
  2769. begin
  2770.   showdebug := isinlist('all',debuglist,':') or isinlist(s,debuglist,':');
  2771. end;
  2772.  
  2773. function unreadarticlesin;
  2774.  
  2775. var
  2776.   result: articlefilenametype;
  2777.   hasoverview: boolean;
  2778.   adir: string;
  2779.   morearticles: boolean;
  2780.   fileinfo: searchrec;
  2781.   anartnum: articlefilenametype;
  2782.   lastread: articlefilenametype;
  2783.  
  2784. begin
  2785.   result := 0;
  2786.  
  2787.   lastread := highestreadin(asource,sourcekind);
  2788.  
  2789.   hasoverview := false;
  2790.  
  2791. {note -- for mail groups, ignore the overview file}
  2792.  
  2793.   if sourcekind=sourcegroup then
  2794.     adir := getgroupdir(asource)
  2795.   else if sourcekind=sourcedir then
  2796.     adir := asource
  2797.   else if sourcekind=sourcefolder then
  2798.     adir := '\\\\invalid\\directory.specified\\\\';
  2799.  
  2800.   if not ismailgroup(asource) then
  2801.     begin
  2802.       overviewreset(adir);
  2803.       if fileresult=0 then
  2804.         hasoverview := true;
  2805.     end;
  2806.  
  2807. {}{}{}{} {the only thing that calls this can handle extra output here}
  2808. {}if hasoverview then xwritess('o',^H);
  2809.  
  2810.   if hasoverview then
  2811.     begin
  2812.       morearticles := not eofoverview;
  2813.     end
  2814.   else
  2815.     begin
  2816.       findfirst(withbackslash(adir)+articlefilenamepattern,archive,fileinfo);
  2817.       morearticles := (doserror=0);
  2818.     end;
  2819.  
  2820.   while morearticles do
  2821.     begin
  2822.       if hasoverview then
  2823.         begin
  2824.           anartnum := readoverviewfilenum;
  2825.           morearticles := not eofoverview;
  2826.         end
  2827.       else
  2828.         begin
  2829.           anartnum := atol(fileinfo.name);
  2830.           findnext(fileinfo);
  2831.           morearticles := (doserror=0);
  2832.         end;
  2833.  
  2834.       if anartnum>lastread then
  2835.         inc(result);
  2836.     end;
  2837.  
  2838.   if hasoverview then
  2839.     closeoverview;
  2840.  
  2841.   unreadarticlesin := result;
  2842. end;
  2843.  
  2844. function highestreadin;
  2845.  
  2846. var
  2847.   result: articlefilenametype;
  2848.   s: string;
  2849.  
  2850. begin
  2851.   result := 0;
  2852.  
  2853.   if sourcekind<>sourcegroup then
  2854.     result := 0
  2855.   else
  2856.     begin
  2857.       reset(joinf);
  2858.       result := impossiblylargeart;
  2859.       while (result=impossiblylargeart) and not eof(joinf) do
  2860.         begin
  2861.           readln(joinf,s);
  2862.           if getfirstw(s)=asource then
  2863.             result := getalreadyread(s);
  2864.         end;
  2865.     end;
  2866.  
  2867. { only needed for initial single-group stuff }
  2868. {
  2869.   if result=impossiblylargeart then
  2870.     begin
  2871.       xwritelnss('not joined to ',asource);
  2872.       shutdown(1);
  2873.     end;
  2874. }
  2875. { end of only needed part }
  2876.  
  2877.   highestreadin := result;
  2878. end;
  2879.  
  2880. function textintext;
  2881.  
  2882. begin
  2883.   if useregex then
  2884.     textintext := regexintext(asubtext,awholetext)
  2885.   else
  2886.     textintext := ( pos(asubtext,awholetext)<>0 );
  2887. end;
  2888.  
  2889.  
  2890.  
  2891. end.
  2892.