home *** CD-ROM | disk | FTP | other *** search
/ Super Net 1 / SUPERNET_1.iso / PC / OTROS / EXTRAS / UUCODE / UUCP / RSNU106A.ZIP / src / rusnfunc.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1993-12-05  |  37.3 KB  |  1,583 lines

  1. unit rusnfunc;
  2.  
  3. {
  4.  
  5. rusn-fun.pas - rusnews functions
  6.  
  7. also see rusngenf.pas - split off into a separate unit to get around code
  8.   segment size limitation
  9.  
  10. }
  11.  
  12. {$I rusn-def.pas}
  13.  
  14. interface
  15.  
  16. uses rusnglob,rusngenf,rusnio;
  17.  
  18. function basesitename(s: string): string;
  19.  
  20. {$ifdef oldmaildelivery}
  21. function newseqnumber: integer;
  22. {$endif}
  23.  
  24. function newmessageid: string;
  25. function getalreadyread(s: string): word;
  26. function joinedtogroup(var group: string): boolean;
  27. function parseheadername(s: string): string;
  28. function parseheadervalue(s: string): killstringt;
  29. function subjkilled(subject: string): boolean;
  30. function fromkilled(from: string): boolean;
  31. function subjantikilled(subject: string): boolean;
  32. function fromantikilled(from: string): boolean;
  33. function getstaticvalue(name: string): string;
  34. function getheaderline(infilename, fieldname: string): string;
  35. function stringtodate(datestr: string): datet;
  36.  
  37. {var only for efficiency}
  38. function subjseq(var s1,s2: subjstringt): boolean;
  39. function firstsubjg(var s1,s2: subjstringt): boolean;
  40.  
  41. function firstartfirst(a,b: integer): boolean;  {assuming subjseq() is true}
  42. function getbasedir(group: string): string;
  43. function groupsattr(group: string; attr: string): string;
  44. function groupbattr(group: string; attr: string): boolean;
  45. function getnextgroup: string;
  46. function alreadyseen(newsgroups: string): boolean;
  47. function getpwinfo164(field: integer): string;
  48. function getpwinfo165(field: integer): string;
  49. function wafexpand(s: string): string;
  50. function makesame(var s: string; prefix,shouldbe: string): boolean;
  51. function expandmail(address: string): string;
  52. function screenline(s: string): string;
  53. function onekey(prompt: string; validkeys: string): char;
  54. function ismailgroup(group: string): boolean;
  55.  
  56.  
  57. implementation
  58.  
  59. function basesitename;
  60.  
  61. var
  62.   atbang: integer;
  63.   atpercent: integer;
  64.   atat: integer;
  65.   result: string;
  66.   work: string;
  67.   atdot: integer;
  68.  
  69. begin
  70.   result := uucpname;
  71.   atbang := pos('!',s);
  72.   atpercent := pos('%',s);
  73.   atat := pos('@',s);
  74.   if atbang>0 then
  75.     begin
  76.       work := s;
  77.       while atbang>0 do
  78.         begin
  79.           result := copy(work,1,atbang-1);
  80.           work := copy(work,atbang+1,255);
  81.           atbang := pos('!',work);
  82.         end;
  83.     end
  84.   else if atpercent>0 then
  85.     begin
  86.       result := copy(s,atpercent+1,255);
  87.       atat := pos('@',result);
  88.       if atat>0 then
  89.         result := copy(result,1,atat-1);
  90.     end
  91.   else if atat>0 then
  92.     begin
  93.       result := copy(s,atat+1,255);
  94.     end;
  95.   atdot := pos('.',result);
  96.   if atdot>0 then
  97.     result := copy(result,1,atdot-1);
  98.   basesitename := result;
  99. end;
  100.  
  101. {$ifdef oldmaildelivery}
  102.  
  103. function newseqnumber;
  104.  
  105. var
  106.   seqf: text;
  107.   seqfn: string;
  108.   newseq: integer;
  109.  
  110. begin
  111.   if waffleversion='1.64' then
  112.     seqfn := waffledir+'\system\'+'seqf'
  113.   else
  114.     seqfn := waffledir+'\uucp\'+'sequence';
  115.   assign(seqf,seqfn);
  116.   reset(seqf);
  117.   readln(seqf,newseq);
  118.   close(seqf);
  119.   rewrite(seqf);
  120.   writeln(seqf,integertozstring(newseq+1,4));
  121.   close(seqf);
  122.   newseqnumber := newseq;
  123. end;
  124.  
  125. {$endif}
  126.  
  127. function newmessageid;
  128.  
  129. begin
  130.   newmessageid :=
  131.    '<'+itoa(year mod 100)+integertozstring(month,2)+
  132.    integertozstring(dayofmonth,2)+'.'+timedigits+'.'+
  133.    randomdigit+randomletter+randomdigit+'.'+newsreadername+'.'+
  134.    'w'+copy(waffleversion,1,1)+copy(waffleversion,3,2)+'w'+'@'+node+'>';
  135. end;
  136.  
  137. function getalreadyread;
  138.  
  139. begin
  140.   getalreadyread := atow(ltrim(trim(copy(s,pos(' ',s)+1,255))));
  141. end;
  142.  
  143. function closegroup(partial,full: string): boolean;
  144.  
  145. var
  146.   result: boolean;
  147.   partwork, fullwork: string;
  148.   partat, fullat: integer;
  149.  
  150. begin
  151.   if (numoccur('.',partial)=numoccur('.',full)) then
  152.     begin
  153.       result := true;
  154.       partwork := partial+'.';
  155.       fullwork := full+'.';
  156.       while result and (pos('.',partwork)>0) do
  157.         begin
  158.           partat := pos('.',partwork);
  159.           fullat := pos('.',fullwork);
  160.           result := result and
  161.            (copy(partwork,1,partat-1)=copy(fullwork,1,partat-1));
  162.           if result then
  163.             begin
  164.               partwork := copy(partwork,partat+1,255);
  165.               fullwork := copy(fullwork,fullat+1,255);
  166.             end;
  167.         end;
  168.     end
  169.   else
  170.     result := false;
  171.   closegroup := result;
  172. end;
  173.  
  174. {joinedtogroup changes the parameter if and only if it isn't joined}
  175. {to, and something else could be found that _is_ joined to}
  176.  
  177. function joinedtogroup;
  178.  
  179. var
  180.   result: boolean;
  181.   eachg: string;
  182.   newname: string;
  183.   subname: string;
  184.  
  185. begin
  186.   result := false;
  187.   newname := '';
  188.   subname := '';
  189.   reset(joinf);
  190.   while not eof(joinf) and not result do
  191.     begin
  192.       readln(joinf,eachg);
  193.       eachg := getfirstw(eachg);
  194.  
  195.       if eachg=group then
  196.         result := true
  197.       else
  198.         begin
  199.           if (newname='') then
  200.             if closegroup(group,eachg) then
  201.               newname := eachg
  202.             else if (subname='') then
  203.               if pos(group,eachg)<>0 then
  204.                 subname := eachg;
  205.         end;
  206.     end;
  207.   if not result and (newname<>'') then
  208.     begin
  209.       group := newname;
  210.       result := true;
  211.     end;
  212.   if not result and (subname<>'') then
  213.     begin
  214.       group := subname;
  215.       result := true;
  216.     end;
  217.   joinedtogroup := result;
  218. end;
  219.  
  220. function parseheadername;
  221.  
  222. begin
  223.   parseheadername := copy(s,1,pos(':',s)-1);
  224. end;
  225.  
  226. function parseheadervalue;
  227.  
  228. begin
  229.   parseheadervalue := copy(s,pos(':',s)+2,255);
  230. end;
  231.  
  232. function killmatch(killtext,headertext: string;
  233.  caseinsensitive,substring: boolean): boolean;
  234.  
  235. { if caseinsensitive, then headertext is already uppercased }
  236.  
  237. begin
  238.   if caseinsensitive then
  239.     if substring then
  240.       killmatch := (pos(upper(killtext),headertext)<>0)
  241.     else
  242.       killmatch := (upper(killtext)=headertext)
  243.   else
  244.     if substring then
  245.       killmatch := (pos(killtext,headertext)<>0)
  246.     else
  247.       killmatch := (killtext=headertext);
  248. end;
  249.  
  250. function subjkilled;
  251.  
  252. var
  253.   i: integer;
  254.   result: boolean;
  255.   noresubject: string;
  256.  
  257. begin
  258.  
  259. { subject matching always done modulo Re: }
  260.  
  261.   result := false;
  262.   noresubject := nore(subject);
  263.  
  264.   if caseinsensitivekill then
  265.     noresubject := upper(noresubject);
  266.  
  267.   for i := 1 to numsubjks do
  268.     if not result then
  269.       result := killmatch(killsubjsp^[i],noresubject,
  270.        caseinsensitivekill,substringsubjectkill);
  271.   subjkilled := result;
  272. end;
  273.  
  274. function fromkilled;
  275.  
  276. var
  277.   i: integer;
  278.   result: boolean;
  279.   newfrom: string;
  280.  
  281. begin
  282. {From: match if that address found anywhere - so that if they change their}
  283. {posting software or whatever you'll still find it.}
  284.  
  285.   result := false;
  286.   newfrom := from;
  287.  
  288.   if caseinsensitivekill then
  289.     newfrom := upper(newfrom);
  290.  
  291.   for i := 1 to numfromks do
  292.     if not result then
  293.       result := killmatch(killfromsp^[i],newfrom,
  294.        caseinsensitivekill,substringfromkill);
  295.  
  296.   fromkilled := result;
  297. end;
  298.  
  299. function subjantikilled;
  300.  
  301. var
  302.   i: integer;
  303.   result: boolean;
  304.   noresubject: string;
  305.  
  306. begin
  307.  
  308. { subject matching always done modulo Re: }
  309.  
  310.   result := false;
  311.   noresubject := nore(subject);
  312.  
  313.   if caseinsensitiveantikill then
  314.     noresubject := upper(noresubject);
  315.  
  316.   for i := 1 to numsubjaks do
  317.     if not result then
  318.       result := killmatch(antikillsubjsp^[i],noresubject,
  319.        caseinsensitiveantikill,substringsubjectantikill);
  320.  
  321.   subjantikilled := result;
  322. end;
  323.  
  324. function fromantikilled;
  325.  
  326. var
  327.   i: integer;
  328.   result: boolean;
  329.   newfrom: string;
  330.  
  331. begin
  332.  
  333.   result := false;
  334.   newfrom := from;
  335.  
  336.   if caseinsensitiveantikill then
  337.     newfrom := upper(newfrom);
  338.  
  339.   for i := 1 to numfromaks do
  340.     if not result then
  341.       result := killmatch(antikillfromsp^[i],newfrom,
  342.        caseinsensitiveantikill,substringfromantikill);
  343.  
  344.   fromantikilled := result;
  345. end;
  346.  
  347. function getstaticvalue;
  348.  
  349. var
  350.   result: string;
  351.   infile: text;
  352.   s: string;
  353.   foundname: string;
  354.  
  355. begin
  356.   result := '';
  357.  
  358.   oldfilemode := filemode;
  359.   if not nofilemode then
  360.     filemode := $40;   {read only, deny none}
  361.  
  362.   if customstatic<>'' then
  363.     begin
  364.       assign(infile,customstatic);
  365.       {$I-}
  366.       reset(infile);
  367.       {$I+}
  368.       if ioresult=0 then
  369.         begin
  370.           while (result='') and not eof(infile) do
  371.             begin
  372.               readln(infile,s);
  373.               if s<>'' then
  374.                 if copy(s,1,1)<>'#' then
  375.                   begin
  376.                     foundname := trim(ltrim(copy(s,1,pos(':',s)-1)));
  377.                     if foundname=name then
  378.                       begin
  379.                         result := trim(ltrim(copy(s,pos(':',s)+1,255)));
  380.                       end;
  381.                   end;
  382.             end;
  383.           close(infile);
  384.         end;
  385.     end;
  386.  
  387.   if result='' then
  388.     begin
  389.  
  390.       assign(infile,wafenv);
  391.       {$I-}
  392.       reset(infile);
  393.       {$I+}
  394.  
  395.       if ioresult=0 then
  396.         begin
  397.           while (result='') and not eof(infile) do
  398.             begin
  399.               readln(infile,s);
  400.               if s<>'' then
  401.                 if copy(s,1,1)<>'#' then
  402.                   begin
  403.                     foundname := trim(ltrim(copy(s,1,pos(':',s)-1)));
  404.                     if foundname=name then
  405.                       begin
  406.                         result := trim(ltrim(copy(s,pos(':',s)+1,255)));
  407.                       end;
  408.                   end;
  409.             end;
  410.           close(infile);
  411.         end;
  412.     end;
  413.  
  414.   filemode := oldfilemode;
  415.  
  416.   getstaticvalue := result;
  417. end;
  418.  
  419. function getheaderline;
  420.  
  421. var
  422.   infile: file;
  423.   foundline: boolean;
  424.   result: string;
  425.   s: string;
  426.   ufieldname: string;
  427.   headerbytesseen: integer;
  428.   morelinesinheader: boolean;
  429.   wastes: string;
  430.   i,j: integer;
  431.  
  432. function nextlinefrombuf: string;
  433.  
  434. var
  435.   result: string;
  436.   gotcrlf: boolean;
  437.   c: char;
  438.  
  439. begin
  440.   result := '';
  441.   gotcrlf := false;
  442.   while (headerbytesseen<headerbytesinmem) and not gotcrlf do
  443.     begin
  444.       inc(headerbytesseen);
  445.       c := headerbuf[headerbytesseen];
  446.       if (c=#13) then
  447.         gotcrlf := true
  448.       else if c<>#10 then
  449.         result := result+c;
  450.     end;
  451.   nextlinefrombuf := result;
  452. end;
  453.  
  454. begin
  455.   result := '';
  456.   ufieldname := upper(fieldname);
  457.  
  458.   foundline := false;
  459.  
  460.   if headerinmem<>infilename then
  461.     begin
  462.  
  463.       oldfilemode := filemode;
  464.       if not nofilemode then
  465.         filemode := $40;   {read only, deny none}
  466.  
  467.       assign(infile,infilename);
  468.       {$I-}
  469.       reset(infile,1);
  470.       {$I+}
  471.  
  472.       if ioresult=0 then
  473.         begin
  474.           blockread(infile,headerbuf,headerbufsize,headerbytesinmem);
  475.           headerinmem := infilename;
  476.           close(infile);
  477.         end
  478.       else
  479.         begin
  480.           for i := 1 to headerbufsize do
  481.             headerbuf[i] := ' ';
  482.           result := '(could not read file)';
  483.           foundline := true;
  484.         end;
  485.  
  486.       filemode := oldfilemode;
  487.  
  488.       for i := 1 to headertlsize do
  489.         begin
  490.           headertrackedlines[i].first := #0;
  491.           headertrackedlines[i].offset := -1;
  492.         end;
  493.       headertrackedlines[1].first := upcase(headerbuf[1]);
  494.       headertrackedlines[1].offset := 1;
  495.       j := 1;
  496.       i := 0;
  497.       while (i<headerbufsize-2) and (j<headertlsize) do
  498.         begin
  499.           inc(i);
  500.           if headerbuf[i]=#10 then
  501.             if headerbuf[i+2]=#10 then
  502.               i := headerbufsize {found the empty line}
  503.             else
  504.               begin
  505.                 inc(j);
  506.                 headertrackedlines[j].first := upcase(headerbuf[i+1]);
  507.                 headertrackedlines[j].offset := i+1;
  508.               end;
  509.         end;
  510.  
  511. {$ifdef testfastheaders}
  512. for i := 1 to min(10,headertlsize) do
  513.   writeln(headertrackedlines[i].offset:3,' ',headertrackedlines[i].first);
  514. delay(1000);
  515. {$endif}
  516.  
  517.     end;
  518.  
  519. {$ifdef veryoldheader}
  520.  
  521.   foundblank := false;
  522.  
  523.   while (not eof(f)) and (not foundblank) and (not foundline) do
  524.     begin
  525.       readln(f,s);
  526.       if copy(upper(ltrim(s)),1,length(fieldname))=ufieldname then
  527.         begin
  528.           foundline := true;
  529.           result := ltrim(trim(copy(trim(s),length(fieldname)+1,255)));
  530.           if not eof(f) then
  531.             begin
  532.               readln(f,s);
  533.               if copy(s,1,1)=' ' then
  534.                 result := result+s;
  535.             end;
  536.         end
  537.       else if length(trim(s))=0 then
  538.         foundblank := true;
  539.     end;
  540.   close(f);
  541. {$endif}
  542.  
  543. {$ifdef oldheader}
  544.  
  545.   foundblank := false;
  546.  
  547.   headerbytesseen := 0;
  548.   while (headerbytesseen<headerbytesinmem) and
  549.    (not foundblank) and (not foundline) do
  550.     begin
  551.       s := nextlinefrombuf;
  552.       if copy(upper(ltrim(s)),1,length(fieldname))=ufieldname then
  553.         begin
  554.           foundline := true;
  555.           result := ltrim(trim(copy(trim(s),length(fieldname)+1,255)));
  556.           if headerbytesseen<headerbytesinmem then
  557.             begin
  558.               morelinesinheader := true;
  559.               while morelinesinheader do
  560.                 begin
  561.                   s := nextlinefrombuf;
  562.                   if (copy(s,1,1)=' ') or (copy(s,1,1)=^I) then
  563.                     begin
  564.                       s := ltrim(s);
  565.  
  566. {handle References: line specially - always get the last part}
  567.  
  568.                       if ufieldname='REFERENCES:' then
  569.                         begin
  570.                           if length(s)>200 then
  571.                             result := s
  572.                           else
  573.                             begin
  574.                               if length(result)+length(s)>200 then
  575.                                 wastes := chopfirstw(result);
  576.                               if length(result)+length(s)>200 then
  577.                                 wastes := chopfirstw(result);
  578.                               if length(result)+length(s)>200 then
  579.                                 wastes := chopfirstw(result);
  580.                               if length(result)+length(s)>200 then
  581.                                 wastes := chopfirstw(result);
  582.                               result := result+' '+s;
  583.                             end;
  584.                         end
  585.                       else
  586.                         result := result+' '+s;
  587.                     end
  588.                   else
  589.                     morelinesinheader := false;
  590.                 end;
  591.             end;
  592.         end
  593.       else if length(trim(s))=0 then
  594.         foundblank := true;
  595.     end;
  596.  
  597. {$endif}
  598.  
  599.   j := 0;
  600.   while (j<headertlsize) and not foundline do
  601.     begin
  602.       inc(j);
  603.       if headertrackedlines[j].first=ufieldname[1] then
  604.         begin
  605.           headerbytesseen := headertrackedlines[j].offset-1;
  606.           s := nextlinefrombuf;
  607.           if copy(upper(ltrim(s)),1,length(fieldname))=ufieldname then
  608.             begin
  609.               foundline := true;
  610.               result := ltrim(copy(trim(s),length(fieldname)+1,255));
  611.               if headerbytesseen<headerbytesinmem then
  612.                 begin
  613.                   morelinesinheader := true;
  614.                   while morelinesinheader do
  615.                     begin
  616.                       s := nextlinefrombuf;
  617.                       if (copy(s,1,1)=' ') or (copy(s,1,1)=^I) then
  618.                         begin
  619.                           s := ltrim(s);
  620.  
  621. {handle References: line specially - always get the last part}
  622.  
  623.                           if ufieldname='REFERENCES:' then
  624.                             begin
  625.                               if length(result)+length(s)>200 then
  626.                                 wastes := chopfirstw(result);
  627.                               result := result+' '+s;
  628.                             end
  629.                           else
  630.                             result := result+' '+s;
  631.                         end
  632.                       else
  633.                         morelinesinheader := false;
  634.                     end;
  635.                 end;
  636.             end;
  637.         end;
  638.     end;
  639.  
  640.   getheaderline := result;
  641. end;
  642.  
  643. {}{} {doesn't handle time zones at all - but at least when a user}
  644.      {posts twice on the same day, the tz will be the same each time}
  645.      {and thus correctly ordered}
  646.  
  647. function stringtodate;
  648.  
  649. var
  650.   result: datet;
  651.   workstr: string;
  652.   dayofmonth: longint;
  653.   monthstr: string;
  654.   month: longint;
  655.   year: longint;
  656.   gmthour: longint;
  657.  
  658. begin
  659.   if datestr='' then
  660.     result := 9999*16384
  661.   else
  662.     begin
  663.       workstr := datestr;
  664.       dayofmonth := snatchint(workstr);
  665.       workstr := ltrim(workstr);
  666.       monthstr := copy(workstr,1,3);
  667.       month := monthstringtointeger(monthstr);
  668.       workstr := ltrim(chop(workstr,4));
  669.       year := snatchint(workstr);
  670.       if year<100 then
  671.         inc(year,1900);
  672.       gmthour := snatchint(workstr);
  673.       result := year*16384+month*1024+dayofmonth*32+gmthour;
  674.     end;
  675.   stringtodate := result;
  676. end;
  677.  
  678. {var only for efficiency}
  679.  
  680. function canonicalsubj(var subject: subjstringt): string;
  681.  
  682. var
  683.   result: string;
  684.  
  685. begin
  686.   if subjectlength=255 then
  687.     result := subject
  688.   else
  689.     result := copy(subject,1,subjectlength);
  690.  
  691.   if subjectscaseinsensitive then
  692.     result := upper(result);
  693.  
  694.   canonicalsubj := result;
  695. end;
  696.  
  697. {var only for efficiency}
  698.  
  699. function canonfschar(var subject: subjstringt): char;
  700.  
  701. var
  702.   result: char;
  703.  
  704. begin
  705.   if subject='' then
  706.     result := ' '
  707.   else
  708.     begin
  709.       if subjectscaseinsensitive then
  710.         result := upcase(subject[1])
  711.       else
  712.         result := subject[1];
  713.     end;
  714.  
  715.   canonfschar := result;
  716. end;
  717.  
  718. function subjseq;
  719.  
  720. var
  721.   result: boolean;
  722.  
  723. begin
  724.   if (s1='') or (s2='') then
  725.     result := (canonicalsubj(s1)=canonicalsubj(s2))
  726.   else if canonfschar(s1)=canonfschar(s2) then
  727.     result := (canonicalsubj(s1)=canonicalsubj(s2))
  728.   else
  729.     result := false;
  730.  
  731.   subjseq := result;
  732. end;
  733.  
  734. function firstsubjg;
  735.  
  736. var
  737.   result: boolean;
  738.  
  739. begin
  740.   if (s1='') or (s2='') then
  741.     result := (canonicalsubj(s1)>canonicalsubj(s2))
  742.   else if canonfschar(s1)<canonfschar(s2) then
  743.     result := false
  744.   else
  745.     result := (canonicalsubj(s1)>canonicalsubj(s2));
  746.  
  747.   firstsubjg := result;
  748. end;
  749.  
  750. function hasheq(h1,h2: hashedt): boolean;
  751.  
  752. begin
  753.   hasheq := (h1[1]=h2[1]) and (h1[2]=h2[2]);
  754. end;
  755.  
  756. function firstartfirst;
  757.  
  758. var
  759.   result: boolean;
  760.  
  761. begin
  762.   result := true;
  763.  
  764. {$ifdef testhash}
  765.  
  766. if true then
  767.   begin
  768.     writeln('#',a,' mes=',hmessageidsp^[a,1]:5,' ',hmessageidsp^[a,2]:5);
  769.     writeln('#',a,' ref=',
  770.      hreferencesp[1]^[a,1]:5,' ',hreferencesp[1]^[a,2]:5,' ',
  771.      hreferencesp[2]^[a,1]:5,' ',hreferencesp[2]^[a,2]:5,' ',
  772.      hreferencesp[3]^[a,1]:5,' ',hreferencesp[3]^[a,2]:5,' ',
  773.      hreferencesp[4]^[a,1]:5,' ',hreferencesp[4]^[a,2]:5);
  774.     writeln('#',b,' mes=',hmessageidsp^[b,1]:5,' ',hmessageidsp^[b,2]:5);
  775.     writeln('#',b,' ref=',
  776.      hreferencesp[1]^[b,1]:5,' ',hreferencesp[1]^[b,2]:5,' ',
  777.      hreferencesp[2]^[b,1]:5,' ',hreferencesp[2]^[b,2]:5,' ',
  778.      hreferencesp[3]^[b,1]:5,' ',hreferencesp[3]^[b,2]:5,' ',
  779.      hreferencesp[4]^[b,1]:5,' ',hreferencesp[4]^[b,2]:5);
  780.  
  781.   if hasheq(hmessageidsp^[b],hreferencesp[1]^[a]) then
  782.     writeln('#',b,' ',filenamesp^[b],' before #',a,' ',filenamesp^[a])
  783.   else if hasheq(hmessageidsp^[b],hreferencesp[2]^[a]) then
  784.     writeln('#',b,' ',filenamesp^[b],' before #',a,' ',filenamesp^[a])
  785.   else if hasheq(hmessageidsp^[b],hreferencesp[3]^[a]) then
  786.     writeln('#',b,' ',filenamesp^[b],' before #',a,' ',filenamesp^[a])
  787.   else if hasheq(hmessageidsp^[b],hreferencesp[4]^[a]) then
  788.     writeln('#',b,' ',filenamesp^[b],' before #',a,' ',filenamesp^[a])
  789.   else
  790.     writeln('#',b,' ',filenamesp^[b],' not refd by #',a,' ',filenamesp^[a]);
  791.  
  792.   
  793.   if hasheq(hmessageidsp^[a],hreferencesp[1]^[b]) then
  794.     writeln('#',a,' ',filenamesp^[a],' before #',b,' ',filenamesp^[b])
  795.   else if hasheq(hmessageidsp^[a],hreferencesp[2]^[b]) then
  796.     writeln('#',a,' ',filenamesp^[a],' before #',b,' ',filenamesp^[b])
  797.   else if hasheq(hmessageidsp^[a],hreferencesp[3]^[b]) then
  798.     writeln('#',a,' ',filenamesp^[a],' before #',b,' ',filenamesp^[b])
  799.   else if hasheq(hmessageidsp^[a],hreferencesp[4]^[b]) then
  800.     writeln('#',a,' ',filenamesp^[a],' before #',b,' ',filenamesp^[b])
  801.   else
  802.     writeln('#',a,' ',filenamesp^[a],' not refd by #',b,' ',filenamesp^[b]);
  803.  
  804.   end;
  805.  
  806. {$endif}
  807.  
  808.   if hasheq(hmessageidsp^[b],hreferencesp[1]^[a]) then
  809.     result := false
  810.   else if hasheq(hmessageidsp^[b],hreferencesp[2]^[a]) then
  811.     result := false
  812.   else if hasheq(hmessageidsp^[b],hreferencesp[3]^[a]) then
  813.     result := false
  814.   else if hasheq(hmessageidsp^[b],hreferencesp[4]^[a]) then
  815.     result := false
  816.   else
  817.     if not hasheq(hmessageidsp^[a],hreferencesp[1]^[b]) then
  818.       if not hasheq(hmessageidsp^[a],hreferencesp[2]^[b]) then
  819.         if not hasheq(hmessageidsp^[a],hreferencesp[3]^[b]) then
  820.           if not hasheq(hmessageidsp^[a],hreferencesp[4]^[b]) then
  821.             begin
  822.  
  823. {no conclusive proof - just guess}
  824.  
  825.               if datesp^[a]>datesp^[b] then
  826.                 result := false;
  827.               if datesp^[a]=datesp^[b] then
  828.                 if (indents[a] and $f) > (indents[b] and $f) then
  829.                   result := false;
  830.             end;
  831.  
  832. {$ifdef testsort}
  833.   write('firstartfirst(',a,',',b,')=');
  834.   if result then writeln('true') else writeln('false');
  835.                 xwrites('pausing...');
  836.                 xwritelns(xreadkey);
  837.   
  838. {$endif}
  839.  
  840.   firstartfirst := result;
  841. end;
  842.  
  843. function fogetbasedir(group: string; forumset: string): string;
  844.  
  845. var
  846.   result: string;
  847.   infilen: string;
  848.   infile: text;
  849.   s: string;
  850.   foundgroup: boolean;
  851.   mangledgroup: string;
  852.   default: string;
  853.   defaultdir: string;
  854.  
  855. begin
  856.   foundgroup := false;
  857.   result := '';
  858.   default := '';
  859.  
  860.   oldfilemode := filemode;
  861.   if not nofilemode then
  862.     filemode := $40;   {read only, deny none}
  863.  
  864.   infilen := waffledir+'\system\'+forumset;
  865.  
  866.   assign(infile,infilen);
  867.   {$I-}
  868.   reset(infile);
  869.   {$I+}
  870.  
  871.   if ioresult=0 then
  872.     begin
  873.       while not foundgroup and not eof(infile) do
  874.         begin
  875.           readln(infile,s);
  876.           foundgroup := (getfirstw(s)=group);
  877.           if pos('/dir=',s)>0 then
  878.             begin
  879.               if getfirstw(s)=group then
  880.                 begin
  881.                   result := trim(ltrim(copy(s,pos('/dir=',s)+5,255)));
  882.                   result := unquote(getfirstw(unslash(result)));
  883.                 end
  884.               else if getfirstw(s)='DEFAULT' then
  885.                 default := s;
  886.             end;
  887.         end;
  888.       close(infile);
  889.     end;
  890.  
  891.   filemode := oldfilemode;
  892.  
  893.   if (result='') and (default<>'') and foundgroup then
  894.     begin
  895.  
  896.       defaultdir := trim(ltrim(copy(default,pos('/dir=',default)+5,255)));
  897.       defaultdir := unquote(getfirstw(unslash(defaultdir)));
  898.  
  899. {waffle treats /dir=x: to mean /dir=x:\ anyway}
  900.  
  901.       if defaultdir[length(defaultdir)]<>'\' then
  902.         defaultdir := defaultdir+'\';
  903.       mangledgroup := group;
  904.       while pos('.',mangledgroup)>0 do
  905.         begin
  906.           result := result+
  907.            copy(mangledgroup,1,min(8,pos('.',mangledgroup)-1))+'\';
  908.           mangledgroup := copy(mangledgroup,pos('.',mangledgroup)+1,255);
  909.         end;
  910.       result := defaultdir+result+
  911.        copy(mangledgroup,1,min(8,length(mangledgroup)))+'\';
  912.     end;
  913.  
  914.   if result<>'' then
  915.     if result[length(result)]<>'\' then
  916.       result := result+'\';
  917.  
  918.   fogetbasedir := result;
  919. end;
  920.  
  921. function secondarygetbasedir(group: string): string;
  922.  
  923. var
  924.   result: string;
  925.   forumset: string;
  926.   mungedl: string;
  927.  
  928. begin
  929.   result := '';
  930.   mungedl := forumsetl;
  931.   while (result='') and (mungedl<>'') do
  932.     begin
  933.       forumset := chopfirstw(mungedl);
  934.       result := fogetbasedir(group,forumset);
  935.     end;
  936.   secondarygetbasedir := result;
  937. end;
  938.  
  939. function getbasedir;
  940.  
  941. var
  942.   result: string;
  943.   nonprefix: string;
  944.   partialprefix: string;
  945.   i: integer;
  946.  
  947. begin
  948.   result := '';
  949.   if ismailgroup(group) then
  950.     begin
  951.  
  952. {partialprefix is mailprefix without the `.userid' bits}
  953.  
  954.       partialprefix := copy(group,1,length(mailprefix)-1-length(userid));
  955.  
  956.       if group=mailprefix then
  957.         begin
  958.  
  959. {look for just partialprefix, and add individual user ids on after}
  960.  
  961.           result := secondarygetbasedir(partialprefix);
  962.           if result<>'' then
  963.             result := result+userid+'\';
  964.         end
  965.  
  966.       else
  967.  
  968.         begin
  969.  
  970. {must be a folder}
  971.  
  972. {look for user's home mail directory, then add folders onto end}
  973.  
  974.           nonprefix := copy(group,length(mailprefix)+2,255);  { lose the . }
  975.  
  976.           for i := 1 to length(nonprefix) do
  977.             if nonprefix[i]='.' then
  978.               nonprefix[i] := '\';
  979.           result := getbasedir(partialprefix);
  980.           if result<>'' then
  981.             result := result+nonprefix+'\';
  982.         end;
  983.  
  984.     end;
  985.  
  986.   if result='' then
  987.     result := secondarygetbasedir(group);
  988.  
  989.   getbasedir := result;
  990. end;
  991.  
  992. {}{}{}{} {need to make sure it's not inside some option's path}
  993.  
  994. function fogroupsattr(group: string; attr: string; forumset: string): string;
  995.  
  996. var
  997.   result: string;
  998.   infilen: string;
  999.   infile: text;
  1000.   s: string;
  1001.   foundgroup: boolean;
  1002.   default: string;
  1003.  
  1004. begin
  1005.   foundgroup := false;
  1006.   result := '';
  1007.   default := '';
  1008.  
  1009.   oldfilemode := filemode;
  1010.   if not nofilemode then
  1011.     filemode := $40;   {read only, deny none}
  1012.  
  1013.   infilen := waffledir+'\system\'+forumset;
  1014.  
  1015.   assign(infile,infilen);
  1016.   {$I-}
  1017.   reset(infile);
  1018.   {$I+}
  1019.  
  1020.   if ioresult=0 then
  1021.     begin
  1022.       while not foundgroup and not eof(infile) do
  1023.         begin
  1024.           readln(infile,s);
  1025.           foundgroup := (getfirstw(s)=group);
  1026.           if pos(attr,s)>0 then
  1027.             begin
  1028.               if foundgroup then
  1029.                 result := 
  1030.                  getfirstw(trim(ltrim(copy(s,pos(attr,s)+length(attr),255))))
  1031.               else if getfirstw(s)='DEFAULT' then
  1032.                 default := s;
  1033.             end;
  1034.         end;
  1035.       close(infile);
  1036.     end;
  1037.  
  1038.   filemode := oldfilemode;
  1039.  
  1040.   if (result='') and (default<>'') and foundgroup then
  1041.     result :=
  1042.      getfirstw(trim(ltrim(copy(default,pos(attr,default)+length(attr),255))));
  1043.  
  1044.   fogroupsattr := result;
  1045. end;
  1046.  
  1047. function groupsattr;
  1048.  
  1049. var
  1050.   forumset: string;
  1051.   mungedl: string;
  1052.   result: string;
  1053.  
  1054. begin
  1055.   result := '';
  1056.   mungedl := forumsetl;
  1057.   while (result='') and (mungedl<>'') do
  1058.     begin
  1059.       forumset := chopfirstw(mungedl);
  1060.       result := fogroupsattr(group,attr,forumset);
  1061.     end;
  1062.   groupsattr := result;
  1063. end;
  1064.  
  1065. {}{}{}{} {need to make sure it's not inside some option's path}
  1066.  
  1067. function fogroupbattr(group: string; attr: string; forumset: string): boolean;
  1068.  
  1069. var
  1070.   result: boolean;
  1071.   infilen: string;
  1072.   infile: text;
  1073.   s: string;
  1074.   foundgroup: boolean;
  1075.   mangledgroup: string;
  1076.   default: string;
  1077.  
  1078. begin
  1079.   foundgroup := false;
  1080.   result := false;
  1081.   default := '';
  1082.  
  1083.   oldfilemode := filemode;
  1084.   if not nofilemode then
  1085.     filemode := $40;   {read only, deny none}
  1086.  
  1087.   infilen := waffledir+'\system\'+forumset;
  1088.  
  1089.   assign(infile,infilen);
  1090.   {$I-}
  1091.   reset(infile);
  1092.   {$I+}
  1093.  
  1094.   if ioresult=0 then
  1095.     begin
  1096.       while not foundgroup and not eof(infile) do
  1097.         begin
  1098.           readln(infile,s);
  1099.           foundgroup := (getfirstw(s)=group);
  1100.           if pos(attr,s)>0 then
  1101.             begin
  1102.               if foundgroup then
  1103.                 result := true
  1104.               else if getfirstw(s)='DEFAULT' then
  1105.                 default := s;
  1106.             end;
  1107.         end;
  1108.       close(infile);
  1109.     end;
  1110.  
  1111.   filemode := oldfilemode;
  1112.  
  1113.   if (default<>'') and foundgroup then
  1114.     result := true;
  1115.  
  1116.   fogroupbattr := result;
  1117. end;
  1118.  
  1119. function groupbattr;
  1120.  
  1121. var
  1122.   forumset: string;
  1123.   mungedl: string;
  1124.   result: boolean;
  1125.  
  1126. begin
  1127.   result := false;
  1128.   mungedl := forumsetl;
  1129.   while not result and (mungedl<>'') do
  1130.     begin
  1131.       forumset := chopfirstw(mungedl);
  1132.       result := fogroupbattr(group,attr,forumset);
  1133.     end;
  1134.   groupbattr := result;
  1135. end;
  1136.  
  1137. function getnextgroup: string;
  1138.  
  1139. var
  1140.   foundgroup: string;
  1141.   result: string;
  1142.  
  1143. begin
  1144.  
  1145. {}{} {this should use joinedgroups[] if possible}
  1146.  
  1147.   result := '';
  1148.   reset(joinf);
  1149.   foundgroup := '';
  1150.  
  1151.   if not eof(joinf) then
  1152.     begin
  1153.       if currgroup='' then
  1154.         begin
  1155.           readln(joinf,foundgroup);
  1156.           result := getfirstw(foundgroup);
  1157.         end
  1158.       else
  1159.         begin
  1160.           while not eof(joinf) and (foundgroup<>currgroup) do
  1161.             begin
  1162.               readln(joinf,foundgroup);
  1163.               foundgroup := getfirstw(foundgroup);
  1164.             end;
  1165.           if not eof(joinf) then
  1166.             begin
  1167.               readln(joinf,foundgroup);
  1168.               result := getfirstw(foundgroup);
  1169.             end;
  1170.         end;
  1171.     end;
  1172.   getnextgroup := result;
  1173. end;
  1174.  
  1175. function alreadyseen;
  1176.  
  1177. var
  1178.   i: integer;
  1179.   newsglist: string;
  1180.   result: boolean;
  1181.   found: boolean;
  1182.  
  1183. begin
  1184.   result := false;
  1185.   if (currgroup<>'control') and (currgroup<>'monitor') and
  1186.    (copy(currgroup,1,14)<>'news.announce.') and
  1187.    ((numoccur('.',currgroup)<>1) or (right(currgroup,8)<>'.answers')) then
  1188.     begin
  1189.       found := false;
  1190.       newsglist := ','+newsgroups+',';
  1191.       i := 1;
  1192.       while (i<numjoined) and not found do
  1193.         begin
  1194.           if (copy(joinedgroups[i],1,14)<>'news.announce.') and
  1195.            (pos(','+joinedgroups[i]+',',newsglist)<>0) and
  1196.              ((numoccur('.',joinedgroups[i])<>1) or
  1197.              (right(joinedgroups[i],8)<>'.answers')) then
  1198.             begin
  1199.               found := true;
  1200.               result := (joinedgroups[i]<>currgroup);
  1201.             end;
  1202.           inc(i);
  1203.         end;
  1204.     end;
  1205.   alreadyseen := result;
  1206. end;
  1207.  
  1208. function getpwinfo164;
  1209.  
  1210. const
  1211.   passwordblocksize=256;
  1212.  
  1213. type
  1214.   passwordbuft=array[1..passwordblocksize] of char;
  1215.  
  1216. var
  1217.   passwordbuf: passwordbuft;
  1218.   passwordf: file;
  1219.   result: string;
  1220.   found: boolean;
  1221.  
  1222. function passwordentry164(fieldnum: integer): string;
  1223.  
  1224. var
  1225.   i: integer;
  1226.   lfs: integer;
  1227.   result: string;
  1228.  
  1229. begin
  1230.   result := '';
  1231.   lfs := 0;
  1232.   for i := 1 to passwordblocksize do
  1233.     begin
  1234.       if passwordbuf[i]=#10 then
  1235.         inc(lfs);
  1236.       if (lfs=fieldnum) and (passwordbuf[i]<>#10) then
  1237.         result := result+passwordbuf[i];
  1238.     end;
  1239.   passwordentry164 := result;
  1240. end;
  1241.  
  1242. begin
  1243.   result := '';
  1244.   found := false;
  1245.  
  1246.   oldfilemode := filemode;
  1247.   if not nofilemode then
  1248.     filemode := $40;   {read only, deny none}
  1249.  
  1250.   assign(passwordf,waffledir+'\admin\'+'password');
  1251.   {$I-}
  1252.   reset(passwordf,1);
  1253.   {$I+}
  1254.  
  1255.   if ioresult=0 then
  1256.     begin
  1257.       blockread(passwordf,passwordbuf,passwordblocksize);
  1258.       while not found and not eof(passwordf) do
  1259.         begin
  1260.           blockread(passwordf,passwordbuf,passwordblocksize);
  1261.           if passwordentry164(0)=userid then
  1262.             begin
  1263.               result := passwordentry164(field);
  1264.               found := true;
  1265.             end;
  1266.         end;
  1267.       close(passwordf);
  1268.     end;
  1269.  
  1270.   filemode := oldfilemode;
  1271.  
  1272.   getpwinfo164 := result;
  1273. end;
  1274.  
  1275. function getpwinfo165;
  1276.  
  1277. const
  1278.   passwordblocksize=1024;
  1279.  
  1280. type
  1281.   passwordbuft=array[1..passwordblocksize] of char;
  1282.  
  1283. var
  1284.   passwordbuf: passwordbuft;
  1285.   passwordf: file;
  1286.   result: string;
  1287.   found: boolean;
  1288.  
  1289. function fieldsize165(fieldnum: integer): integer;
  1290.  
  1291. var
  1292.   result: integer;
  1293.  
  1294. begin
  1295.   result := 0;
  1296.   case fieldnum of
  1297.     1: result := 12; {name}
  1298.     2: result := 12; {pass}
  1299.     3: result := 24; {identity}         {I'm told _this_ is the one for %W}
  1300.     4: result := 24; {realname}
  1301.     5: result := 22; {phone}
  1302.     6: result := 40; {shell}
  1303.     7: result := 10; {editor}
  1304.     8: result := 10; {console}
  1305.     9: result := 66; {comment}
  1306.    10: result := 8;  {level}
  1307.    11: result := 10; {terminal}
  1308.    12: result := 10; {language}
  1309.    13: result := 10; {suite}
  1310.    14: result := 10; {account}
  1311.    15: result := 12; {group}
  1312.    16: result := 2;  {access}
  1313.    17: result := 8;  {priv}
  1314.    18: result := 12; {age}
  1315.    19: result := 2;  {color}
  1316.    20: result := 5;  {encryption}
  1317.    21: result := 8;  {help}
  1318.   end;
  1319.   fieldsize165 := result;
  1320. end;
  1321.  
  1322. function fieldstart165(fieldnum: integer): integer;
  1323.  
  1324. var
  1325.   i: integer;
  1326.   result: integer;
  1327.  
  1328. begin
  1329.   result := 0;
  1330.   for i := 1 to fieldnum-1 do
  1331.     inc(result,fieldsize165(i));
  1332.   fieldstart165 := result;
  1333. end;
  1334.  
  1335. function passwordentry165(fieldnum: integer): string;
  1336.  
  1337. var
  1338.   i: integer;
  1339.   start: integer;
  1340.   size: integer;
  1341.   result: string;
  1342.   ch: char;
  1343.   done: boolean;
  1344.  
  1345. begin
  1346.   result := '';
  1347.   size := fieldsize165(fieldnum);
  1348.   start := fieldstart165(fieldnum);
  1349.   done := false;
  1350.   i := 1;
  1351.   while (i<=size) and not done do
  1352.     begin
  1353.       ch := passwordbuf[i+start];
  1354.       if ch=#0 then
  1355.         done := true
  1356.       else
  1357.         result := result+ch;
  1358.       inc(i);
  1359.     end;
  1360.   passwordentry165 := result;
  1361. end;
  1362.  
  1363. begin
  1364.   result := '';
  1365.   found := false;
  1366.  
  1367.   oldfilemode := filemode;
  1368.   if not nofilemode then
  1369.     filemode := $40;   {read only, deny none}
  1370.  
  1371.   assign(passwordf,waffledir+'\admin\'+'password');
  1372.   {$I-}
  1373.   reset(passwordf,1);
  1374.   {$I-}
  1375.  
  1376.   if ioresult=0 then
  1377.     begin
  1378.       blockread(passwordf,passwordbuf,passwordblocksize);
  1379.       while not found and not eof(passwordf) do
  1380.         begin
  1381.           blockread(passwordf,passwordbuf,passwordblocksize);
  1382.           if passwordentry165(1)=userid then
  1383.             begin
  1384.               result := passwordentry165(field);
  1385.               found := true;
  1386.             end;
  1387.         end;
  1388.       close(passwordf);
  1389.     end;
  1390.  
  1391.   filemode := oldfilemode;
  1392.  
  1393.   getpwinfo165 := result;
  1394. end;
  1395.  
  1396. function wafexpand;
  1397.  
  1398. var
  1399.   result: string;
  1400.   i: integer;
  1401.   c: char;
  1402.  
  1403. begin
  1404.   if pos('%',s)=0 then
  1405.     result := s
  1406.   else
  1407.     begin
  1408.       result := '';
  1409.       i := 1;
  1410.       while i<=length(s) do
  1411.         begin
  1412.           if s[i]='%' then
  1413.             begin
  1414.               inc(i);
  1415.               if i<=length(s) then
  1416.                 begin
  1417.                   c := s[i];
  1418.                   case c of
  1419.                     '%': result := result+'%';
  1420.                     'A': result := result+userid;
  1421.                     'W': result := result+fullname;
  1422.                     'n': result := result+node;
  1423.                     'u': result := result+uucpname;
  1424.                     else result := result+'(unknown flag %'+c+')';
  1425.                   end;
  1426.                 end;
  1427.             end
  1428.           else
  1429.             result := result+s[i];
  1430.           inc(i);
  1431.         end;
  1432.     end;
  1433.   wafexpand := result;
  1434. end;
  1435.  
  1436. function makesame;
  1437.  
  1438. var
  1439.   result: boolean;
  1440.  
  1441. begin
  1442.   result := false;
  1443.   if copy(s,1,length(prefix))=prefix then
  1444.     if s<>prefix+shouldbe then
  1445.       begin
  1446.         s := prefix+shouldbe;
  1447.         result := true;
  1448.       end;
  1449.   makesame := result;
  1450. end;
  1451.  
  1452. function expandmail;
  1453.  
  1454. var
  1455.   result: string;
  1456.   newaddressfn: string;
  1457.   newaddressf: text;
  1458.   changed: boolean;
  1459.   s: string;
  1460.  
  1461. begin
  1462.   result := address;
  1463.   changed := false;
  1464.   if (pos('@',address)=0) and (pos('!',address)=0) then
  1465.     begin
  1466.       newaddressfn := waffledir+'\system\'+'aliases';
  1467.  
  1468.       oldfilemode := filemode;
  1469.       if not nofilemode then
  1470.         filemode := $40;   {read only, deny none}
  1471.  
  1472.       assign(newaddressf,newaddressfn);
  1473.       {$I-}
  1474.       reset(newaddressf);
  1475.       {$I+}
  1476.  
  1477.       if ioresult=0 then
  1478.         begin
  1479.           while not changed and not eof(newaddressf) do
  1480.             begin
  1481.               readln(newaddressf,s);
  1482.               if chopfirstw(s)=address then
  1483.                 begin
  1484.                   changed := true;
  1485.                   result := s;
  1486.                 end;
  1487.             end;
  1488.           close(newaddressf);
  1489.         end;
  1490.       if not changed then
  1491.         begin
  1492.           newaddressfn := home+'\aliases';
  1493.  
  1494.           assign(newaddressf,newaddressfn);
  1495.           {$I-}
  1496.           reset(newaddressf);
  1497.           {$I+}
  1498.  
  1499.           if ioresult=0 then
  1500.             begin
  1501.               while not changed and not eof(newaddressf) do
  1502.                 begin
  1503.                   readln(newaddressf,s);
  1504.                   if chopfirstw(s)=address then
  1505.                     begin
  1506.                       changed := true;
  1507.                       result := s;
  1508.                     end;
  1509.                 end;
  1510.               close(newaddressf);
  1511.             end;
  1512.         end;
  1513.  
  1514.       if not changed then
  1515.         begin
  1516.  
  1517. {make sure no chance of security hole - no .. or \ or / or : in address}
  1518.  
  1519. {don't need to make sure it's not a device - last part of name is always}
  1520. {the string 'forward'}
  1521.  
  1522.          if (pos('/',address)=0) and (pos(':',address)=0) and
  1523.           (pos('\',address)=0) and (pos('..',address)=0) then
  1524.            begin
  1525.              newaddressfn := userdir+'\'+address+'\forward';
  1526.  
  1527.              assign(newaddressf,newaddressfn);
  1528.              {$I-}
  1529.              reset(newaddressf);
  1530.              {$I+}
  1531.  
  1532.              if ioresult=0 then
  1533.                begin
  1534.                  if not eof(newaddressf) then
  1535.                    begin
  1536.                      changed := true;
  1537.                      readln(newaddressf,result);
  1538.                    end;
  1539.                  close(newaddressf);
  1540.                end;
  1541.            end;
  1542.         end;
  1543.       filemode := oldfilemode;
  1544.     end;
  1545.   expandmail := result;
  1546. end;
  1547.  
  1548. function screenline;
  1549.  
  1550. var
  1551.   expandeds: string;
  1552.  
  1553. begin
  1554.   expandeds := trim(expand(s));
  1555.   if length(expandeds)<cols then
  1556.     screenline := expandeds
  1557.   else
  1558.     screenline := copy(expandeds,1,cols-2)+'<';
  1559. end;
  1560.  
  1561. function onekey;
  1562.  
  1563. var
  1564.   result: char;
  1565.  
  1566. begin
  1567.   xclreolxy(1,lpp);
  1568.   xwritess(prompt,' ');
  1569.   repeat
  1570.     result := xreadkey;
  1571.   until pos(result,validkeys)<>0;
  1572. {caller has to clear line after - might not want to right away}
  1573.   onekey := result;
  1574. end;
  1575.  
  1576. function ismailgroup;
  1577.  
  1578. begin
  1579.   ismailgroup := (copy(group,1,length(mailprefix))=mailprefix);
  1580. end;
  1581.  
  1582. end.
  1583.