home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #31 / NN_1992_31.iso / spool / alt / sources / 2903 / rn100src.exe / RN.PRG < prev    next >
Encoding:
Text File  |  1992-12-17  |  26.1 KB  |  1,041 lines

  1. /*
  2.  
  3.       rn.prg      newsreader for Waffle
  4.  
  5.       Release 0.85 by Kafka
  6.  
  7. */
  8.  
  9. #include "inkey.ch"
  10. #include "fileio.ch"
  11. #include "directry.ch"
  12. #include "achoice.ch"
  13. #include "setcurs.ch"
  14. #include "memoedit.ch"
  15. #include "marislib.ch"
  16. #include "rn.ch"
  17.  
  18. PARAMETER scrsize
  19. PushDir()
  20. rnmain(scrsize)
  21. PopDir()
  22.  
  23. PROCEDURE rnmain (cScrSize)
  24.  
  25.    LOCAL aGroups := {}
  26.  
  27.  
  28.    setcursor( SC_NONE )
  29.  
  30.    SET DELETED ON
  31.    SET SCOREBOARD OFF
  32.  
  33.    shadowtype(3,8,chr(255) + chr(255) + chr(255))      // set shadow type
  34.  
  35.    readinsert(.t.)
  36.  
  37.    if pcount() > 0
  38.       if cScrSize == "/50"
  39.          setmode(50,80)
  40.       endif
  41.    endif
  42.  
  43.    clear screen
  44.  
  45.    PushVideo()
  46.  
  47.    // get username from the enviroment
  48.    if ! empty(gete("RNUSER"))
  49.       UserName(lower(gete("RNUSER")))
  50.    else
  51.       ask(" Error ", "Environment variable RNUSER not set", {" OK "}, CLR_ERR)
  52.       PopVideo()
  53.       Bye()
  54.    endif
  55.  
  56.    if ! empty(gete("WAFFLE"))
  57.       WafDir(strtran(upper(gete("WAFFLE")), "\STATIC"))
  58.       GotoUserDir()
  59.  
  60.       ScrTitle(" News reader for Waffle ", "RN " + VERSION + " by Kafka │ ", CLR_HDR)
  61.  
  62.       @ 0, 47 say if (empty(FullUserName()), UserName(), left(FullUsername(),20)) color "b/w"
  63.  
  64.       if ! file ((UserDir()+"\RNHIST.DBF"))
  65.          dbcreate((UserDir()+"\RNHIST"), {{"MSGID", "C", 60, 0}})
  66.       endif
  67.  
  68.       use (UserDir()+"\RNHIST") new alias RNHIST
  69.       if ! file(UserDir()+ "\RNHIST.NTX")
  70.          index on lower(RNHIST->MSGID) to (UserDir()+ "\RNHIST")
  71.       else
  72.          set index to (UserDir()+ "\RNHIST")
  73.       endif
  74.  
  75.       aGroups := NwsGrData()
  76.  
  77.       BrowseNewsgroup(1,0,maxrow()-1, maxcol(), aGroups)
  78.  
  79.    else
  80.       ask(" Error ", "Environment variable WAFFLE not set", {" OK "}, CLR_ERR)
  81.    endif
  82.  
  83.    PopVideo()
  84. RETURN
  85.  
  86.  
  87.  
  88. FUNCTION ViewMessage( cNewsGroup, a, i, mode )
  89.    LOCAL aHdr := {}
  90.    LOCAL cPath
  91.    LOCAL lReadNew := .f.
  92.    LOCAl nFile
  93.    LOCAL nLines
  94.    LOCAL l
  95.    LOCAL cText := ""
  96.    LOCAL cLine
  97.    LOCAl n
  98.    LOCAl lCont
  99.    LOCAl aKill := GetKillInfo( cNewsgroup )
  100.  
  101.    PRIVATE nLastKey
  102.    PRIVATE aMsg := a
  103.  
  104.    nLastKey := 0
  105.  
  106.    PushVideo()
  107.  
  108.    KeyBar({"PgUp PgDn  = Browse", "Esc=quit", "Enter = Next message", "Tab/Space - Next New"}, "n/w")
  109.  
  110.    keyboard(chr(0))     // empty lastkey()
  111.  
  112.    lCont := .t.
  113.    while lCont
  114.  
  115.       cPath := NwsGrPath(cNewsgroup) + "\" + a[i,3]
  116.  
  117.       if ( mode == READ_NEW .and. IsNew(a[i, 4]) ) .or. mode == READ_OLD
  118.  
  119.          aHdr := ReadHeader( cPath )
  120.  
  121.          if (GetKill( aKill, aHdr[MSG_FROM], aHdr[MSG_SUBJ] ) # KILL_OLD .and. mode == READ_NEW) .or. mode == READ_OLD
  122.  
  123.             nFile := fopen(cPath, FO_READ + FO_DENYWRITE )
  124.  
  125.             l := .t.
  126.             nLines := 0
  127.             while ! feof(nFile)
  128.                cLine := freadline(nFile)
  129.                if ! empty(cLine)
  130.                   if l
  131.                      nLines += mlcount(cLine,80)
  132.                   endif
  133.                else
  134.                   l := .f.
  135.                   exit
  136.                endif
  137.             enddo
  138.  
  139.             fclose(nFile)
  140.             ++nLines
  141.  
  142.             CurrMsg(aHdr, cPath, cNewsgroup)
  143.             nLastKey := 0
  144.  
  145.             dispbegin()
  146.  
  147.             @ 1,0 clear to maxrow()-1, 79
  148.             @ 1,0 say "Newsgroups: "
  149.             @ row(), col() say aHdr[MSG_NWSG] color "w+/n"
  150.  
  151.             @ 2,0 say "From: "
  152.             @ row(), col() say aHdr[MSG_FROM] color "w+/n"
  153.  
  154.             @ 3,0 say "Subject: "
  155.             @ row(), col() say aHdr[MSG_SUBJ] color "w+/n"
  156.  
  157.             @ 4,0 say "Date: "
  158.             @ row(), col() say aHdr[MSG_DATE] color "w+/n"
  159.  
  160.             dispend()
  161.  
  162.             if filesize(cPath) < 65535
  163.                memoedit(memoread(cPath), 6, 0, maxrow()-2,79, .f., "fViewMessage", ,,nLines+1, 0)
  164.                if IsNew(a[i,4])
  165.                   MarkAsRead(a[i,4])
  166.                endif
  167.             else
  168.                ask(" View message ", " Sorry can't show message " + cPath + ", size > 64K", {" OK "}, CLR_POP)
  169.             endif
  170.  
  171.             do case
  172.                case nLastKey == 0
  173.                   exit
  174.  
  175.                case nLastKey == K_ALT_S
  176.                   KillIt(cNewsgroup, aHdr[MSG_SUBJ], KILL_OLD )
  177.  
  178.                case nLastKey == K_ALT_F
  179.                   KillIt(cNewsgroup, aHdr[MSG_FROM], KILL_OLD)
  180.  
  181.                case nLastKey == K_ALT_P
  182.                   KillIt(cNewsgroup, aHdr[MSG_FROM], KILL_HI)
  183.  
  184.                case nLastKey == K_ALT_H
  185.                   KillIt(cNewsgroup, aHdr[MSG_FROM], KILL_HI)
  186.  
  187.                case nLastKey == K_ALT_E
  188.                   EditKillFile()
  189.  
  190.                case nLastKey == K_ENTER .or. nLastKey == K_PLUS
  191.                   // skip to next message
  192.                   mode := READ_OLD
  193.  
  194.                case nLastKey == K_TAB  .or. nLastKey == K_SPACE  // next new message
  195.                   mode := READ_NEW
  196.  
  197.                case nLastKey == K_MINUS
  198.                   // skip to previous message
  199.                   if i == 1
  200.                      i := 0
  201.                      sound(3500, 4, 80, 25)
  202.                   else
  203.                      i -= 2
  204.                   endif
  205.             endcase
  206.          endif
  207.       endif
  208.       i++
  209.  
  210.       if i > len(a)
  211.          --i
  212.          exit
  213.       endif
  214.  
  215.    enddo
  216.  
  217.    PopVideo()
  218. RETURN ( i )
  219.  
  220.  
  221. FUNCTION fViewMessage (nMode, nLine, nCol)
  222.    LOCAL nRet := 0
  223.    LOCAL nKey := lastkey()
  224.  
  225.    do case
  226.  
  227.       case nMode == ME_UNKEY .or. nMode == ME_UNKEYX .or. nMode == ME_IDLE
  228.          do case
  229.             case nKey == asc("f")      // follow-up
  230.                NwsFollow()
  231.                nRet := ME_IGNORE
  232.  
  233.             case nKey == asc("F")      // forward article
  234.                NwsForward()
  235.                nRet := ME_IGNORE
  236.  
  237.             case nKey == asc("r")      // private reply
  238.                NwsReply(.f.)
  239.                nRet := ME_IGNORE
  240.  
  241.             case nKey == asc("R")      // private reply - ask for adressee
  242.                NwsReply(.t.)
  243.                nRet := ME_IGNORE
  244.  
  245.             case nKey == asc("P")
  246.                pmail()
  247.  
  248.             case nKey == asc("p")      // post
  249.                NwsPost( CurrMsg()[3], aMsg)
  250.                nRet := ME_IGNORE
  251.  
  252.             case nKey == asc("s")      // save article to disk
  253.                NwsSave()
  254.  
  255.             case nKey == asc("C")      // cancel article
  256.  
  257.             case nKey == asc("P")      // print article
  258.                NwsPrint()
  259.  
  260.             case nKey == asc("!")
  261.                shell()
  262.  
  263.             case nKey == K_ALT_S .or. nKey == K_ALT_F .or. nKey == K_ALT_P ;
  264.                         .or. nKey == K_ALT_H .or. nKey == K_ALT_E
  265.  
  266.                nLastKey := nKey
  267.                keyboard chr(K_ESC)
  268.  
  269.             case nKey == K_ESC
  270.                nRet := ME_DEFAULT      // quit
  271.  
  272.             case nKey == K_MINUS
  273.                nLastKey := nKey
  274.                keyboard chr(K_ESC)
  275.  
  276.             case nKey == K_ENTER .or. nKey == K_PLUS
  277.                nLastKey := nKey
  278.                keyboard chr(K_ESC)
  279.  
  280.             case nKey == K_TAB .or. nKey == K_SPACE
  281.                nLastKey := K_TAB
  282.                keyboard chr(K_ESC)
  283.  
  284.          endcase
  285.       case nMode == ME_UNKEYX
  286.  
  287.       case nMode == ME_INIT
  288.  
  289.    endcase
  290. RETURN(nRet)
  291.  
  292.  
  293. PROCEDURE NwsFollow
  294.    LOCAL aMsg := CurrMsg()
  295.    LOCAL aHeader := aMsg[1]
  296.    LOCAL cPath := aMsg[2]
  297.    LOCAL nMsg 
  298.    LOCAL lQuote := .f.
  299.    LOCAL cText := ""
  300.    LOCAL cTmpFile
  301.    LOCAL lSend := .f.
  302.    LOCAL cSubj := ""
  303.    LOCAL nAsk
  304.  
  305.    PushVideo()
  306.    setcursor(SC_NORMAL)
  307.  
  308.    nAsk := Ask(" Follow-up ", " Quote article ?", {" ~Yes ", " ~No "}, CLR_POP)
  309.  
  310.    if nAsk == 1
  311.       lQuote := .t.
  312.    elseif nAsk == 0
  313.       PopVideo()
  314.       RETURN
  315.    endif
  316.  
  317.    nAsk := Ask(" Follow-up ", "Change subject ?", {" ~Yes ", " ~No "}, CLR_POP, 2)
  318.    do case
  319.       case nAsk == 1
  320.          cSubj := GetString(" Change subject ", " Enter new subject ", "(was: " + padr(trim(aHeader[MSG_SUBJ])+ ")",60), , CLR_POP)
  321.       case nAsk == 2
  322.          cSubj := ltrim(aHeader[MSG_SUBJ])
  323.          if left(lower(ltrim(cSubj)),4) == "re: "
  324.             cSubj := substr(cSubj,5)
  325.          endif
  326.       case nAsk == 0
  327.          PopVideo()
  328.          RETURN
  329.    endcase
  330.  
  331.    @ 1,0 clear to maxrow()-1,79
  332.  
  333.    KeyBar({"Esc = Abort", "Ctrl-Enter = Save"}, "n/w")
  334.  
  335.    if lQuote
  336.       nMsg := fopen(cPath, FO_READ + FO_DENYWRITE)
  337.       while ! feof(nMsg) .and. ! empty(freadline(nMsg))
  338.       enddo
  339.  
  340.       cText += "In article " + aHeader[MSG_MSGID] + ", " + From2Name(aHeader[MSG_FROM]) + " writes:" + CR_LF + CR_LF
  341.  
  342.       while ! feof(nMsg)
  343.           cText += upper(left(aHeader[MSG_FROM],2)) + "> " + freadline(nMsg) + CR_LF
  344.       enddo
  345.       fclose( nMsg )
  346.  
  347.       if cText # (cText := Edit(cText, "fNwsFollow"))
  348.          lSend := .t.
  349.       endif
  350.  
  351.    else
  352.       cText := edit(cText, "fNwsFollow")
  353.       if ! empty(cText)
  354.          lSend := .t.
  355.       endif
  356.    endif
  357.  
  358.    if lSend
  359.       cTmpFile := MkTemp()
  360.       cText = MakeHeader(aHeader,"f", cSubj) + hardcr(cText)
  361.       if file(UserDir()+ "\sig")
  362.          cText += CR_LF + CR_LF + memoread(UserDir() + "\sig")
  363.       endif
  364.       memowrit(cTmpFile, cText)
  365.       ScrStat("Posting follow-up in " + if(empty(aHeader[MSG_FUT]), aHeader[MSG_NWSG], aHeader[MSG_FUT]), CLR_HDR)
  366.       swpruncmd("rnews < " + cTmpFile )
  367.    else
  368.       ask(" Follow-up ", " Empty or unedited article not posted", {" OK "}, CLR_POP)
  369.    endif
  370.  
  371.    PopVideo()
  372. RETURN
  373.  
  374.  
  375. FUNCTION fNwsFollow (nMode, nLine, nCol)
  376.    LOCAL nRet := 0
  377.    LOCAL nKey := lastkey()
  378.  
  379.    do case
  380.       case nMode == ME_UNKEY .or. nMode == ME_UNKEYX .or. nMode == ME_IDLE
  381.          do case
  382.             case nKey == K_ESC
  383.                if ask(" Edit ", "Abort edit ?", {" ~Yes ", " ~No "}, CLR_POP) == 1
  384.                   nRet := ME_DEFAULT      // quit
  385.                endif
  386.  
  387.             case nKey == K_CTRL_RET
  388.                if ask(" Edit ", "Send ?", {" ~Yes ", " ~No "}, CLR_POP) == 1
  389.                   keyboard chr(K_CTRL_W)
  390.                endif
  391.  
  392.          endcase
  393.  
  394.       case nMode == ME_INIT
  395.  
  396.    endcase
  397. RETURN(nRet)
  398.  
  399.  
  400. PROCEDURE NwsReply(lAsk)
  401.    LOCAL aMsg := CurrMsg()
  402.    LOCAL aHeader := aMsg[1]
  403.    LOCAL cPath := aMsg[2]
  404.    LOCAL nMsg 
  405.    LOCAL lQuote := .f.
  406.    LOCAL cText := ""
  407.    LOCAL cTmpFile
  408.    LOCAL lSend := .f.
  409.    LOCAL cAdressee
  410.    LOCAL nAsk
  411.  
  412.    PushVideo()
  413.  
  414.    setcursor(SC_NORMAL)
  415.  
  416.    if lAsk
  417.       cAdressee := trim(GetString(" Private reply ", " Enter adressee ", space(40),, CLR_POP))
  418.       if lastkey() == K_ESC
  419.          PopVideo()
  420.          RETURN
  421.       endif
  422.    else
  423.       cAdressee := From2Address(aHeader[MSG_FROM])
  424.    endif
  425.  
  426.  
  427.    nAsk := ask(" Private reply ", " Quote article ?", {" ~Yes ", " ~No "}, CLR_POP)
  428.    if nAsk == 0
  429.       PopVideo()
  430.       RETURN
  431.    elseif nAsk == 1
  432.       lQuote := .t.
  433.    endif
  434.  
  435.    @ 1,0 clear to maxrow()-1,79
  436.  
  437.    KeyBar({"Esc = Abort", "Ctrl-Enter = Save"}, "n/w")
  438.  
  439.    if lQuote
  440.       nMsg := fopen(cPath, FO_READ + FO_DENYWRITE)
  441.  
  442.       while ! feof(nMsg) .and. ! empty(freadline(nMsg))
  443.       enddo
  444.  
  445.       cText += "In article " + aHeader[MSG_MSGID] + ", you write the following:" + CR_LF + CR_LF
  446.       while ! feof(nMsg)
  447.           cText += upper(left(aHeader[MSG_FROM],2)) + "> " + freadline(nMsg) + CR_LF
  448.       enddo
  449.       fclose(nMsg)
  450.  
  451.       if cText # (cText := Edit(cText, "fNwsFollow"))
  452.          lSend := .t.
  453.       endif
  454.  
  455.    else
  456.       cText := Edit("", "fNwsFollow")
  457.       if ! empty(cText)
  458.          lSend := .t.
  459.       endif
  460.    endif
  461.  
  462.    if lSend
  463.       cText = MakeHeader(aHeader,"r",,cAdressee) + hardcr(cText)
  464.       if file(UserDir()+ "\sig")
  465.          cText += CR_LF + CR_LF + memoread(UserDir() + "\sig")
  466.       endif
  467.  
  468.       cTmpFile := MkTemp()
  469.       memowrit(cTmpFile, cText)
  470.       ScrStat("Sending mail to " + cAdressee, CLR_HDR)
  471.       
  472.       swpruncmd("rmail -f" + UserName() ;
  473.               + " " + cAdressee + " < " + cTmpFile )
  474.  
  475.     else
  476.       ask(" Private reply ", " Empty or unedited message not mailed", {" OK "}, CLR_POP)
  477.    endif
  478.  
  479.    PopVideo()
  480. RETURN
  481.  
  482.  
  483. PROCEDURE NwsPost(cNwsGrp, a)     // Post article
  484.    LOCAL lQuote := .f.
  485.    LOCAL cText := ""
  486.    LOCAL cTmpFile
  487.    LOCAL lSend := .f.
  488.    LOCAL cSubj
  489.    LOCAL cHeader := ""
  490.    LOCAL cCrossPost := space(60)
  491.    LOCAL cFupTo := ""
  492.  
  493.    PushVideo()
  494.    setcursor(SC_NORMAL)
  495.  
  496.    if ask(" Post ", " Post article in " + cNwsGrp + " ?", { " YES ", " NO "}, CLR_POP) == 2 .or. lastkey() == K_ESC
  497.       PopVideo()
  498.       RETURN
  499.    endif
  500.  
  501.    cSubj := GetString(" Post article ", "Enter subject", space(60),, CLR_POP)
  502.  
  503.    if empty(cSubj)
  504.       PopVideo()
  505.       RETURN
  506.    endif
  507.  
  508.    if ask(" Post article ", " Crosspost to other newsgroups ?", {" Yes ", " No " }, CLR_POP, 2) == 1
  509.       cCrossPost := trim(GetString(" Post article ", " Crosspost to (delimited by a comma): ", cCrossPost, , CLR_POP))
  510.       if lastkey() == K_ESC
  511.          PopVideo()
  512.          RETURN
  513.       endif
  514.    endif
  515.  
  516.    if ask(" Post article ", " Enter Follow-up to ?", {" Yes ", " No " }, CLR_POP, 2) == 1
  517.       cFupTo := GetString(" Post article ", "Follow-up to", space(60),, CLR_POP)
  518.       if lastkey() == K_ESC
  519.          PopVideo()
  520.          RETURN
  521.       endif
  522.    endif
  523.  
  524.    @ 1,0 clear to maxrow()-1,79
  525.  
  526.    KeyBar({"Esc = Abort", "Ctrl-Enter = Save"}, "n/w")
  527.  
  528.    cText := Edit("", "fNwsFollow")
  529.    if ! empty(cText)
  530.       lSend := .t.
  531.    endif
  532.  
  533.    if lSend
  534.  
  535.       cTmpFile := MkTemp()
  536.       cHeader += "Path: " + GetStatic("uucpname") + "!" + UserName() + CR_LF
  537.       cHeader += "From: " + UserName() +"@"+ GetStatic("node") + " (" + FullUserName() + ")"  + CR_LF
  538.  
  539.       if ! empty(cCrossPost)
  540.          cHeader += "Newsgroups: " + trim(cNwsGrp) + "," + cCrossPost + CR_LF
  541.       else
  542.          cHeader += "Newsgroups: " + trim(cNwsGrp) + CR_LF
  543.       endif
  544.  
  545.       if ! empty(cFupTo)
  546.          cHeader += "Followup-To: " + trim(cFupTo) + CR_LF
  547.       endif
  548.  
  549.  
  550.       cHeader += "Subject: " + trim(cSubj) + CR_LF
  551.       cHeader += "Date: " + left(cdow(date()),3) + ", " +    ;
  552.                   + str(day(date()),2) + " " ;
  553.                   + left(cmonth(date()), 3) + " "   ;
  554.                   + substr(dtoc(date()), 7,2) + " "   ;
  555.                   + time() + " ";
  556.                   + gete("TZ") + CR_LF
  557.  
  558.       cHeader += "Message-ID: " + MkMsgId() + CR_LF
  559.  
  560.       if cNwsGrp == "alt.hackers"
  561.          cHeader += "Approved: " + "Auto-approved by RN by Kafka@desert.hacktic.nl" + CR_LF
  562.       endif
  563.  
  564.       cHeader += "Organisation: " + GetStatic("organ") + CR_LF + CR_LF
  565.  
  566.       if file(UserDir()+ "\sig")
  567.          cText += CR_LF + CR_LF + memoread(UserDir() + "\sig")
  568.       endif
  569.  
  570.       memowrit(cTmpFile, cHeader + hardcr(cText) )
  571. //      ScrStat("Posting article in " + if(empty(cFupTo), alltrim(cNwsGrp), cFupTo), CLR_HDR)
  572.       ScrStat("Posting article in " + alltrim(cNwsGrp), CLR_HDR)
  573.       swpruncmd("rnews < " + cTmpFile )
  574.     else
  575.       ask(" Post ", " Empty article not posted ", {" OK "}, CLR_POP)
  576.    endif
  577.  
  578.    PopVideo()
  579.  
  580. RETURN
  581.  
  582.  
  583. PROCEDURE NwsSave
  584.    LOCAL aMsg := CurrMsg()
  585.    LOCAL aHeader := aMsg[1]
  586.    LOCAL cPath := aMsg[2]
  587.    LOCAL cFile
  588.    LOCAL cSavDir
  589.  
  590.    PushVideo()
  591.    setcursor(SC_NORMAL)
  592.  
  593.    cFile := trim(GetString(" Save article ", "Enter filename", space(60), "@!", CLR_POP))
  594.    if ! empty(cFile)
  595.       chdir(cSavDir)
  596.       ScrStat("Saving article to " + cFile, CLR_HDR)
  597.       fcopy(cPath, cFile)
  598.       chdir(cSavDir)
  599.    endif
  600.  
  601.    PopVideo()
  602. RETURN
  603.  
  604.  
  605. FUNCTION NwsPrint()
  606.    LOCAL aMsg := CurrMsg()
  607.    LOCAL aHeader := aMsg[1]
  608.    LOCAL cPath := aMsg[2]
  609.    LOCAL nPrinter := 0
  610.  
  611.    if ask(" View article ", "Print current article ?", { " ~Yes ", " ~No " }, CLR_POP) == 1
  612.       nPrinter := ask(" Print article ", " Print to ", {" LPT1 ", " LPT2 ", " LPT3 "}, "n/w,w/n")
  613.       do case
  614.          case nPrinter == 1
  615.             fcopy(cPath, "LPT1")
  616.          case nPrinter == 2
  617.             fcopy(cPath, "LPT2")
  618.          case nPrinter == 3
  619.             fcopy(cPath, "LPT3")
  620.          otherwise
  621.  
  622.       endcase
  623.    endif
  624. RETURN(NIL)
  625.  
  626.  
  627. FUNCTION NwsGrPath(cNwsGrp)
  628.    LOCAL cPath := ""
  629.    LOCAL n
  630.    LOCAL fHandle
  631.    LOCAL c
  632.    LOCAL nPos
  633.    STATIC aPath
  634.  
  635.    if aPath == NIL
  636.       aPath := {}
  637.    endif
  638.  
  639.    nPos := ascan(aPath, {|x| x[1] == cNwsGrp } )
  640.  
  641.    if nPos > 0
  642.       cPath := aPath[nPos, 2]
  643.    else
  644.       fHandle := fopen ( WafDir() + "\usenet", FO_READ + FO_DENYWRITE)
  645.       while ! feof( fHandle )
  646.          c := freadline( fHandle )
  647.          if left(c, 1) # "#"
  648.             if token(c," ",1) == cNwsGrp
  649.                if "/dir=" $ lower(c)
  650.                   nPos := at("/dir=", lower(c)) + 5
  651.                   cPath := token(substr(c, nPos), " ", 1)
  652.                   aadd(aPath, {cNwsGrp, cPath})
  653.                   exit
  654.                else
  655.                   cPath := NewsDir()
  656.                   for n := 1 to numtoken(trim(cNwsGrp), ".")
  657.                      cPath += "\" + left(token(trim(cNwsGrp),".", n),8)
  658.                   next
  659.                   if right(cPath,1) == "\"
  660.                      cPath = substr(cPath, 1, len(cPath)-1)
  661.                   endif
  662.                   aadd(aPath, {cNwsGrp, cPath})
  663.                   exit
  664.                endif
  665.             endif
  666.          endif
  667.       enddo
  668.       fClose ( fHandle )
  669.    endif
  670.  
  671.    if empty(cPath)
  672.       ask(" Warning! ", "Joined newsgroup " + cNwsGrp + " isn't contained in '" + WafDir() + "\usenet" + "'",, CLR_ERR )
  673.    endif
  674. RETURN(cPath)
  675.  
  676.  
  677. FUNCTION NwsGrData
  678.    STATIC aGroups := {}             // array for groups
  679.    LOCAL fHandle
  680.    LOCAL nNewMsg                   // new messages
  681.    LOCAL nTotMsg
  682.    LOCAL cLine
  683.    LOCAL nJoin
  684.    LOCAl cNwsgr
  685.  
  686.    if len(aGroups) < 1
  687.  
  688.       ScrStat("Retrieving newsgroups information", "*n/w")
  689.  
  690.       if (nJoin := fopen(UserDir() + "\join")) # -1
  691.          while ! feof(nJoin)
  692.  
  693.             cNwsgr := alltrim(token(freadline( nJoin ), " ", 1))
  694.  
  695.             if left(cNwsgr, 1) # "#" .and. ! empty(cNwsgr)
  696.  
  697.                nTotMsg := 0
  698.                nNewMsg := 0
  699.  
  700.                aadd(aGroups, {cNwsgr,{},0,0 })
  701.                fHandle := fopen( NwsGrPath( cNwsgr ) + "\index.rn", FO_READ + FO_DENYWRITE )
  702.  
  703.                while ! feof( fHandle )
  704.                   nTotMsg ++
  705.                   cLine := freadline( fHandle )
  706.                   if IsNew( token(cLine, chr(9), 4))
  707.                      nNewMsg++
  708.                   endif
  709.                enddo
  710.  
  711.                aGroups[len(aGroups),3] := nTotMsg
  712.                aGroups[len(aGroups),4] := nNewMsg
  713.  
  714.                fclose( fHandle )
  715.  
  716.             endif
  717.          enddo
  718.       else
  719.          ask("Error", "Could not open " + UserDir() + "\join" ,, CLR_ERR)
  720.          quit
  721.       endif
  722.  
  723.       fclose(nJoin )
  724.    endif
  725.  
  726. RETURN (aGroups)
  727.  
  728.  
  729. FUNCTION IsNew(cMsgId)       // Determines if a message is new or not
  730.    LOCAL lRet
  731.    lRet := RNHIST->(dbseek(lower(alltrim(cMsgId))))
  732. RETURN (! lRet)
  733.  
  734.  
  735. FUNCTION UserDir()
  736. RETURN(GetStatic("user") + "\" + UserName())
  737.  
  738.  
  739. FUNCTION ImportJoin
  740.    LOCAL nFile, cNg, nMsgNo, n, cLine
  741.  
  742.    dbcreate(UserDir()+"\rnjoin", {{"NG_CODE","N", 5, 0}, {"NG_NAME", "C", 30, 0}})
  743.    dbcreate(UserDir()+"\rnhist", {{"MSGID","C", 60, 0}})
  744.  
  745.    use (UserDir()+"\RNHIST") new alias RNHIST
  746.    index on RNHIST->MSGID to (UserDir()+ "\RNHIST")
  747.  
  748.    use (UserDir()+"\RNJOIN") new alias RNJOIN
  749.    index on lower(RNJOIN->NG_NAME) to (UserDir()+ "\RNJOIN")
  750.  
  751.    if (nFile := fopen(UserDir()+"\JOIN")) == -1
  752.       ask(" Error ", "Could not open" + UserDir()+"\JOIN", {" OK "}, CLR_ERR)
  753.       ? "RTFM!..."
  754.       QUIT
  755.    endif
  756.  
  757.    while ! feof(nFile)
  758.       cLine := freadline(nFile)
  759.       cNg := token(cLine, " ", 1)
  760.       nMsgNo := val(token(cLine, " ", 2))
  761.  
  762.       if ! empty(cNg)
  763.          ScrStat("Adding " + cNg, "n/w")
  764.          RNJOIN->(dbappend())
  765.          replace RNJOIN->NG_NAME with cNg, ;
  766.                RNJOIN->NG_CODE with recno()
  767.       endif
  768.    enddo
  769.  
  770.    fclose(nFile)
  771.  
  772.    RNJOIN->(dbclosearea())
  773.    RNHIST->(dbclosearea())
  774.  
  775. RETURN(NIL)
  776.  
  777.  
  778. PROCEDURE Help(cProc)
  779.  
  780.    if cProc == "HELP"
  781.       RETURN
  782.    endif
  783.  
  784.    PushVideo()
  785.  
  786.    setcolor("w+/g")
  787.  
  788.    shadowbox(2,11,21,67, 1, .t.)
  789.    SayCenter(2," Help! ", "gr+/g")
  790.  
  791.    @  3, 13 say "            !  -  shell"
  792.    @  4, 13 say "            f  -  follow up"
  793.    @  5, 13 say "            p  -  post"
  794.    @  6, 13 say "            r  -  private reply"
  795.    @  7, 13 say "            R  -  private reply - ask for addressee"
  796.    @  8, 13 say "            c  -  catch up (mark whole group as read)"
  797.    @  9, 13 say "    tab/space  -  next NEW article"
  798.    @ 10, 13 say "       return  -  next article"
  799.    @ 11, 13 say "            P  -  start Pmail"
  800.    @ 12, 13 say "        Alt-S  -  kill subject"
  801.    @ 13, 13 say "        Alt-F  -  kill poster"
  802.    @ 14, 13 say "        Alt-P  -  hilite poster"
  803.    @ 15, 13 say "        Alt-H  -  hilite subject"
  804.    @ 16, 13 say "            S  -  sort article list"
  805.    @ 17, 13 say "            s  -  save article in file "
  806.    @ 18, 13 say "            F  -  forward article "
  807.    saycenter(21, " RN " + VERSION + ", Phreeware 1993 by kafka@desert.hacktic.nl ", "gr+/g")
  808.  
  809.    saycenter(20, "Press the 'any' key", "n/g")
  810.    inkey(0)
  811.  
  812.    PopVideo()
  813. RETURN
  814.  
  815.  
  816. PROCEDURE GotoUserDir()
  817.    LOCAL cDir := UserDir()
  818.    setdrive(left(cDir, 2))
  819.    chdir(substr(cDir, 3))
  820. RETURN
  821.  
  822.  
  823. FUNCTION Shell
  824.    PushVideo()
  825.    setcursor (SC_NORMAL )
  826.    clear screen
  827.    ? "Type EXIT to return to RN"
  828.    swpruncmd()
  829.    PopVideo()
  830. RETURN("")
  831.  
  832.  
  833. PROCEDURE pmail
  834.    PushVideo()
  835.    setcursor(SC_NORMAL)
  836.    swpruncmd("PMAIL")
  837.    PopVideo()
  838. RETURN
  839.  
  840.  
  841. PROCEDURE NwsForward
  842.    LOCAL aMsg := CurrMsg()
  843.    LOCAL aHeader := aMsg[1]
  844.    LOCAL cPath := aMsg[2]
  845.    LOCAL cText := ""
  846.    LOCAL cTmpFile
  847.    LOCAL cFwTo := ""
  848.  
  849.    PushVideo()
  850.  
  851.    if Ask(" Forward posting ", " Forward article ?", { " ~Yes ", " ~No " }, CLR_POP) == 1
  852.       cFwTo := trim(GetString(" Forward article ", "Forward article to :", space(60), ,CLR_POP))
  853.       cText += memoread(cPath)
  854.       cTmpFile := MkTemp()
  855.       memowrit(cTmpFile, cText)
  856.       ScrStat("Forwarding message to " + cFwTo, CLR_HDR)
  857.       swpruncmd("rmail -f" + UserName() ;
  858.          + " " + cFwTo + " < " + cTmpFile )
  859.    endif
  860.  
  861.    PopVideo()
  862. RETURN
  863.  
  864.  
  865.  
  866. FUNCTION ReadNewsgroup (cNewsgroup, mode )
  867.    LOCAL cPath := NwsGrPath(token(cNewsgroup," ",1))
  868.    LOCAL fHandle
  869.    LOCAL aMsgLst := {}
  870.    LOCAL cLine
  871.    LOCAL aKill := GetKillInfo( cNewsgroup )
  872.  
  873.    if isdir( cPath )     // goto newsgroup's dir
  874.  
  875.       PushVideo()
  876.  
  877.       ScrStat(" Retrieving newsgroup (be patient) ", "*n/w" )
  878.  
  879.       // Read header info into array
  880.  
  881.       fHandle := fopen( cPath + "\index.rn", FO_READ + FO_DENYWRITE )
  882.  
  883.       while ! feof( fHandle )
  884.          cLine := freadline( fHandle )
  885.          AddMsg( aMsgLst, cLine, cNewsgroup )
  886.       enddo
  887.  
  888.       fclose( fHandle )
  889.  
  890.       do case
  891.          case mode == READ_NG_NORMAL
  892.             BrowseMessages( 2, 0, maxrow() -1 , maxcol(), aMsgLst, cNewsgroup )
  893.          case mode == READ_NG_CATCHUP
  894.             Catchup ( aMsgLst )
  895.       endcase
  896.  
  897.       PopVideo()
  898.  
  899.    endif
  900. RETURN ( aMsgLst)
  901.  
  902.  
  903.  
  904. FUNCTION GetKillInfo ( cNewsgroup )
  905.    LOCAL aKill := {}
  906.    LOCAL fHandle
  907.    LOCAL cPath := Userdir() + "\" + "kill"
  908.    LOCAL cLine
  909.    LOCAL nPos
  910.  
  911.    if file ( cPath )
  912.       fHandle := fopen( cPath , FO_READ + FO_DENYWRITE )
  913.       while ! feof( fHandle )
  914.          cLine := freadline( fHandle )
  915.          if left( cLine, 1 ) # "#"
  916.             if alltrim(token(cLine, "/", 1)) == alltrim( cNewsgroup ) ;
  917.                .or. alltrim(token(cLine, "/", 1)) == "*"
  918.                aadd(aKill, {})
  919.                nPos := len(aKill)
  920.                aadd(aKill[nPos], lower(token(cLine, "/", 2)))
  921.                aadd(aKill[nPos], token(token(cLine, "/", 3),":", 1))
  922.                aadd(aKill[nPos], lower(token(cLine, ":", 2)))
  923.             endif
  924.          endif
  925.       enddo
  926.       fclose( fHandle )
  927.    endif
  928.  
  929. RETURN ( aKill )
  930.  
  931.  
  932. PROCEDURE KillIt( cNewsgroup, cString, Action )
  933.    LOCAL fHandle
  934.    LOCAL cPath := Userdir() + "\" + "kill"
  935.    LOCAL cLine
  936.  
  937.    if  ! file ( cPath )
  938.       fHandle := fcreate( cPath , FC_NORMAL)
  939.       fclose( fHandle )
  940.    endif
  941.  
  942.    fHandle := fopen( cPath , FO_READWRITE + FO_DENYWRITE)
  943.  
  944.    FileBottom( fHandle)
  945.  
  946.    cLine := alltrim(cNewsgroup) + "/" + lower(strtran(alltrim(cString), "re:")) + "/h:"
  947.  
  948.    do case
  949.       case Action == KILL_OLD
  950.          cLine += 'j'
  951.       case Action == KILL_HI
  952.          cLine += 'h'
  953.    endcase
  954.  
  955.    fwriteline(fHandle, cLine )
  956.  
  957.    fclose( fHandle )
  958.  
  959. RETURN
  960.  
  961.  
  962. FUNCTION GetKill ( aKill, cFrom, cSubj )
  963.    LOCAL n
  964.    LOCAL nRet := KILL_NONE
  965.  
  966.    for n := 1 to len( aKill )
  967.       if aKill[n, 1] $ lower(cFrom + cSubj)
  968.          do case
  969.             case aKill[n,3] == 'm'
  970.                nRet := ( KILL_NEW )
  971.             case aKill[n,3] == 'j'
  972.                nRet := ( KILL_OLD )
  973.             case aKill[n,3] == 'h'
  974.                nRet := ( KILL_HI )
  975.          endcase
  976.       endif
  977.    next
  978.  
  979. RETURN ( nRet )
  980.  
  981.  
  982.  
  983. PROCEDURE Catchup( a )
  984.    LOCAL n
  985.    PushVideo()
  986.    ScrStat(" Marking all messages as read ", "n*/w" )
  987.  
  988.    for n := 1 to len(a)
  989.       if IsNew (a[n, 4])
  990.          MarkAsRead(a[n, 4])
  991.       endif
  992.    next
  993.  
  994.    PopVideo()
  995. RETURN
  996.  
  997.  
  998. PROCEDURE EditKillFile
  999.    LOCAL cPath := Userdir() + "\" + "kill"
  1000.    LOCAL cText
  1001.  
  1002.    PushVideo()
  1003.    setcursor ( SC_NORMAL )
  1004.    ScrTitle( "Edit  kill file ", "RN " + VERSION + " by Kafka │ " + FullUserName(), CLR_HDR)
  1005.    KeyBar({" PgUp PgDn Home End Ctrl-Home Ctrl-End = Browse", "Ctrl-Enter = Save", "Esc = Abort"}, CLR_HDR)
  1006.  
  1007.    if file ( cPath )
  1008.       cText := memoread( cPath )
  1009.    else
  1010.       cText := ""
  1011.    endif
  1012.  
  1013.    if cText # (cText := Edit(cText, "fEditKill"))
  1014.       memowrit ( cPath, cText )
  1015.    endif
  1016.  
  1017.    PopVideo()
  1018. RETURN
  1019.  
  1020. FUNCTION fEditKill (nMode, nLine, nCol)
  1021.    LOCAL nRet := 0
  1022.    LOCAL nKey := lastkey()
  1023.  
  1024.    do case
  1025.       case nMode == ME_UNKEY .or. nMode == ME_UNKEYX .or. nMode == ME_IDLE
  1026.          do case
  1027.             case nKey == K_ESC
  1028.                if ask(" Edit ", "Abort edit ?", {" ~Yes ", " ~No "}, CLR_POP) == 1
  1029.                   nRet := ME_DEFAULT      // quit
  1030.                endif
  1031.  
  1032.             case nKey == K_CTRL_RET
  1033.                if ask(" Edit ", "Save ?", {" ~Yes ", " ~No "}, CLR_POP) == 1
  1034.                   keyboard chr(K_CTRL_W)
  1035.                endif
  1036.  
  1037.          endcase
  1038.    endcase
  1039. RETURN( nRet )
  1040.  
  1041.