home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 35 Internet / 35-Internet.zip / rxnew11a.zip / rexxnews.cmd < prev    next >
OS/2 REXX Batch file  |  1993-04-29  |  67KB  |  2,108 lines

  1. /*------------------------------------------------------------------
  2.  * RexxNews 
  3.  *------------------------------------------------------------------
  4.  * 04-27-93 by Albert L. Crosby
  5.  *------------------------------------------------------------------
  6.  * Portions of this package are based on rnr.cmd :
  7.  *------------------------------------------------------------------
  8.  * 08-09-92 by Patrick J. Mueller
  9.  *------------------------------------------------------------------*/
  10.  
  11. "@echo off"
  12.  
  13. settings.version="RexxNews v. 1.1a by Albert Crosby"
  14.  
  15. /* Settings.varnames includes the variables that can be set and displayed
  16.    with the RexxNews SET command.  The format is "variablename      type".
  17.    The types can be any valid REXX datatype name.  The 'S' datatype is
  18.    redefined as any String.  */
  19.  
  20. settings.varnames="",
  21.                   "server               S",    /* Name of the current/default NNTP server                    */
  22.                   "rows                 W",    /* Number of rows on the screen                               */
  23.                   "cols                 W",    /* Number of columns on the screen                            */
  24.                   "overlap              W",    /* Number of lines to overlap when displaying an list         */
  25.                   "username             S",    /* Email name for the OS/2 user (at the os/2 workstation)     */
  26.                   "hostname             S",    /* Host name for the OS/2 workstation                         */
  27.                   "fullname             S",    /* Full name for the OS/2 user                                */
  28.                   "organization         S",    /* Organization header field value                            */
  29.                   "disclaimer           S",    /* Disclaimer header field value                              */
  30.                   "replyto              S",    /* Reply-To header field value                                */
  31.                   "timezone             S",    /* Time Zone abbreviation                                     */
  32.                   "quotechar            S",    /* Character(s) to be used when quoting articles              */
  33.                   "signature            S",    /* Name of signature file to include in posts/mail            */
  34.                   "displayatgroup       B",    /* Display the first article after the group command?         */
  35.                   "nextgroupafterlast   B",    /* Issue NEXTGROUP after the last article in a group?         */
  36.                   "nextgroupatconnect   B",    /* Issue NEXTGROUP immeadiately on connecting to server?      */
  37.                   "newarticlesatgroup   B",    /* List SUBJECTS for new articles after the group command?    */
  38.                   "newgroupsatconnect   B",    /* List NEWGROUPS immeadiately on connecting to server?       */
  39.                   "displayheaders       B",    /* Display headers when listing articles?                     */
  40.                   "editor               S",    /* Name of the editor to use for composing posts              */
  41.                   "rexxnewsdir          S",    /* Location of the REXXNEWS executeables and help files       */
  42.                   "etcdir               S",    /* Location for ETC files (NEWSRC and alternate server CFG)   */
  43.                   "tempdir              S",    /* Location for placing temporary files                       */
  44.                   "groupname            S",    /* Name of the current group                                  */
  45.                   "groupstat            S",    /* Subscription status of the current group                   */
  46.                   "groupnewsrcline      W",    /* Line of the current group within the NEWSRC file           */
  47.                   "grouphighest         W",    /* Highest article that has been seen in current group        */
  48.                   "newsrcdate           W",    /* Date the NEWSRC file was last saved                        */
  49.                   "newsrctime           W",    /* Time the NEWSRC file was last saved                        */
  50.                   "newsrcname           S",    /* Name of the current NEWSRC file                            */
  51.                   "newuser              B",    /* Is this the first time the user has ran RexxNews?          */
  52.                   "askifsubmore         W",    /* Max groups to subscribe to automatically w/o prompting     */
  53.                   "postingok            B",    /* Does the current server allow posting?                     */
  54.                   "usexhdr              B",    /* Can the XHDR command be used with the current server?      */
  55.                   "shellescapechar      S",    /* Charachter used to shell to OS/2 at the command prompt     */
  56.                   "savenewsrcatexit     B",    /* Save the NEWSRC file in case of an abnormal exit           */
  57.                   "sortcommand          S",    /* Name of the command to use when sorting                    */
  58.                   "sortmaxbytes         W",    /* Maximum number of bytes the sort program can handle        */
  59.                   ""
  60.  
  61. parse source . . name
  62. settings.rexxnewsdir=filespec('drive',name)||filespec('path',name)
  63.  
  64. settings.etcdir=value('etc',,'OS2ENVIRONMENT')
  65. settings.tempdir=value('tmp',,'OS2ENVIRONMENT')
  66. if settings.tempdir="" then settings.tempdir=value('temp',,'OS2ENVIRONMENT')
  67. if settings.tempdir="" then settings.tempdir=settings.etcdir
  68.  
  69. /*USER DEFINEABLE CONSTANTS & DEFAULT VALUES */
  70. settings.newsrcname='newsrc' 
  71. settings.newgroupsatconnect=1
  72. settings.overlap=2           
  73. settings.displayheaders=1    
  74. settings.displayatgroup=1    
  75. settings.savenewsrcatexit=1
  76. settings.newarticlesatgroup=1
  77. settings.editor="epm"
  78. settings.username="os2user"
  79. settings.fullname="unknown"
  80. settings.replyto=""
  81. settings.organization=""
  82. settings.disclaimer=""
  83. settings.signature=""
  84. settings.timezone="CST"
  85. settings.quotechar=">"
  86. settings.askifsubmore=50
  87. settings.nextgroupafterlast=1
  88. settings.nextgroupatconnect=1
  89. settings.shellescapechar="!"
  90. settings.sortcommand="sort"
  91. settings.sortmaxbytes=64512
  92. /****************************/
  93. /*Variables that shouldn't be changed by the user... */
  94. settings.groupnewsrcline=0
  95. settings.current=0
  96. settings.unread=0
  97. settings.groupname=""
  98. first=0
  99. last=0
  100. remain=0
  101. articleavailable=0
  102.  
  103. signal on halt name shutdown
  104.  
  105. parse arg serverarg .
  106.  
  107. call opening
  108.  
  109. /*------------------------------------------------------------------
  110.  * initialize system function package
  111.  *------------------------------------------------------------------*/
  112. if RxFuncQuery("SysLoadFuncs") then
  113.    do
  114.    rc = RxFuncAdd("SysLoadFuncs","RexxUtil","SysLoadFuncs")
  115.    if rc\=0 then
  116.       do
  117.       say "Error loading the RexxUtil package.  There is a problem with your"
  118.       say "OS/2 installation."
  119.       exit -1
  120.       end
  121.    rc = SysLoadFuncs()
  122.    end
  123.  
  124. /*------------------------------------------------------------------
  125.  * initialize socket function package
  126.  *------------------------------------------------------------------*/
  127. if RxFuncQuery("SockLoadFuncs") then
  128.    do
  129.    rc = RxFuncAdd("SockLoadFuncs","RxSock","SockLoadFuncs")
  130.    if rc\=0 then
  131.       do
  132.       say "Error loading the rxSock package.  rxSock is an extension to REXX"
  133.       say "that allows it to utilize TCPIP and BSD-style Sockets.  You can"
  134.       say "obtain rxSock via anonymous FTP from software.watson.ibm.com or"
  135.       say "ftp-os2.nmsu.edu."
  136.       exit -1
  137.       end
  138.    rc = SockLoadFuncs()
  139.    end
  140.  
  141. /* Get info about this machine */
  142. addr = SockGetHostId()
  143. rc   = SockGetHostByAddr(addr,"host.!")
  144. settings.hostname=host.!name
  145. settings.realhostname=host.!name
  146.  
  147. parse value SysTextScreenSize() with settings.rows settings.cols
  148. settings.originalrows=settings.rows
  149. settings.originalcols=settings.cols
  150.  
  151. call opening
  152.  
  153. call loadsettings addslash(settings.rexxnewsdir)||'REXXNEWS.CFG'
  154. call loadsettings addslash(settings.etcdir)||'REXXNEWS.CFG'
  155. call loadsettings 'REXXNEWS.CFG'
  156.  
  157. altserver=0
  158.  
  159. if serverarg\="" then 
  160.    do
  161.    if settings.server\="" & translate(serverarg)\=translate(settings.server) then
  162.       do
  163.       altserver=1
  164.       settings.newsrcname=addslash(settings.etcdir)||left(translate(serverarg,'-','.'),8,'_')||'.NRC'
  165.       call loadsettings addslash(settings.etcdir)||left(translate(serverarg,'-','.'),8,'_')||'.CFG'
  166.       end
  167.    settings.server=serverarg
  168.    end
  169.  
  170. if settings.newsrcname="newsrc" then settings.newsrcname=addslash(settings.etcdir)||settings.newsrcname
  171.  
  172. settings.newuser=0
  173. if \loadnewsrc() then settings.newuser=\altserver
  174.  
  175. if (settings.server = "") then
  176.    do
  177.    say "Expecting a news server name to be passed as a parameter or in the the"
  178.    say "configuration file."
  179.    exit 1
  180.    end
  181.  
  182. say
  183. say 'Connecting to server...'
  184.  
  185. call ConnectServer settings.server
  186.  
  187. /* Don't show new users all of the groups that exist (unless they ask)... */
  188. if settings.newuser then 
  189.    call help 'intro'
  190. else 
  191.    do
  192.    if settings.newgroupsatconnect then
  193.       do
  194.       call newgroups
  195.       say
  196.       end
  197.    if settings.nextgroupatconnect then
  198.       do 
  199.       trc=nextgroup()
  200.       if trc\=0 & settings.displayatgroup & settings.unread then settings.current=article(settings.current)
  201.       end
  202.    end
  203.  
  204. rc = Interact(sock)
  205.  
  206. /*------------------------------------------------------------------
  207.  * quittin' time!
  208.  *------------------------------------------------------------------*/
  209.  
  210. Shutdown:
  211.  
  212. trc = SendMessage(sock,"quit")
  213.  
  214. shutdownerr:
  215.  
  216. trc = SockSoclose(sock)
  217.  
  218. if settings.savenewsrcatexit\=0 then call fileout 'newsrc.',settings.newsrcname, 1, 1
  219.  
  220. if settings.rows\=settings.originalrows | settings.cols\=settings.originalcols then 
  221.    "mode "settings.originalcols","settings.originalrows" 1>NUL 2>NUL"
  222.  
  223. exit
  224.  
  225. /*------------------------------------------------------------------
  226.  * get command and execute in a loop
  227.  *------------------------------------------------------------------*/
  228. Interact:        procedure expose !. settings. newsrc. first last remain articleavailable sock
  229.    sock = arg(1)
  230.  
  231.    /*------------------------------------------------------------------
  232.     * commands is the commands currently implemented in rnr.cmd 
  233.     *------------------------------------------------------------------*/
  234.    rawcommands = "STAT BODY HEAD NEWNEWS RAW"
  235.  
  236.    group=settings.groupname
  237.  
  238.    do forever
  239.       commandline=prompt()
  240.  
  241.       parse var commandline command args 
  242.  
  243.       if commandline="" then iterate
  244.  
  245.       if left(commandline,length(settings.shellescapechar))=settings.shellescapechar then
  246.          do
  247.          left(commandline,length(settings.shellescapechar)+1)
  248.          iterate
  249.          end
  250.  
  251.       if command=="DEBUG" then
  252.          do
  253.          say "Entering debug mode... (hopefully)"
  254.          trace '?A'
  255.          iterate
  256.          end
  257.  
  258.       if abbrev("QUIT",translate(command)) then
  259.          do
  260.          settings.savenewsrcatexit=1
  261.          leave
  262.          end
  263.  
  264.       if ("EXIT"==translate(command)) then
  265.          do
  266.          call charout ,"Are you sure (newsrc will not be updated!)? "
  267.          if translate(SysGetKey("Echo"))="Y" then 
  268.             do
  269.             settings.savenewsrcatexit=0
  270.             leave
  271.             end
  272.          say
  273.          iterate
  274.          end
  275.  
  276.       if ("?" == command) | abbrev("HELP",translate(command)) then
  277.          do
  278.          rc = Help(args)
  279.          iterate
  280.          end
  281.  
  282.       if ("SET" == translate(command)) then
  283.          do
  284.          call set args
  285.          iterate
  286.          end
  287.  
  288.       if abbrev("QUERY",translate(command)) then
  289.          do
  290.          parse args args .
  291.          call set args
  292.          iterate
  293.          end
  294.  
  295.       if ("SHOW" == translate(command)) then
  296.          do
  297.          call display 'newsrc.',1
  298.          iterate
  299.          end
  300.  
  301.       if abbrev("POST",translate(command)) then
  302.          do
  303.          call post group,"post"
  304.          iterate
  305.          end
  306.  
  307.       if abbrev("FOLLOWUP",translate(command)) then
  308.          do
  309.          if \articleavailable then
  310.             do
  311.             say "No article available to follow-up!"
  312.             iterate
  313.             end
  314.          call post group,'followup'
  315.          iterate
  316.          end
  317.  
  318.       if abbrev("REPLY",translate(command)) then
  319.          do
  320.          if \articleavailable then
  321.             do
  322.             say "No article available to reply to!"
  323.             iterate
  324.             end
  325.          call post group,'reply'
  326.          iterate
  327.          end
  328.  
  329.       if abbrev("MAIL",translate(command)) then
  330.          do
  331.          if \articleavailable then
  332.             do
  333.             say "No article available to mail!"
  334.             iterate
  335.             end
  336.          call post group,'mail'
  337.          iterate
  338.          end
  339.  
  340.      if abbrev("MARK",translate(command)) then
  341.         do
  342.         call mark
  343.         if settings.nextgroupafterlast then call nextgroup
  344.         iterate
  345.         end
  346.  
  347.       if abbrev("DETAILS",translate(command),2) then
  348.          do
  349.          call details
  350.          iterate
  351.          end
  352.  
  353.       if abbrev("OS2",translate(command)) then
  354.          do
  355.          args
  356.          iterate
  357.          end
  358.  
  359.       if abbrev("TIME",translate(command)) then
  360.          do
  361.          say 'Current time is:' time() 'on' date()
  362.          say
  363.          iterate
  364.          end
  365.  
  366.       if abbrev("GROUP",translate(command)) then
  367.          do
  368.          if args=="" then
  369.             do
  370.             say 'Expecting a group name.'
  371.             iterate
  372.             end
  373.          articleavailable=0
  374.          call group args
  375.          if settings.displayatgroup & settings.unread then settings.current=article(settings.current)
  376.          iterate
  377.          end
  378.  
  379.       if abbrev("NEXT",translate(command)) then
  380.          do
  381.          if group="" then
  382.             do
  383.             say "You must select a group first."
  384.             iterate
  385.             end
  386.          call next
  387.          iterate
  388.          end                                                    
  389.  
  390.       if abbrev("NEXTGROUP",translate(command)) then
  391.          do
  392.          trc=nextgroup(args)
  393.          if trc\=0 & settings.displayatgroup & settings.unread then settings.current=article(settings.current)
  394.          iterate
  395.          end
  396.  
  397.       if abbrev("LAST",translate(command)) | abbrev("BACK",translate(command)) then
  398.          do
  399.          if group="" then
  400.             do
  401.             say "You must select a group first."
  402.             iterate
  403.             end
  404.          call last
  405.          iterate
  406.          end
  407.  
  408.       if abbrev("LISTGROUPS",translate(command)) then
  409.          do
  410.          call listgroups args
  411.          iterate
  412.          end
  413.  
  414.       if abbrev("ARTICLE",translate(command)) | abbrev("DISPLAY",translate(command)) then
  415.          do
  416.          if group="" then
  417.             do
  418.             say "You must select a group first."
  419.             iterate
  420.             end
  421.          settings.current=article(args)
  422.          iterate
  423.          end
  424.  
  425.       if abbrev("NEWGROUPS",translate(command)) then
  426.          do
  427.          call newgroups args
  428.          iterate
  429.          end
  430.  
  431.       if abbrev("AUTHORS",translate(command)) | abbrev("FROM",translate(command)) then
  432.          do
  433.          if group="" then
  434.             do
  435.             say "You must select a group first."
  436.             iterate
  437.             end
  438.          num=headers('from',args)
  439.          if num\=0 then settings.current=article(num)
  440.          iterate
  441.          end
  442.  
  443.       if abbrev("SAVE",translate(command)) then
  444.          do
  445.          if group="" then
  446.             do
  447.             say "You must select a group first."
  448.             iterate
  449.             end
  450.          if \articleavailable then
  451.             do
  452.             say "No article available to be saved.  Display an article first."
  453.             iterate
  454.             end
  455.          if args="" then
  456.             do
  457.             call charout , "Write article to file: "
  458.             parse pull args
  459.             if args="" then iterate
  460.             end
  461.          call fileout 'line.',args
  462.          iterate
  463.          end
  464.  
  465.       if abbrev("SAVENEWSRC",translate(command)) then
  466.          do
  467.          call fileout 'newsrc.',settings.newsrcname, 1, 1
  468.          iterate
  469.          end
  470.  
  471.       if abbrev("SUBJECTS",translate(command)) then
  472.          do
  473.          if group="" then
  474.             do
  475.             say "You must select a group first."
  476.             iterate
  477.             end
  478.          num=headers('subject',args)
  479.          if num\=0 then settings.current=article(num)
  480.          iterate
  481.          end
  482.  
  483.       if abbrev("SUBSCRIBE",translate(command)) then
  484.          do
  485.          if group="" then
  486.             do
  487.             say "You must select a group first."
  488.             iterate
  489.             end
  490.          settings.groupstat=':'
  491.          call updatenewsrc group
  492.          say "Marked group "settings.groupname" as subscribed."
  493.          iterate
  494.          end
  495.  
  496.       if abbrev("UNSUBSCRIBE",translate(command)) then
  497.          do
  498.          if group="" then
  499.             do
  500.             say "You must select a group first."
  501.             iterate
  502.             end
  503.          settings.groupstat='!'
  504.          call updatenewsrc group
  505.          say "Marked group "settings.groupname" as unsubscribed."
  506.          iterate
  507.          end
  508.  
  509.       if abbrev("SEARCH",translate(command)) then
  510.          do
  511.          call search(args)
  512.          iterate
  513.          end
  514.  
  515.       if abbrev("EDITNEWSRC",translate(command)) then
  516.          do
  517.          call editnewsrc
  518.          iterate
  519.          end
  520.  
  521.       if wordpos(translate(command),rawcommands)=0 then
  522.          do
  523.          say 'Unknown command: 'command
  524.          iterate
  525.          end
  526.  
  527.       if "RAW"==translate(command) then commandline=args
  528.  
  529.       articleavailable=0
  530.       trc = SendCommand(sock,commandline)
  531.  
  532.       call display 'line.',1
  533.  
  534.    end
  535.  
  536.    return ""
  537.  
  538. /*------------------------------------------------------------------
  539.  * display
  540.  *------------------------------------------------------------------*/
  541. Display: 
  542.    parse arg list, n, string, skip, keylist, firstword, lastword
  543.    if list="" then return ""
  544.    if \datatype(n,"W") then n=2
  545.    if \datatype(skip,"W") then skip=0
  546.    if \datatype(firstword,"W") then firstword=0
  547.    if lastword\="" & \datatype(lastword,"W") then lastword=""
  548.    _r=skip+1
  549.    _cls=0;
  550.    _rot13=0;
  551.    say
  552.  
  553.    interpret "do _i = n to "list"0+1;",
  554.       "if pos(d2c(12),"list"_i,1)\=0 then do; _cls=1;_r=_r+((settings.rows-settings.overlap)-_r//(settings.rows-settings.overlap));end;",
  555.       "if _r//(settings.rows-settings.overlap)=0 | _i>"list"0 then",
  556.          "do;",
  557.          'call charout ,"---MORE'string'---";',
  558.          "if _i>"list"0 then call charout ,'<END>';",
  559.          'key=SysGetKey('NOECHO');',
  560.          'call charout ,d2c(13)||copies(" ",settings.cols-1)||d2c(13);',
  561.          'if pos(translate(key),translate(keylist))\=0 then return translate(key);',
  562.          'if "="==key then do;call charout , "Move to line: ";parse pull _line;if _line>0 & _line<="list"0 then _i=_line;else _i=_i-1;call SysCls;_r=1;end;',
  563.          'if "Q"==translate(key) then return "";',
  564.          'if d2c(13)==key then _r=_r-1;',
  565.          'if "U"==translate(key) & _i>2*(settings.rows-settings.overlap) then _i=_i-2*(settings.rows-settings.overlap);',
  566.          'if "T"==translate(key) then do;call SysCls;_i=n;_r=1;end;',
  567.          'if "^"==translate(key) then do;call SysCls;_i=n;_r=1;end;',
  568.          'if "B"==translate(key) then do;call SysCls;_i='list'0-(settings.rows-settings.overlap);_r=1;end;',
  569.          'if "$"==translate(key) then do;call SysCls;_i='list'0-(settings.rows-settings.overlap);_r=1;end;',
  570.          'if "?"==translate(key) then do;call help("more");_i=_i-(settings.rows-settings.overlap);end;',
  571.          'if "D"==translate(key) then do;_rot13=\_rot13;call SysCls;_i=n;_r=1;end;',
  572.          'if _cls then do; Call SysCls; _cls=0; end;',
  573.          'if length('list'_i)>settings.cols',
  574.             'then _r=_r+(length('list'_i)%settings.cols);',
  575.          'if _i<1 then _i=1;',
  576.          'end;',
  577.       "_line="list"_i;",                                                                            
  578.       "if _rot13 then _line=translate(_line,'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz','NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm');",
  579.       "if _i<="list"0 then ",
  580.          "if firstword=0 then say _line;",
  581.          "else say subword(_line,firstword,lastword);",
  582.       "_r=_r+1;",
  583.    "end"
  584.    return ""
  585.  
  586. /* An Alternate display function that limits displayed lines with a 'needle' */
  587.  
  588. DisplayN:
  589.    parse arg list, n, string, needle, keylist
  590.    if list="" then return
  591.    if n="" then n=2
  592.    _r=1
  593.    say
  594.  
  595.    interpret "do _i = n to "list"0+1;",
  596.       "if _r//(settings.rows-settings.overlap)=0 | _i>"list"0 then",
  597.          "do;",
  598.          'call charout ,"---MORE'string'---";',
  599.          "if _i>"list"0 then call charout ,'<END>';",
  600.          'key=SysGetKey('NOECHO');',
  601.          'call charout ,d2c(13)||copies(" ",settings.cols-1)||d2c(13);',
  602.          'if pos(translate(key),translate(keylist))\=0 then return translate(key);',
  603.          'if "Q"==translate(key) then return;',
  604.          'if d2c(13)==key then _r=_r-1;',
  605.          'if "U"==translate(key) & _i>2*(settings.rows-settings.overlap) then _i=_i-2*(settings.rows-settings.overlap);',
  606.          'if "T"==translate(key) then do;call SysCls;_i=n;_r=1;end;',
  607.          'if "^"==translate(key) then do;call SysCls;_i=n;_r=1;end;',
  608.          'if "B"==translate(key) then do;call SysCls;_i='list'0-(settings.rows-settings.overlap);_r=1;end;',
  609.          'if "$"==translate(key) then do;call SysCls;_i='list'0-(settings.rows-settings.overlap);_r=1;end;',
  610.          'if "?"==translate(key) then do;call help("more");_i=_i-(settings.rows-settings.overlap);end;',
  611.          'if length('list'_i)>settings.cols',
  612.             'then _r=_r+(length('list'_i)%settings.cols);',
  613.          'if _i<1 then _i=1;',
  614.          'end;',
  615.       "if _i=1 | pos(translate(needle),translate("list"_i))\=0 then",
  616.          "do;",
  617.          "if _i<="list"0 then say "list"_i;",
  618.          "_r=_r+1;",
  619.          "end;",
  620.    "end"
  621.    return ""
  622.  
  623. /*------------------------------------------------------------------
  624.  * help
  625.  *------------------------------------------------------------------*/
  626. Help: procedure expose settings. newsrc. sock
  627.    arg topic
  628.    if topic="" then topic="general"
  629.    if "TOPICS"==translate(topic) then
  630.       do
  631.       call SysFileTree addslash(settings.rexxnewsdir)||'*.rxn','topics','FO'
  632.       do i=topics.0 to 1 by -1
  633.          n=i+2
  634.          topics.n=linein(topics.i)
  635.          call stream topics.i,'c','CLOSE'
  636.       end
  637.       n=topics.0+3
  638.       topics.n='topics'
  639.       topics.0=n
  640.       topics.1=settings.version
  641.       topics.2="Help is available for the following topics:"
  642.       call SysCls
  643.       call Display 'topics.',1,' (RexxNews Help Topics)'
  644.       return 1
  645.       end
  646.    if filein('help.',addslash(settings.rexxnewsdir)||topic||'.rxn')=0 then
  647.       if filein('help.',addslash(settings.rexxnewsdir)||strip(left(topic,8))||'.rxn')=0 then
  648.          do
  649.          say "No help available for '"topic"'."
  650.          say "Type HELP for general information or HELP INTRO for an introduction to RexxNews"
  651.          return 0
  652.          end
  653.    call SysCls
  654.    call Display 'help.',1,' ('||topic||' help)'
  655.    return 1
  656.  
  657. /*------------------------------------------------------------------
  658.  * Issue a command to the server that expects a response
  659.  *------------------------------------------------------------------*/
  660.  
  661. SendCommand: procedure expose !. line. settings. newsrc. sock
  662.    sock = arg(1)
  663.    data = arg(2) || d2c(13) || d2c(10)
  664.  
  665.    trc = SendMessage(sock,data)
  666.    if trc\=-1 then 
  667.       do
  668.       trc = GetResponse(sock)
  669.       parse var line.1 code msg
  670.       end
  671.    if trc=-1 | (code=503 & pos('TIME',translate(msg))\=0 and pos('OUT',translate(MSG))\=0) then
  672.       do
  673.       rc = SockSoClose(sock)
  674.       call ConnectServer settings.server
  675.       trc=SendMessage(sock,'group '||settings.groupname)
  676.       trc=GetResponse(sock)
  677.       trc=SendMessage(sock,'stat '||settings.current)
  678.       trc=GetResponse(sock)
  679.       trc=SendMessage(sock,data)
  680.       trc=GetResponse(sock)
  681.       end
  682.  
  683.    return trc
  684.  
  685. /*------------------------------------------------------------------
  686.  * get a response from the server
  687.  *------------------------------------------------------------------*/
  688. GetResponse:     procedure expose !. line. settings. newsrc. sock
  689.    sock = arg(1)
  690.  
  691.    moreids = "100 215 220 221 222 230 231"
  692.  
  693.    progress="\|/-"
  694.  
  695.    line.0 = 1
  696.    line.1 = GetResponseLine(sock)
  697.  
  698.    parse var line.1 rid msg
  699.  
  700.    if rid=400 then 
  701.       do
  702.       say 'The server has closed the connection with the following message:'
  703.       say msg
  704.       signal shutdownerr
  705.       end
  706.  
  707.    if (wordpos(rid,moreids) = 0) then
  708.       return ""
  709.  
  710.    o=0
  711.  
  712.    do forever
  713.       call charout , substr(progress,1+o//length(progress),1)||d2c(13)
  714.       o = line.0 + 1
  715.  
  716.       line.o = GetResponseLine(sock)
  717.  
  718.       if (line.o = ".") then
  719.          do
  720.          call charout , " "||d2c(13)
  721.          return ""
  722.          end
  723.  
  724.       line.0 = o
  725.    end
  726.    call charout " "||d2c(13)
  727.  
  728.    return ""
  729.  
  730. /*------------------------------------------------------------------
  731.  * get a line from the server
  732.  *------------------------------------------------------------------*/
  733. GetResponseLine: procedure expose !. settings. newsrc. sock
  734.    sock = arg(1)
  735.  
  736.    crlf = d2c(13) || d2c(10)
  737.  
  738.    if (symbol('!.buff') = "LIT") then
  739.       !.buff = ""
  740.  
  741.    do while (pos(crlf,!.buff) = 0)
  742.       rc = SockRecv(sock,"data",8000)
  743.       !.buff = !.buff || data
  744.    end
  745.  
  746.    p = pos(crlf,!.buff)
  747.  
  748.    line = substr(!.buff,1,p-1)
  749.    !.buff = substr(!.buff,p+2)
  750.  
  751.    return line
  752.  
  753. /*------------------------------------------------------------------
  754.  * send a string to the server
  755.  *------------------------------------------------------------------*/
  756. SendMessage:     procedure expose !. settings. newsrc. sock
  757.    sock = arg(1)
  758.    data = arg(2) || d2c(13) || d2c(10)
  759.  
  760.    len = length(data)
  761.    do while (len > 0)
  762.       i = SockSend(sock,data);
  763.  
  764.       if (errno \= 0) then
  765.          return Error(-1,rc,"Error sending data to server.")
  766.  
  767.       if (i <= 0) then
  768.          return Error(sock,100,"Server closed the connection.")
  769.  
  770.       data = substr(data,len+1)
  771.       len  = length(data)
  772.    end
  773.  
  774.    return 0
  775.  
  776. /*------------------------------------------------------------------
  777.  * exit with a message and return code
  778.  *------------------------------------------------------------------*/
  779. Error: procedure expose settings. newsrc. sock
  780.    sock = arg(1)
  781.    retc = arg(2)
  782.    msg  = arg(3)
  783.  
  784.    if (sock \= -1) then
  785.       rc = SockSoClose(sock)
  786.  
  787.    say msg
  788.  
  789.    return retc
  790.  
  791. opening:
  792.    /*------------------------------------------------------------------
  793.     * initialize system function package
  794.     *------------------------------------------------------------------*/
  795.    if RxFuncQuery("SysLoadFuncs") then
  796.       do
  797.       rc = RxFuncAdd("SysLoadFuncs","RexxUtil","SysLoadFuncs")
  798.       rc = SysLoadFuncs()
  799.       end
  800.    
  801.    /*------------------------------------------------------------------
  802.     * initialize socket function package
  803.     *------------------------------------------------------------------*/
  804.    if RxFuncQuery("SockLoadFuncs") then
  805.       do
  806.       rc = RxFuncAdd("SockLoadFuncs","RxSock","SockLoadFuncs")
  807.       rc = SockLoadFuncs()
  808.       end
  809.  
  810.    call SysCls
  811.    say settings.version 
  812.    say 
  813.    say "A NNTP NewsReader Client in REXX and rxSock for OS/2 2.x"
  814.    say
  815.    return
  816.  
  817. filein:
  818.    arg stem, filename
  819.    if arg()<2 then return 0
  820.    if stem="" | filename="" then 
  821.       do
  822.       say "Error reading file "filename" into stem "stem
  823.       return 0
  824.       end
  825.    _i=0
  826.    if stream(filename,'c','OPEN READ')=='READY:' then
  827.       do
  828.       interpret 'do while stream(filename,"S")="READY";',
  829.          "_i=_i+1;",
  830.          stem"_i=linein(filename);",
  831.       'end;',
  832.       stem"0=_i"
  833.       end
  834.    call stream filename,'c','CLOSE'
  835.    interpret '_n='stem'0;if 'stem'_n="" then do;'stem'0=_n-1;_i=_n;end'
  836.    return _i
  837.  
  838. loadsettings: procedure expose settings. newsrc. sock
  839.    parse arg settingfile
  840.    if settingfile="" then return 0;
  841.    else if filein('set.',settingfile)=0 then return 0
  842.    do i=1 to set.0
  843.       if set.i\="" & left(set.i,1)\=';' then call set(set.i)
  844.    end
  845.    if settings.rows\=settings.originalrows | settings.cols\=settings.originalcols then 
  846.       "mode "settings.cols","settings.rows" 1>NUL 2>NUL"
  847.    parse value SysTextScreenSize() with settings.rows settings.cols
  848.    return 1
  849.  
  850. loadnewsrc:
  851.    say 'Processing newsrc file...'
  852.    say settings.newsrcname
  853.    drop newsrc.
  854.    if stream(settings.newsrcname,'c','query exists')\="" then
  855.       do
  856.       datetime=stream(settings.newsrcname ,'c','query datetime')
  857.       parse var datetime mo'-'dd'-'yy hh':'mm':'ss
  858.       settings.newsrcdate=strip(yy||mo||dd)
  859.       settings.newsrctime=strip(hh||mm||ss)
  860.       call filein 'newsrc.',settings.newsrcname
  861.       return 1
  862.       end
  863.    else
  864.       do
  865.       settings.newsrcdate=""
  866.       settings.newsrctime=""
  867.       newsrc.0=0
  868.       return 0
  869.       end
  870.  
  871. updatenewsrc: procedure expose settings. newsrc. sock
  872.    parse arg group, article
  873.    if translate(group)\=translate(settings.groupname) then
  874.       do
  875.       say "Error updating newsrc: wrong group."
  876.       return
  877.       end
  878.    i=settings.groupnewsrcline
  879.    if article>settings.grouphighest then 
  880.       newsrc.i=group||settings.groupstat||' 1-'||article
  881.    return ""
  882.  
  883. prompt: procedure expose sock group first last settings. !. newsrc. sock
  884.    say
  885.    if group\="" then 
  886.       do
  887.       trc = SendCommand(sock,'stat')
  888.       parse var line.1 code settings.current msgid .
  889.       call charout , 'Group: 'group '('
  890.       if settings.groupstat\=':' then call charout , 'un'
  891.       say "subscribed)"
  892.       Say  "Articles available: "first"-"last" --- Current article "settings.current" ["last-settings.current" remaining]"
  893.       end
  894.    if settings.newuser then
  895.       do
  896.       say "Enter HELP INTRO to review the introduction to RexxNews."
  897.       say "Use the LISTGROUPS command to find groups. (See HELP LISTGROUPS)"
  898.       if newsrc.0>0 then
  899.          say "Use the NEXTGROUP command to see the next group with unread articles."
  900.       if group="" then
  901.          say "Enter Group <groupname> to move to a group."
  902.       else say "Enter Display to see the current article or Next for the next article."
  903.       end
  904.    say "Enter RexxNews command (or help or quit)"
  905.    parse pull commandline
  906.    return commandline
  907.  
  908. fileout:
  909.    parse arg stem, filename, compress, overwrite, quiet, _needle
  910.    if overwrite\="1" then overwrite=0
  911.    if compress\="1" then compress=0
  912.    if \datatype(quiet,'B') then quiet=0
  913.    if stem="" then return 0;
  914.    interpret "if "stem"0 = 0 then return 0"
  915.    append=0
  916.    exists=0
  917.    if stream(filename,'c','query exists')\="" then exists=1
  918.    if \overwrite & exists then
  919.       do
  920.       if promptkey("File "filename" already exists.  Append? ","YN")="Y" then 
  921.          append=1
  922.       else if promptkey("Replace it? ","YN")="N" then return 0
  923.       say
  924.       end
  925.    call stream filename,'c','open write'
  926.    if \append & exists then call lineout filename,,1
  927.    if stream(filename,'s')\="READY" then
  928.       do
  929.       say "Error writing to file "filename": "_state
  930.       call stream filename,'c','CLOSE'
  931.       return 0
  932.       end
  933.    _n=0
  934.    interpret 'do _i=1 to 'stem'0;',
  935.       'if (\compress) | strip('stem'_i)\="" then',
  936.          'do;',
  937.          'if _needle="" | pos(translate(needle),translate('stem'_i))\=0 then',
  938.             'do;',
  939.             '_n=_n+1;',
  940.             'call lineout filename, 'stem'_i;',
  941.             'end;',
  942.          'end;',
  943.    'end'
  944.    call stream filename,'c','CLOSE'
  945.    if \quiet then say _n' line(s) written to 'filename
  946.    return _n
  947.  
  948. checknewsrc: procedure expose settings. newsrc. sock
  949.    parse arg group subscribe
  950.    if group="" then return 0
  951.    subscribe=translate(left(subscribe,1))
  952.    do i= 1 to newsrc.0
  953.       parse var newsrc.i name pointer
  954.       if name="" then iterate
  955.       stat=right(name,1)
  956.       if pos(stat,':!')=0 then
  957.          do
  958.          stat=" "
  959.          end
  960.       else
  961.          do
  962.          name=left(name,length(name)-1)
  963.          end
  964.       if translate(name)=translate(group) then
  965.          do
  966.          settings.groupstat=stat
  967.          settings.groupname=name
  968.          settings.groupnewsrcline=i
  969.          n=translate(pointer,"  ","-,")
  970.          n=word(n,words(n))
  971.          if \datatype(n,'w') then n=0
  972.          settings.grouphighest=n
  973.          if pos(settings.groupstat,':!')=0 then
  974.             do
  975.             if pos(subscribe,"YN")\=0 then
  976.                key=promptkey(group' is currently neither subscribed nor unsubscribed.  Subscribe (Y/N)? ','YN','Y')
  977.             else key=subscribe
  978.             if key='Y' then settings.groupstat=':'
  979.             else settings.groupstat='!'
  980.             newsrc.i=group||settings.groupstat||' '||pointer
  981.             end
  982.          return n
  983.          end
  984.    end
  985.    settings.groupnewsrcline=i
  986.    settings.grouphighest=0
  987.    if pos(subscribe,"YN")=0 then
  988.      do
  989.      call charout , "Subscribe to "group"? "
  990.      do until pos(key,"YN")\=0
  991.         key=translate(SysGetKey('NoEcho'))
  992.      end
  993.      say key
  994.      end
  995.    else key=subscribe
  996.    if key='Y' then settings.groupstat=':'
  997.    else settings.groupstat='!'
  998.    settings.groupname=group
  999.    newsrc.0=i
  1000.    newsrc.i=group||settings.groupstat||' 0'
  1001.    return i
  1002.  
  1003. newgroups: procedure expose sock settings. !. newsrc.
  1004.    arg _datetime
  1005.    parse var _datetime _date _time
  1006.  
  1007.    if _time="" then _time="000000"
  1008.    if _date="" then
  1009.       do
  1010.       _date=settings.newsrcdate
  1011.       _time=settings.newsrctime
  1012.       end
  1013.    trc = SendCommand(sock,'newgroups'||' '||_date||' '||_time)
  1014.    parse var line.1 code number id  .
  1015.    if line.0=1 then
  1016.       do
  1017.       say 'No new groups since '_date _time'.'
  1018.       return
  1019.       end
  1020.    line.1='New groups since '_date _time'.'
  1021.    type='L'
  1022.    signal _listgroups
  1023.    return
  1024.    
  1025. xhdr: procedure  expose sock settings. !. newsrc.
  1026.    parse arg tag, article
  1027.    if \datatype(article,'W') then article=""
  1028.    if tag="" then tag="subject"
  1029.    if settings.usexhdr then 
  1030.       do
  1031.       trc = SendCommand(sock,'xhdr '||tag||' '||article)
  1032.       value=""
  1033.       if line.0>1 then parse var line.2 article value
  1034.       if value='(none)' then value=""
  1035.       return value
  1036.       end
  1037.    trc = SendCommand(sock,'head '||article)
  1038.    parse var line.1 code .
  1039.    if code\=221 then return ""
  1040.    value=""
  1041.    do i=2 to line.0
  1042.       parse var line.i ln":"value
  1043.       if translate(tag)=translate(ln) then leave
  1044.       value=""
  1045.    end
  1046.    trc = SendCommand(sock,'stat '||settings.current)
  1047.    return value
  1048.  
  1049. article:
  1050.    arg num
  1051. _article:
  1052.    if num="$" then num=last
  1053.    else if num="^" then num=first
  1054.    else if \datatype(num,'W') then num=settings.current
  1055.    if \articleavailable | num\=settings.current then
  1056.       do
  1057.       if settings.displayheaders=1
  1058.          then command='ARTICLE'
  1059.          else command='BODY'
  1060.       trc = SendCommand(sock,command||' '||num)
  1061.       parse var line.1 code number id .
  1062.       if code\=220 then
  1063.          do
  1064.          say "Error retrieving article:  Code "code
  1065.          articleavailable=0
  1066.          return 0
  1067.          end
  1068.       articleavailable=1
  1069.       call updatenewsrc settings.groupname, number
  1070.       end
  1071.    else number=settings.current
  1072.    call SysCLS
  1073.    settings.current=number
  1074.    key=Display('line.', ,' (line "_i-1" of "'line.0'" article "settings.current" of "last")', ,'NLWRFM?HGPAS*')
  1075.    if key='N' then signal next
  1076.    else if key='L' then signal last
  1077.    else if key='*' then
  1078.       do
  1079.       call mark
  1080.       num=settings.current
  1081.       if settings.nextgroupafterlast then
  1082.          do
  1083.          num=nextgroup(subscribedonly)
  1084.          if num=0 then return settings.current
  1085.          articleavailable=0
  1086.          signal _article
  1087.          end
  1088.       else return settings.current
  1089.       end
  1090.    else if key='S' then
  1091.       do
  1092.       _num=headers('subject')
  1093.       if _num=0 then signal _article
  1094.       articleavailable=0
  1095.       trc=SendCommand(sock,'stat '||_num)
  1096.       parse var line.1 code _num .
  1097.       if code\=223 then 
  1098.          do
  1099.          say "No such article"
  1100.          signal _article
  1101.          end
  1102.       num=_num
  1103.       settings.current=num
  1104.       signal _article
  1105.       end
  1106.    else if key='W' then
  1107.       do
  1108.       call charout , "Write article to file: "
  1109.       parse pull fn
  1110.       if fn\="" then call fileout 'line.',fn
  1111.       signal _article
  1112.       end
  1113.    else if key='A' then
  1114.       do
  1115.       call charout , "Skip to article (available articles:"first"-"last"): "
  1116.       parse pull _num
  1117.       if \datatype(_num,'W') | pos(_num,'^$')\=0 then signal _article
  1118.       articleavailable=0
  1119.       trc=SendCommand(sock,'stat '||_num)
  1120.       parse var line.1 code _num .
  1121.       if code\=223 then 
  1122.          do
  1123.          say "No such article"
  1124.          signal _article
  1125.          end
  1126.       num=_num
  1127.       settings.current=num
  1128.       signal _article
  1129.       end
  1130.    else if key='R'|key='F'|key='M'|key='P' then
  1131.       do 
  1132.       call post group,key
  1133.       signal _article
  1134.       end
  1135.    else if key='?'|key='H' then
  1136.       do
  1137.       call help 'reading'
  1138.       signal _article
  1139.       end
  1140.    else if key='G' then
  1141.       do
  1142.       call charout , 'Group: '
  1143.       parse pull gn
  1144.       articleavailable=0
  1145.       if gn="" then signal _article
  1146.       number=group(gn)
  1147.       num=settings.current
  1148.       signal _article
  1149.       end
  1150.    return settings.current
  1151.  
  1152. headers: procedure expose settings. sock first last !. newsrc.
  1153.    parse arg tag, range, needle
  1154.    if range="" then range=settings.current||'-'||last
  1155.    if range="*" then range=first||'-'||last
  1156.    if tag="" then tag='subject'
  1157.    if (translate(tag)\="GROUPS") then 
  1158.       do
  1159.       say "Retreiving the "tag" field for article(s) "range"..."
  1160.       msg=tag||' fields for article(s) 'range
  1161.       if needle\="" then msg=msg||' containing 'needle':'
  1162.       else msg=msg||':'
  1163.       if settings.usexhdr then
  1164.          do
  1165.          trc = SendCommand(sock,'xhdr '||tag||' '||range)
  1166.          parse var line.1 code number id .
  1167.          end
  1168.       else
  1169.          do
  1170.          parse var range begin'-'end
  1171.          n=2
  1172.          do i=begin to end
  1173.             line.n=i||' '||xhdr(tag,i)
  1174.          end
  1175.          code=221
  1176.          end
  1177.       end
  1178.    else 
  1179.       do
  1180.       say "Retrieving a list of all groups... This may take a few moments."
  1181.       msg='List of groups containing '||needle||' in their names:'
  1182.       trc = SendCommand(sock,'list')
  1183.       parse var line.1 code number id .
  1184.       end
  1185.    if code\=221 & code\=215 then
  1186.       do
  1187.       say "Error retrieving list:  Code "code
  1188.       return 0
  1189.       end
  1190.    if line.0=1 then
  1191.       do
  1192.       call charout ,"No articles in "range
  1193.       if needle="" then say "."
  1194.       else say " matching "needle"."
  1195.       return 0
  1196.       end
  1197.    line.1=msg
  1198.    call SysCLS
  1199.    if needle="" then call Display 'line.',1 
  1200.    else call DisplayN 'line.',1,,needle
  1201.    if promptkey("Move to one of these articles now? ","YN","Y")="N" then return 0
  1202.    call charout , "Article number: "
  1203.    parse pull number
  1204.    if \datatype(number,'W') then number=0
  1205.    return number
  1206.  
  1207. set: procedure expose settings. newsrc. sock
  1208.    parse arg command
  1209.    if command\="" then
  1210.       do
  1211.       parse var command variable value 
  1212.       i=wordpos(translate(variable),translate(settings.varnames))
  1213.       if i//2=0 then
  1214.          do
  1215.          say "Unknown variable: "variable
  1216.          return 0
  1217.          end
  1218.       else
  1219.          if value="" then
  1220.             do
  1221.             say left("Variable",25)||left("Value",35)||" Type"
  1222.             say left("========",25)||left("=====",35)||" ===="
  1223.             interpret "say left(word(settings.varnames,i),25)||left(settings."word(settings.varnames,i)",35)||' '||word(settings.varnames,i+1)"
  1224.             return 1
  1225.             end
  1226.          else
  1227.          do
  1228.          if word(settings.varnames,i+1)="S" | datatype(value,word(settings.varnames,i+1)) then
  1229.             interpret "settings."variable"=value"
  1230.          else say "Improper argument type for "variable"."
  1231.          return 1
  1232.          end
  1233.       end
  1234.    set.0=words(settings.varnames)/2+4
  1235.    set.1=settings.version "Settings"
  1236.    set.2=""
  1237.    set.3=left("Variable",25)||left("Value",35)||" Type"
  1238.    set.4=left("========",25)||left("=====",35)||" ===="
  1239.    n=4
  1240.    do i=1 to words(settings.varnames) by 2
  1241.       n=n+1
  1242.       interpret "set.n=left(word(settings.varnames,i),25)||left(settings."word(settings.varnames,i)",35)||' '||word(settings.varnames,i+1)"
  1243.    end
  1244.    call Display 'set.',1,' (RexxNews SET values)'
  1245.    return
  1246.  
  1247. details:
  1248.    say
  1249.    say "Current group: "group
  1250.    say "Available articles: "remain
  1251.    say "First article: "first
  1252.    say "Last article: "last
  1253.    say "Current article: "settings.current
  1254.    if group\="" then 
  1255.       do
  1256.       say "Article can be saved:" articleavailable
  1257.       say "Subject: "xhdr(subject)
  1258.       say "From:    "xhdr(from)
  1259.       say "Lines:   "xhdr(lines)
  1260.       _i=settings.groupnewsrcline
  1261.       say "newsrc line #"_i
  1262.       say "newsrc line: "newsrc._i
  1263.       end
  1264.    return
  1265.  
  1266. search: procedure expose settings. sock first last !. newsrc.
  1267.    arg args
  1268.    if args="" then return 0
  1269.    !field=""
  1270.    parse var args field rest 1 "RANGE " range . 1 "FOR " needle
  1271.    if field='RANGE' | field='FOR' | (needle="" & rest="") then !field='SUBJECT'
  1272.    if (needle="" & rest="") then needle=field
  1273.    if range="" then range=settings.current||'-'||last
  1274.    else if range="*" then range=first||'-'||last
  1275.    if !field\="" then field=!field
  1276.    if settings.groupname="" & "GROUPS"\=translate(field) then
  1277.       do
  1278.       say "You must select a group first."
  1279.       return 0
  1280.       end
  1281.    if needle="" then
  1282.       do
  1283.       say "The syntax of the SEARCH command is:"
  1284.       say "  SEarch [<field>] [RANGE <range>] [FOR] <string>"
  1285.       say "  See HELP SEARCH for more information."
  1286.       return 0
  1287.       end
  1288.    call headers field, range, needle
  1289.    return 1
  1290.  
  1291. next: 
  1292.    if settings.current=last then
  1293.       do 
  1294.       say "No more articles."
  1295.       if settings.nextgroupafterlast then
  1296.          do
  1297.          num=nextgroup(subscribedonly)
  1298.          if num=0 then return settings.current
  1299.          articleavailable=0
  1300.          signal _article
  1301.          end
  1302.       return settings.current
  1303.       end
  1304.    trc = SendCommand(sock,'next')
  1305.    parse var line.1 . settings.current .
  1306.    num=settings.current
  1307.    articleavailable=0
  1308.    signal _article
  1309.  
  1310. last: 
  1311.    if settings.current=first then
  1312.       do 
  1313.       say "No previous article."
  1314.       return settings.current
  1315.       end
  1316.    trc = SendCommand(sock,'LAST')
  1317.    parse var line.1 . settings.current .
  1318.    num=settings.current
  1319.    articleavailable=0
  1320.    signal _article
  1321.  
  1322. nextgroup:
  1323.    parse arg type
  1324.    type=translate(left(type,1))
  1325.    if type='S'|type=''|type='1' then subscribedonly=1
  1326.    else subscribedonly=0
  1327.    __gn=-99
  1328. _nextgroup:
  1329.    articleavailable=0
  1330.    if settings.groupnewsrcline>=newsrc.0 then
  1331.       do
  1332.       _gn=1
  1333.       settings.groupnewsrcline=1
  1334.       end
  1335.    else _gn=settings.groupnewsrcline+1
  1336.    if __gn=-99 then __gn=_gn
  1337.    else if __gn=_gn then
  1338.       do
  1339.       say "There are no newsgroups left with unread articles."
  1340.       return 0
  1341.       end
  1342.    parse var newsrc._gn name .
  1343.    stat=right(name,1)
  1344.    if pos(stat,':!')=0 then
  1345.       do
  1346.       stat=" "
  1347.       end
  1348.    else
  1349.       do
  1350.       name=left(name,length(name)-1)
  1351.       end
  1352.    _grc=group(name)
  1353.    if _grc=0 then signal _nextgroup
  1354.    if _grc<0 then
  1355.       do
  1356.       settings.groupnewsrcline=settings.groupnewsrcline+1
  1357.       signal _nextgroup
  1358.       end
  1359.    if settings.groupstat='!' & subscribedonly then signal _nextgroup
  1360.    say "Advancing to group "settings.groupname
  1361.    return _grc
  1362.  
  1363. group: procedure expose sock !. settings. group newsrc. articleavailable first last
  1364.    parse arg args
  1365.  
  1366.    if args="" then return -2
  1367.    trc=SendCommand(sock,'group '||args)
  1368.    parse var line.1 code .
  1369.    if code=411 then 
  1370.       do
  1371.       say 'No active group named 'args'.'
  1372.       group=settings.groupname
  1373.       return -1
  1374.       end
  1375.    if code\=211 then
  1376.       do
  1377.       say 'An error occured while issuing the command: group '||args
  1378.       say line.1
  1379.       group=settings.groupname
  1380.       return -1
  1381.       end
  1382.    parse var line.1 code remain first last group .
  1383.    if remain>0 then
  1384.       do
  1385.       settings.unread=1
  1386.       settings.current=checknewsrc(group)
  1387.       if settings.current=0 then 
  1388.          do
  1389.          settings.groupname=group
  1390.          return -1
  1391.          end
  1392.       if first>settings.current then settings.current=first
  1393.       else 
  1394.          do
  1395.          if settings.current>=last then 
  1396.             do
  1397.             say "No unread articles in "group
  1398.             settings.unread=0
  1399.             settings.current=last
  1400.             trc = SendCommand(sock,'stat '||settings.current)
  1401.             return 0
  1402.             end
  1403.          else
  1404.             do
  1405.             trc = SendCommand(sock,'stat '||settings.current)
  1406.             parse var line.1 code .
  1407.             if code\=223 then settings.current=first
  1408.             else
  1409.                do
  1410.                trc = SendCommand(sock,'next')
  1411.                parse var line.1 code .
  1412.                if code=223 then parse var line.1 . settings.current .
  1413.                end
  1414.             end
  1415.          end
  1416.       if settings.newarticlesatgroup then call headers 'subject'
  1417.       end
  1418.    else
  1419.       do
  1420.       say "No articles in group "group
  1421.       group=""
  1422.       articleavailable=0
  1423.       settings.unread=0
  1424.       settings.current=0
  1425.       return -1
  1426.       end
  1427.    return settings.current
  1428.  
  1429. ConnectServer: procedure expose sock settings. !. newsrc.
  1430.    arg servername
  1431.  
  1432.    /*------------------------------------------------------------------
  1433.     * get address of server
  1434.     *------------------------------------------------------------------*/
  1435.    rc = SockGetHostByName(settings.server,"host.!")
  1436.    if (rc = 0) then
  1437.       do
  1438.       say "Unable to resolve server name" settings.server
  1439.       exit
  1440.       end
  1441.    
  1442.    server = host.!addr
  1443.    
  1444.    /*------------------------------------------------------------------
  1445.     * open socket
  1446.     *------------------------------------------------------------------*/
  1447.    sock = SockSocket("AF_INET","SOCK_STREAM",0)
  1448.    if (sock = -1) then
  1449.       do
  1450.       say "Error opening socket:" errno
  1451.       exit
  1452.       end
  1453.    
  1454.    /*------------------------------------------------------------------
  1455.     * connect socket
  1456.     *------------------------------------------------------------------*/
  1457.    server.!family = "AF_INET"
  1458.    server.!port   = 119
  1459.    server.!addr   = server
  1460.    
  1461.    trc = SockConnect(sock,"server.!")
  1462.    if (trc = -1) then
  1463.       do
  1464.       trc=Error(sock,rc,"Error connecting to newsserver :" errno)
  1465.       signal shutdownerr
  1466.       end
  1467.    
  1468.    trc = GetResponse(sock)
  1469.    do i = 1 to line.0
  1470.       say line.i
  1471.    end
  1472.    
  1473.    parse var line.1 code .
  1474.    if code=200 then
  1475.       settings.postingok=1
  1476.    else settings.postingok=0
  1477.    
  1478.    if code\=200 & code\=201 then
  1479.       do
  1480.       settings.savenewsrcatexit=0
  1481.       signal shutdown
  1482.       end
  1483.    
  1484.    trc = SendCommand(sock,"xhdr")
  1485.    
  1486.    parse var line.1 code .
  1487.    if code=501 then settings.usexhdr=1
  1488.    else settings.usexhdr=0
  1489.    
  1490.    trc = SendCommand(sock,"MODE READER")
  1491.    parse var line.1 code msg 
  1492.    if code\=500 then say "Server supports the INN extensions."
  1493.    return ""
  1494.  
  1495. post: procedure expose settings. sock !. newsrc.
  1496.    parse arg group, type, recipient
  1497.  
  1498.    type=left(translate(type),1)
  1499.  
  1500.    if type="F" then followup=1
  1501.    if type="R" | type="M" then mail=1
  1502.  
  1503.    if type="M" & recipient="" then
  1504.       do
  1505.       call charout , "Forward article to: "
  1506.       parse pull recipient
  1507.       if recipient="" then return 0
  1508.       end
  1509.  
  1510.    if followup\=1 then followup=0
  1511.    if mail\=1 then mail=0
  1512.  
  1513.    tempfile=SysTempFileName(addslash(settings.tempdir)||'POST????.RXN')
  1514.    messageid='<'||date('B')||'.'||time('S')||'@'||settings.hostname||'>'
  1515.  
  1516.    validgroups=0
  1517.    if mail then 
  1518.       do
  1519.       newsgroups=group
  1520.       validgroups=1
  1521.       end
  1522.    if followup then 
  1523.       do
  1524.       validgroups=1
  1525.       newsgroups=xhdr('Newsgroups',settings.current)
  1526.       followupto=xhdr('Followup-to',settings.current)
  1527.       if translate(followupto)='SENDER' | translate(followupto)='POSTER' | pos('@',followupto)\=0 then
  1528.          do
  1529.          mail=1
  1530.          if pos('@',followupto)\=0 then
  1531.             recipient=followupto
  1532.          else recipient=""
  1533.          end
  1534.       else if followupto\="" then newsgroups=followupto
  1535.       end
  1536.  
  1537.    if \settings.postingok & \mail then
  1538.       do
  1539.       say "This server does not allow posting."
  1540.       return 0
  1541.       end
  1542.  
  1543.    if \validgroups then
  1544.       do
  1545.       say
  1546.       say "Enter the name of the newsgroup(s) to post to (or '.' to abort)."
  1547.       if group\="" then say "Press enter by itself to choose the group "group":"
  1548.       end
  1549.    do while \validgroups
  1550.       say
  1551.       call charout , 'Newsgroups: '
  1552.       parse pull newsgroups
  1553.       if newsgroups=="." then return 0
  1554.       if newsgroups="" then newsgroups=group
  1555.       if newsgroups\="" & pos(" ",strip(newsgroups))=0 then validgroups=1
  1556.    end
  1557.    if followup | mail then
  1558.       do
  1559.       subject=xhdr('subject',settings.current)
  1560.       keywords=xhdr('keywords',settings.current)
  1561.       summary=xhdr('summary',settings.current)
  1562.       distribution=xhdr('distribution',settings.current)
  1563.       end
  1564.    else 
  1565.       do
  1566.       call charout , "Subject: "
  1567.       parse pull subject
  1568.       if subject="" then 
  1569.          do
  1570.          say "No subject... posting aborted."
  1571.          return 0
  1572.          end
  1573.       call charout , "Keywords: "
  1574.       parse pull keywords
  1575.       call charout , "Summary: "
  1576.       parse pull summary
  1577.       call charout , "Distribution: "
  1578.       parse pull distribution
  1579.       end
  1580.  
  1581.    if mail & recipient="" then
  1582.       do
  1583.       from=xhdr('from',settings.current)
  1584.       _replyto=xhdr('reply-to',settings.current)
  1585.       if _replyto="" then recipient=from
  1586.       else recipient=_replyto
  1587.       end
  1588.  
  1589.    n=0
  1590.    if newsgroups\="" then
  1591.       do
  1592.       n=n+1
  1593.       posting.1="Newsgroups: "||newsgroups
  1594.       end
  1595.    if mail then
  1596.       do
  1597.       n=n+1
  1598.       posting.n="To: "recipient
  1599.       end
  1600.    n=n+1
  1601.    if translate(settings.username)="UNKNOWN" then 
  1602.       call queryfield 'username', 'your user name'
  1603.    posting.n="From: "||settings.username||"@"||settings.hostname
  1604.    if translate(settings.fullname)="UNKNOWN" then
  1605.       call queryfield 'fullname', 'your full name'
  1606.    if settings.fullname\="" then posting.n=posting.n||' ('||settings.fullname||')'
  1607.    n=n+1
  1608.    posting.n="Subject: "
  1609.    if (mail|followup) & translate(left(subject,3))\="RE:" then posting.n=posting.n||"Re: "
  1610.    posting.n=posting.n||subject
  1611.    if translate(settings.organization)="UNKNOWN" then
  1612.       call queryfield 'organization', 'your organization'
  1613.    if settings.organization\="" then
  1614.       do
  1615.       n=n+1
  1616.       posting.n="Organization: "||settings.organization
  1617.       end
  1618.    n=n+1
  1619.    posting.n="Message-ID: "||messageid
  1620.    if summary\="" then
  1621.       do
  1622.       n=n+1
  1623.       posting.n="Summary: "||summary
  1624.       end
  1625.    if keywords\="" then
  1626.       do
  1627.       n=n+1
  1628.       posting.n="Keywords: "||keywords
  1629.       end
  1630.    if translate(settings.replyto)="UNKNOWN" then
  1631.       call queryfield 'replyto', 'the address for replies'
  1632.    if settings.replyto\="" then
  1633.       do
  1634.       n=n+1
  1635.       posting.n="Reply-to: "||settings.replyto
  1636.       end
  1637.    if translate(settings.disclaimer)="UNKNOWN" then
  1638.       call queryfield 'disclaimer', 'a disclaimer'
  1639.    if settings.disclaimer\="" then
  1640.       do
  1641.       n=n+1
  1642.       posting.n="Disclaimer: "||settings.disclaimer
  1643.       end
  1644.    n=n+1
  1645.    posting.n="X-Newsreader: "||settings.version
  1646.    if distribution\="" then
  1647.       do
  1648.       n=n+1
  1649.       posting.n="Distribution: "||distribution
  1650.       end
  1651.    n=n+1
  1652.    posting.n="Date: "||date('W')||', '||date()||' '||time()||' '||settings.timezone
  1653.    if followup | mail then
  1654.       do
  1655.       n=n+1
  1656.       posting.n="References: "||xhdr('references',settings.current)||" "||xhdr('Message-ID',settings.current)
  1657.       trc=SendCommand(sock,'body')
  1658.       if line.0>1 then
  1659.          do
  1660.          n=n+1
  1661.          posting.n=""
  1662.          n=n+1
  1663.          refline="In article "||xhdr('message-id',settings.current)||',  '||xhdr('From',settings.current)||" writes:"
  1664.          if length(refline)<79 then
  1665.             posting.n=refline
  1666.          else
  1667.             do
  1668.             i=words(refline)
  1669.             do while wordindex(refline,i)>79
  1670.                i=i-1
  1671.                if i=0 then leave
  1672.             end
  1673.             if i=0 then posting.n=refline
  1674.             else
  1675.                do
  1676.                posting.n=subword(refline,1,i-1)
  1677.                n=n+1
  1678.                posting.n='  '||subword(refline,i)
  1679.                end
  1680.             end
  1681.          do i=2 to line.0
  1682.             n=n+1
  1683.             posting.n=settings.quotechar||line.i
  1684.          end
  1685.          end
  1686.       end
  1687.    n=n+1
  1688.    posting.n=""
  1689.    n=n+1
  1690.    posting.n=""
  1691.    if settings.signature\="" then
  1692.       do
  1693.       sig.0=0
  1694.       n=n+1
  1695.       posting.n="--"
  1696.       if filein('sig.',settings.signature)=0 then
  1697.          if pos('/',settings.signature)\=0 then
  1698.             if filein('sig.',addslash(settings.etcdir)||settings.signature)=0 then
  1699.                if filein('sig.',addslash(settings.rexxnewsdir)||settings.signature)=0 then
  1700.                   say "Unable to load signature file "settings.signature
  1701.       do i=1 to sig.0
  1702.          n=n+1
  1703.          posting.n=sig.i
  1704.       end
  1705.       end
  1706.    posting.0=n
  1707.  
  1708.    call fileout 'posting.',tempfile,0,1,1
  1709.    edit=1
  1710.    if type='M' then
  1711.       if promptkey("Edit before mailing? ","YN","Y")="N" then edit=0
  1712.    if edit then
  1713.       do
  1714.       datestamp=stream(tempfile,'c','query datetime')
  1715.       settings.editor tempfile
  1716.       if datestamp==stream(tempfile,'c','query datetime') then
  1717.          do
  1718.          say "File was not changed... Aborting."
  1719.          "erase "tempfile
  1720.          return 0
  1721.          end
  1722.       end
  1723.    if mail then
  1724.       do
  1725.       do while verify(recipient,'()<>','M')\=0 
  1726.          if pos('(',recipient)>0 then recipient=word(recipient,1)
  1727.          if pos('<',recipient)>0 then parse var recipient "<"recipient">"
  1728.       end
  1729.       "start "settings.rexxnewsdir||"rxnewsml " tempfile settings.username||"@"||settings.hostname recipient
  1730.       return 1
  1731.       end
  1732.    if filein('posting.',tempfile)=0 then
  1733.       do
  1734.       say "Error retrieving file from editor.  Aborting...."
  1735.       "erase "tempfile
  1736.       return 0
  1737.       end
  1738.    trc=SendCommand(sock,"post")
  1739.    parse var line.1 code .
  1740.    say line.1
  1741.    if code\=340 then 
  1742.       do
  1743.       say "Server is not allowing posting for some reason....  Aborting..."
  1744.       "erase "tempfile
  1745.       return 0
  1746.       end
  1747.    say "Sending your post to the NNTP server..."
  1748.    do i=1 to posting.0
  1749.       trc=SendMessage(sock,posting.i)
  1750.    end
  1751.    trc=SendCommand(sock,'.')
  1752.    parse var line.1 code msg
  1753.    if code=240 then
  1754.       do
  1755.       say "Posting succeeded."
  1756.       "erase "tempfile
  1757.       return 1
  1758.       end
  1759.    say "Posting message failed with error code "code":"
  1760.    say msg
  1761.    return 0
  1762.  
  1763. addslash: procedure expose settings. newsrc. sock
  1764.    parse arg dirname
  1765.    if right(dirname,1)='\' then return dirname
  1766.    else return dirname||'\'
  1767.  
  1768. queryfield: procedure expose settings. newsrc. sock
  1769.    parse arg field description
  1770.    if description="" then description=field
  1771.    call charout , "Please enter "description":"
  1772.    interpret "parse pull settings."field";",
  1773.    "return settings."field
  1774.  
  1775. listgroups: procedure expose sock !. settings. newsrc.
  1776.    parse arg needle, type
  1777.    type=translate(left(type,1))
  1778.    if pos(type,'SUPL')=0 then type='L'
  1779.    if settings.newuser & needle="" then
  1780.       do
  1781.       say
  1782.       say "The LISTGROUP command without an operand will list all the groups"
  1783.       say "known at your NNTP server.  This may be over 1800 groups."
  1784.       say
  1785.       say "Rather than list all of these groups, you can enter a string to"
  1786.       say "search for in the groups' names.  For example, if you are interested"
  1787.       say "in newsgroups discussing OS/2, you might enter the string 'os2'."
  1788.       say
  1789.       say "Press enter if you really wish to list ALL of the newsgroups."
  1790.       say 
  1791.       call charout , "Enter the search string: "
  1792.       parse pull needle
  1793.       end
  1794.    say "Preparing a sorted list of all known newsgroups..."
  1795.    trc=SendCommand(sock,'list')
  1796.    line.1=""
  1797.    call sortstem 'line.',needle
  1798.    if needle\="" then line.1="All newsgroups containing "needle":"
  1799.    else line.1="All newsgroups:"
  1800.  
  1801. _listgroups:
  1802.    if type='L' then 
  1803.       do
  1804.       call Display 'line.',2,' ('||line.1||' "_i-1" of '||line.0||')', , ,1,1
  1805.       key=promptkey("Do wish to subscribe to any of these groups (Yes/No/All)? ","YNA","Y")
  1806.       if key="Y" then type="P"
  1807.       else if key="A" then 
  1808.          do
  1809.          if settings.newuser | line.0>settings.askifsubmore then
  1810.             do
  1811.             if promptkey("Are you sure you want to subscribe to all "||line.0" groups? ","YN","N")="N" then type='P'
  1812.             else type="S"
  1813.             end
  1814.          else type="S"
  1815.          end
  1816.       else return 1
  1817.       end
  1818.  
  1819.    if type='P' then
  1820.       do
  1821.       say
  1822.       say "You are about to be presented with each of the "line.0" groups and be given"
  1823.       say "a chance to subscribe or unsubscribe to each one."
  1824.       say
  1825.       say "Answering Yes to the question will add that group to your NEWSRC file as"
  1826.       say "   subscribed."
  1827.       say "Answering No will not add the group to your NEWSRC file."
  1828.       say "Answering U for Unsubscribe will add the group to your NEWSRC file as an"
  1829.       say "   unsubscribed group."
  1830.       say "Choosing Q will return you to the RexxNews prompt."
  1831.       say
  1832.       do _i=2 to line.0
  1833.       key=promptkey("Subscribe to" subword(line._i,1,1) "("_i-1 "of" line.0-1"): "||d2c(10)||d2c(13)||" (Yes/No/Unsubscribe/Quit)? ","YNUQ","N")
  1834.       say
  1835.       if key="Q" then leave
  1836.       else if key="N" then iterate
  1837.       else if key="Y" | key="U" then call checknewsrc subword(line._i,1,1), key
  1838.       else say "Internal error in promptkey. "sourceline()
  1839.       end
  1840.       end
  1841.  
  1842.    if type='S' then
  1843.       do
  1844.       if settings.newuser | line.0>settings.askifsubmore then
  1845.          if promptkey("Are you sure you want to subscribe to all "||line.0" groups? ","YN","N")="N" then return 0
  1846.       do _i=2 to line.0
  1847.       call checknewsrc subword(line._i,1,1), 'Y'
  1848.       end
  1849.       end
  1850.  
  1851.    if type='U' then
  1852.       do
  1853.       if settings.newuser | line.0>settings.askifsubmore then
  1854.          if promptkey("Are you sure you want to mark all "||line.0" groups as unsubscribed? ","YN","N")="N" then return 0
  1855.       do _i=2 to line.0
  1856.       call checknewsrc subword(line._i,1,1), 'N'
  1857.       end
  1858.       end
  1859.    group=""
  1860.    settings.groupname=""
  1861.    return 1
  1862.  
  1863. mark: procedure expose sock !. settings. last newsrc.
  1864.    articleavailable=0
  1865.    settings.current=last
  1866.    call updatenewsrc settings.groupname, settings.current
  1867.    trc = SendCommand(sock,'stat '||last)
  1868.    return 1
  1869.  
  1870. editnewsrc: procedure expose sock !. settings. newsrc. articleavailable group
  1871.    tempnewsrc=SysTempFileName(addslash(settings.tempdir)||'TEMP????.NRC')
  1872.    articleavailable=0
  1873.    group=""
  1874.    call fileout "newsrc.",tempnewsrc,1,1,1
  1875.    deleted=1
  1876.    call SysCls
  1877.    say "You are about to edit your NEWSRC file "settings.newsrcname
  1878.    say
  1879.    say "You can:"
  1880.    say 
  1881.    say "   Mark a group as Subscribed"
  1882.    say "   Mark a group as Unsubscribed"
  1883.    say "   Delete an entry"
  1884.    say "   Reset the highest article read back to 1"
  1885.    say "   Order groups by a 'key' value"
  1886.    say "     (Assign a 1 to 6 digit key that will be used to sort the newsgroups."
  1887.    say "     Groups without a key will be assigned 999999 to place them at"
  1888.    say "     the end of your newsrc file.  See HELP EDITNEWSRC for details.)"
  1889.    say
  1890.    if promptkey("Would you like to have the newsrc file sorted alphabetically before continuing? ","YN","N")="Y" then call sortstem 'newsrc.'
  1891.    sortbykey=0
  1892. _editnewsrc:
  1893.    say
  1894.  
  1895.    key.=999999
  1896.    tempnum=0
  1897.    templine=""
  1898.    do i=1 to newsrc.0
  1899.     if tempnum<>i then do;templine=newsrc.i;tempnum=i;end
  1900.     if temmpline="" and newsrc.0="" then iterate
  1901.     call SysCls
  1902.     say "There are "newsrc.0" entries in your NEWSRC file."
  1903.     say
  1904.     if newsrc.i="" then parse var templine gn range
  1905.     else parse var newsrc.i gn range
  1906.     subscribed=pos(right(gn,1),'!:')
  1907.     if subscribed\=0 then gn=left(gn,length(gn)-1)
  1908.     subscribed=word("Unknown Unsubscribed Subscribed",subscribed+1)
  1909.     say "Entry "i" is group" '"'gn'"'
  1910.     say "Status is "subscribed";  Articles read are "range
  1911.     if sortbykey then say "Current key: "key.i
  1912.     if newsrc.i="" then say "*DELETED*"
  1913.     key=promptkey("Do you wish to change it (Yes/No/Quit/Abort/Backup/List)? ","YNQABL","N")
  1914.     if key="B" then
  1915.       do
  1916.       if i>1 then i=i-2
  1917.       iterate
  1918.       end
  1919.     if key="L" then
  1920.        do
  1921.        i=i-1
  1922.        call Display 'newsrc.',1,' (NEWSRC file)'
  1923.        end
  1924.     if key="N" then iterate
  1925.     else if key="Q" then leave
  1926.     else if key="A" then
  1927.        do
  1928.        if promptkey("Really lose your changes? ","YN","N")="N" then
  1929.           do
  1930.           i=i-1
  1931.           iterate
  1932.           end
  1933.        else
  1934.          do
  1935.           call filein 'newsrc.',tempnewsrc
  1936.           "erase "tempnewsrc
  1937.           return 0
  1938.           end
  1939.        end
  1940.    else
  1941.       do
  1942.       say
  1943.       if subscribed="Subscribed" then 
  1944.          say "Unsubscribe from "gn"/"
  1945.       else say "Subscribe to "gn"/"
  1946.       say "Delete "gn" from your NEWSRC/"
  1947.       say "Reset the highest article read for "gn"/"
  1948.       say "Order "gn" within your NEWSRC by a key/"
  1949.       say "Abort changes to "gn"/"
  1950.       say
  1951.       if subscribed="Subscribed" then 
  1952.          key=promptkey("(U/D/R/O/A)? ","UDRAO","A")
  1953.       else key=promptkey("(S/D/R/O/A)? ","SDRAO","A")
  1954.       if key="A" then 
  1955.         do
  1956.         newscr.i=templine
  1957.         i=i-1
  1958.         iterate
  1959.         end
  1960.       else if key="S" then
  1961.          do
  1962.          parse var newsrc.i gn range
  1963.          stat=right(gn,1)
  1964.          if pos(stat,":!")\=0 then
  1965.             gn=left(gn,length(gn)-1)
  1966.          stat=":"
  1967.          newsrc.i=gn||": "||range
  1968.          i=i-1
  1969.          iterate
  1970.          end
  1971.       else if key="U" then
  1972.          do
  1973.          parse var newsrc.i gn range
  1974.          stat=right(gn,1)
  1975.          if pos(stat,":!")\=0 then
  1976.             gn=left(gn,length(gn)-1)
  1977.          newsrc.i=gn||"! "||range
  1978.          i=i-1
  1979.          iterate
  1980.          end
  1981.       else if key="D" then
  1982.          do
  1983.          if promptkey("Really delete the entry? ","YN")="N" then iterate
  1984.          deleted=1
  1985.          newsrc.i=""
  1986.          i=i-1
  1987.          iterate
  1988.          end
  1989.       else if key="R" then
  1990.          do
  1991.          if promptkey("Really clear the highest article read? ","YN")="N" then iterate
  1992.          parse var newsrc.i gn range
  1993.          newsrc.i=gn||" 1-1"
  1994.          i=i-1
  1995.          iterate
  1996.          end
  1997.       else if key="O" then
  1998.          do
  1999.          say
  2000.          say "Enter a 1 to 6 digit key for the group (use small numbers for groups"
  2001.          say "  that you would like near or at the beginning of your NEWSRC file."
  2002.          say "  Groups with the same key will sort alphabetically.)"
  2003.          say
  2004.          call charout , 'Key value for "'gn'": '
  2005.          parse pull key.i
  2006.          if key.i="" then key.i=999999
  2007.          do while length(key.i)>6 | \datatype(key.i,'W')
  2008.             call charout , 'Please re-enter the key for "'gn'": '
  2009.             parse pull key.i
  2010.             if key.i="" then key.i=999999
  2011.          end
  2012.          sortbykey=1
  2013.          i=i-1
  2014.          iterate
  2015.          end
  2016.       end
  2017.    end
  2018.    if deleted then
  2019.       do
  2020.       tempnewsrc2=SysTempFileName(addslash(settings.tempdir)||'TEMP????.NRC')
  2021.       call fileout 'newsrc.', tempnewsrc2, 1, 1, 1
  2022.       call filein 'newsrc.', tempnewsrc2
  2023.       "erase "tempnewsrc2
  2024.       end
  2025.    if sortbykey then
  2026.       do
  2027.       do i=1 to newsrc.0
  2028.          newsrc.i=format(key.i,7)||' '||newsrc.i
  2029.       end
  2030.       do 
  2031.       call sortstem 'newsrc.'
  2032.       do i=1 to newsrc.0
  2033.          newsrc.i=subword(newsrc.i,2)
  2034.       end
  2035.       end
  2036.       end
  2037.    Call SysCLS
  2038.    say
  2039.    if promptkey("Review your modified newsrc file before saving? ","YN")="Y" then call Display 'newsrc.',1,' (Modified NEWSRC file)'
  2040.    say
  2041.    if promptkey("Keep the changes you have made? ","YN")="N" then
  2042.       do
  2043.       call filein 'newsrc.',tempnewsrc
  2044.       "erase "tempnewsrc
  2045.       return 0
  2046.       end
  2047.    call fileout 'newsrc.',settings.newsrcname, 1, 1, 1
  2048.    "erase "tempnewsrc
  2049.    return 1
  2050.  
  2051. sortstem: 
  2052.    parse arg stem,_needle
  2053.    sortinfile=SysTempFileName(addslash(settings.tempdir)||'SORT????.IN')
  2054.    sortoutfile=SysTempFileName(addslash(settings.tempdir)||'SORT????.OUT')
  2055.    call fileout stem,sortinfile,,,1,_needle
  2056.    if stream(sortinfile,'C','QUERY SIZE')<settings.sortmaxbytes then
  2057.       do
  2058.       say "Sorting the list..."
  2059.       settings.sortcommand "<"sortinfile" >"sortoutfile "2> nul"
  2060.       trc=filein(stem,sortoutfile)
  2061.       "erase "sortoutfile
  2062.       return 0
  2063.       end
  2064.    else 
  2065.       do
  2066.       if _needle\="" then trc=filein(stem,sortinfile)
  2067.       say "The file was too big to sort..."
  2068.       say "It was "stream(sortinfile,'C','QUERY SIZE')" bytes long."
  2069.       say "The command "settings.sortcommand" can only handle "settings.sortmaxbytes" bytes."
  2070.       say "You can replace the this sort command with another sort program"
  2071.       say "to be able to sort larger files."
  2072.       say "Press enter to continue..."
  2073.       parse pull
  2074.       end
  2075.    "erase "sortinfile
  2076.    return 1
  2077.  
  2078. promptkey: procedure expose settings. newsrc. sock
  2079.    parse arg message, keys, default
  2080.    call charout , message
  2081.    if default\="" then keys=keys||d2c(13)
  2082.    do until pos(key,keys)\=0
  2083.       key=translate(SysGetKey('NoEcho'))
  2084.    end
  2085.    if key=d2c(13) then key=translate(default)
  2086.    say key
  2087.    return key
  2088.  
  2089. /*
  2090.  
  2091. The 'Simplified' newsrc file:
  2092.  
  2093. The date of the file is used to find 'new' newsgroups.
  2094.  
  2095. newsgroup.name[:|!] [currentarticle]
  2096.  
  2097. Where:  ':' indicates a 'subscribed' group
  2098.         '!' indicates an 'unsubscribed' group
  2099.  
  2100. The 'currentarticle' is the number of the last article read in the group.
  2101.  
  2102. NO USE OF THE SUBSCRIBED/UNSUBSCRIBED STATUS IS MADE AT PRESENT!!
  2103.  
  2104. Note:  This reader *is* capable of using a Unix .newsrc file.
  2105.        Unimplemented features are ignored.
  2106.  
  2107. */
  2108.