home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 18 REXX / 18-REXX.zip / RXNEWS1A.ZIP / rexxnews.cmd < prev    next >
OS/2 REXX Batch file  |  1993-04-19  |  31KB  |  1,046 lines

  1. /*------------------------------------------------------------------
  2.  * RexxNews 
  3.  *------------------------------------------------------------------
  4.  * 04-15-93 by Albert L. Crosby
  5.  *------------------------------------------------------------------
  6.  * Portions of this package are based on rnr.cmd :
  7.  *------------------------------------------------------------------
  8.  * 08-09-92 originally by Patrick J. Mueller
  9.  *------------------------------------------------------------------*/
  10.  
  11. settings.=""
  12. settings.version="RexxNews v. 1.0a by Albert Crosby"
  13. settings.varnames="newsrcname overlap headers displayatgroup rows cols ",
  14.                   "groupname groupstat grouphighest groupnewsrcline ",
  15.                   "newsrcdate newsrctime newgroupsatconnect etcdir ",
  16.                   "rexxnewsdir newarticlesatgroup postingok newuser",
  17.                   "server usexhdr"
  18.  
  19. settings.vartypes="A W B B W W ",
  20.                   "A A W W ",
  21.                   "W W B A",
  22.                   "A B B B",
  23.                   "A B"
  24.  
  25. settings.etcdir=value('etc',,'OS2ENVIRONMENT')
  26.  
  27. /*USER DEFINEABLE CONSTANTS *//********************************/
  28. settings.newsrcname='newsrc' /* Name of newsrc file (within the etcdir)      */
  29. settings.newgroupsatconnect=1 /* Show newgroups at connect time              */
  30. settings.overlap=2            /* Overlap in paging                           */
  31. settings.headers=1            /* Display headers, 1=Yes, 0=No                */
  32. settings.displayatgroup=1     /* Display first article on entering newsgroup */
  33. settings.savenewsrcatexit=0   /* Save the newsrc in case of an error */
  34. settings.newarticlesatgroup=1 /* Display new article subjects when moved to group */
  35. /****************************//***********************************************/
  36.  
  37. trace off
  38.  
  39. signal on halt name shutdown
  40.  
  41. parse arg argserver .
  42.  
  43. call opening
  44.  
  45. /*------------------------------------------------------------------
  46.  * initialize system function package
  47.  *------------------------------------------------------------------*/
  48. if RxFuncQuery("SysLoadFuncs") then
  49.    do
  50.    rc = RxFuncAdd("SysLoadFuncs","RexxUtil","SysLoadFuncs")
  51.    rc = SysLoadFuncs()
  52.    end
  53.  
  54. /*------------------------------------------------------------------
  55.  * initialize socket function package
  56.  *------------------------------------------------------------------*/
  57. if RxFuncQuery("SockLoadFuncs") then
  58.    do
  59.    rc = RxFuncAdd("SockLoadFuncs","RxSock","SockLoadFuncs")
  60.    rc = SockLoadFuncs()
  61.    end
  62.  
  63. parse source . . name
  64. settings.rexxnewsdir=filespec('drive',name)||filespec('path',name)
  65.  
  66. parse value SysTextScreenSize() with settings.rows settings.cols
  67.  
  68. call opening
  69.  
  70. call loadsettings
  71.  
  72. if argserver\="" then settings.server=argserver
  73.  
  74. if (settings.server = "") then
  75.    do
  76.    say "Expecting a news server name to be passed as a parameter or in the the"
  77.    say "configuration file."
  78.    exit 1
  79.    end
  80.  
  81.  
  82. if settings.newsrcname="newsrc" then settings.newsrcname=settings.etcdir||'\'||settings.newsrcname
  83.  
  84. settings.newuser=\loadnewsrc()
  85.  
  86. say
  87. say 'Connecting to server...'
  88.  
  89. /*------------------------------------------------------------------
  90.  * get address of server
  91.  *------------------------------------------------------------------*/
  92. rc = SockGetHostByName(settings.server,"host.!")
  93. if (rc = 0) then
  94.    do
  95.    say "Unable to resolve server name" settings.server
  96.    exit
  97.    end
  98.  
  99. server = host.!addr
  100.  
  101. /*------------------------------------------------------------------
  102.  * open socket
  103.  *------------------------------------------------------------------*/
  104. sock = SockSocket("AF_INET","SOCK_STREAM",0)
  105. if (sock = -1) then
  106.    do
  107.    say "Error opening socket:" errno
  108.    exit
  109.    end
  110.  
  111. /*------------------------------------------------------------------
  112.  * connect socket
  113.  *------------------------------------------------------------------*/
  114. server.!family = "AF_INET"
  115. server.!port   = 119
  116. server.!addr   = server
  117.  
  118. trc = SockConnect(sock,"server.!")
  119. if (trc = -1) then
  120.    Error(sock,rc,"Error connecting to newsserver :" errno)
  121.  
  122. trc = GetResponse(sock)
  123. do i = 1 to line.0
  124.    say line.i
  125. end
  126.  
  127. parse var line.1 code .
  128. if code=200 then
  129.    settings.postingok=1
  130. else settings.postingok=0
  131.  
  132. if code\=200 & code\=201 then
  133.    do
  134.    settings.savenewsrcatexit=0
  135.    signal shutdown
  136.    end
  137.  
  138. trc = SendMessage(sock,"xhdr")
  139. trc = GetResponse(sock)
  140.  
  141. parse var line.1 code .
  142. if code=501 then settings.usexhdr=1
  143. else settings.usexhdr=0
  144.  
  145. trc = SendMessage(sock,"MODE READER")
  146. trc = GetResponse(sock)
  147. parse var line.1 code .
  148. if code\=500 then say "Server supports the INN extensions."
  149.  
  150. say
  151.  
  152. if settings.newuser then call help 'intro'
  153. else /* Don't show new users all of the groups that exist (unless they ask)... */
  154.    if settings.newgroupsatconnect then
  155.       do
  156.       call newgroups
  157.       say
  158.       end
  159.  
  160. rc = Interact(sock)
  161.  
  162. /*------------------------------------------------------------------
  163.  * quittin' time!
  164.  *------------------------------------------------------------------*/
  165.  
  166. Shutdown:
  167.  
  168. if settings.savenewsrcatexit\=0 then call fileout 'newsrc.',settings.newsrcname, 1
  169.  
  170. trc = SendMessage(sock,"quit")
  171. trc = SockSoclose(sock)
  172.  
  173. exit
  174.  
  175. /*------------------------------------------------------------------
  176.  * get command and execute in a loop
  177.  *------------------------------------------------------------------*/
  178. Interact:        procedure expose !. settings. newsrc.
  179.    sock = arg(1)
  180.  
  181.    /*------------------------------------------------------------------
  182.     * commands is the commands currently implemented in rnr.cmd 
  183.     *------------------------------------------------------------------*/
  184.    rawcommands = "STAT BODY HEAD NEWNEWS LIST RAW"
  185.  
  186.    group=""
  187.    first=0
  188.    last=0
  189.    current=0
  190.    remain=0
  191.    articleavailable=0
  192.  
  193.    do forever
  194.       commandline=prompt()
  195.  
  196.       parse var commandline command args 
  197.  
  198.       if commandline=="" then iterate
  199.  
  200.       if abbrev("QUIT",translate(command)) then
  201.          do
  202.          settings.savenewsrcatexit=1
  203.          leave
  204.          end
  205.  
  206.       if ("EXIT"==translate(command)) then
  207.          do
  208.          call charout ,"Are you sure (newsrc will not be updated!)? "
  209.          if translate(SysGetKey("Echo"))="Y" then 
  210.             do
  211.             settings.savenewsrcatexit=0
  212.             leave
  213.             end
  214.          say
  215.          iterate
  216.          end
  217.  
  218.       if ("?" == command) | abbrev("HELP",translate(command)) then
  219.          do
  220.          rc = Help(args)
  221.          iterate
  222.          end
  223.  
  224.       if ("SET" == translate(command)) then
  225.          do
  226.          call set args
  227.          iterate
  228.          end
  229.  
  230.       if ("SHOW" == translate(command)) then
  231.          do
  232.          call display 'newsrc.',1
  233.          iterate
  234.          end
  235.  
  236.       if abbrev("DETAILS",translate(command),2) then
  237.          do
  238.          call details
  239.          iterate
  240.          end
  241.  
  242.       if abbrev("OS2",translate(command)) then
  243.          do
  244.          args
  245.          iterate
  246.          end
  247.  
  248.       if abbrev("TIME",translate(command)) then
  249.          do
  250.          say 'Current time is:' time() 'on' date()
  251.          say
  252.          iterate
  253.          end
  254.  
  255.       if abbrev("GROUP",translate(command)) then
  256.          do
  257.          if args=="" then
  258.             do
  259.             say 'Expecting a group name.'
  260.             iterate
  261.             end
  262.          articleavailable=0
  263.          trc = SendMessage(sock,'group '||args)
  264.          trc = GetResponse(sock)
  265.          parse var line.1 code .
  266.          if code=411 then 
  267.             do
  268.             say 'No active group named 'args'.'
  269.             group=settings.groupname
  270.             iterate
  271.             end
  272.          parse var line.1 code remain first last group .
  273.          if remain>0 then
  274.             do
  275.             settings.unread=1
  276.             current=checknewsrc(group)
  277.             if first>current then current=first
  278.             else 
  279.                do
  280.                if current>=last then 
  281.                   do
  282.                   say "No unread articles in "group
  283.                   settings.unread=0
  284.                   current=last
  285.                   trc = SendMessage(sock,'stat '||current)
  286.                   trc = GetResponse(sock)
  287.                   iterate
  288.                   end
  289.                else
  290.                   do
  291.                   trc = SendMessage(sock,'stat '||current)
  292.                   trc = GetResponse(sock)
  293.                   parse var line.1 code .
  294.                   if code\=223 then current=first
  295.                   else
  296.                      do
  297.                      trc = SendMessage(sock,'next')
  298.                      trc = GetResponse(sock)
  299.                      parse var line.1 code .
  300.                      if code=223 then parse var line.1 . current .
  301.                      end
  302.                   end
  303.                end
  304.             if settings.newarticlesatgroup then call headers 'subject'
  305.             if settings.displayatgroup & settings.unread then current=article(current)
  306.             end
  307.          else
  308.             do
  309.             say "No articles in group "group
  310.             group=settings.groupname
  311.             end
  312.          iterate
  313.          end
  314.  
  315.       if abbrev("NEXT",translate(command)) then
  316.          do
  317.          if group="" then
  318.             do
  319.             say "You must select a group first."
  320.             iterate
  321.             end
  322.          call next
  323.          iterate
  324.          end                                                    
  325.  
  326.       if abbrev("LAST",translate(command)) | abbrev("BACK",translate(command)) then
  327.          do
  328.          if group="" then
  329.             do
  330.             say "You must select a group first."
  331.             iterate
  332.             end
  333.          call last
  334.          iterate
  335.          end
  336.  
  337.       if abbrev("ARTICLE",translate(command)) | abbrev("DISPLAY",translate(command)) then
  338.          do
  339.          if group="" then
  340.             do
  341.             say "You must select a group first."
  342.             iterate
  343.             end
  344.          current=article(args)
  345.          iterate
  346.          end
  347.  
  348.       if abbrev("NEWGROUPS",translate(command)) then
  349.          do
  350.          call newgroups args
  351.          iterate
  352.          end
  353.  
  354.       if abbrev("AUTHORS",translate(command)) | abbrev("FROM",translate(command)) then
  355.          do
  356.          if group="" then
  357.             do
  358.             say "You must select a group first."
  359.             iterate
  360.             end
  361.          call headers 'from',args
  362.          iterate
  363.          end
  364.  
  365.       if abbrev("SAVE",translate(command)) then
  366.          do
  367.          if group="" then
  368.             do
  369.             say "You must select a group first."
  370.             iterate
  371.             end
  372.          if \articleavailable then
  373.             do
  374.             say "No article available to be saved.  Display an article first."
  375.             iterate
  376.             end
  377.          if args="" then
  378.             do
  379.             call charout , "Write article to file: "
  380.             parse pull args
  381.             if args="" then iterate
  382.             end
  383.          call fileout 'line.',args
  384.          iterate
  385.          end
  386.  
  387.       if abbrev("SUBJECTS",translate(command)) then
  388.          do
  389.          if group="" then
  390.             do
  391.             say "You must select a group first."
  392.             iterate
  393.             end
  394.          call headers 'subject',args
  395.          iterate
  396.          end
  397.  
  398.       if abbrev("SEARCH",translate(command)) then
  399.          do
  400.          call search(args)
  401.          iterate
  402.          end
  403.  
  404.       if wordpos(translate(command),rawcommands)=0 then
  405.          do
  406.          say 'Unknown or unimplemented command: 'command
  407.          iterate
  408.          end
  409.  
  410.       if "RAW"==translate(command) then command=args
  411.  
  412.       articleavailable=0
  413.       trc = SendMessage(sock,commandline)
  414.       trc = GetResponse(sock)
  415.  
  416.       call display 'line.',1
  417.  
  418.    end
  419.  
  420.    return ""
  421.  
  422. /*------------------------------------------------------------------
  423.  * display
  424.  *------------------------------------------------------------------*/
  425. Display: 
  426.    parse arg list, n, string, initial, keylist
  427.    if list="" then return ""
  428.    if n="" then n=2
  429.    if initial="" then initial=0
  430.    _r=initial+1
  431.    _cls=0;
  432.    say
  433.  
  434.    interpret "do _i = n to "list"0+1;",
  435.       "if pos(d2c(12),"list"_i,1)\=0 then do; _cls=1;_r=_r+((settings.rows-settings.overlap)-_r//(settings.rows-settings.overlap));end;",
  436.       "if _r//(settings.rows-settings.overlap)=0 | _i>"list"0 then",
  437.          "do;",
  438.          'call charout ,"---MORE'string'---";',
  439.          "if _i>"list"0 then call charout ,'<END>';",
  440.          'key=SysGetKey('NOECHO');',
  441.          'call charout ,d2c(13)||copies(" ",settings.cols-1)||d2c(13);',
  442.          'if "Q"==translate(key) then return "";',
  443.          'if d2c(13)==key then _r=_r-1;',
  444.          'if "U"==translate(key) & _i>2*(settings.rows-settings.overlap) then _i=_i-2*(settings.rows-settings.overlap);',
  445.          'if "T"==translate(key) then do;call SysCls;_i=n;_r=1;end;',
  446.          'if pos(translate(key),translate(keylist))\=0 then return translate(key);',
  447.          'if _cls then do; Call SysCls; _cls=0; end;',
  448.          'if length('list'_i)>settings.cols',
  449.             'then _r=_r+(length('list'_i)%settings.cols);',
  450.          'end;',
  451.       "if _i<="list"0 then say "list"_i;",
  452.       "_r=_r+1;",
  453.    "end"
  454.    return ""
  455.  
  456. /* An Alternate display function that limits displayed lines with a 'needle' */
  457.  
  458. DisplayN:
  459.    parse arg list, n, string, needle
  460.    if list="" then return
  461.    if n="" then n=2
  462.    _r=1
  463.    say
  464.  
  465.    interpret "do _i = n to "list"0+1;",
  466.       "if _r//(settings.rows-settings.overlap)=0 | _i>"list"0 then",
  467.          "do;",
  468.          'call charout ,"---MORE'string'---";',
  469.          "if _i>"list"0 then call charout ,'<END>';",
  470.          'key=SysGetKey('NOECHO');',
  471.          'call charout ,d2c(13)||copies(" ",settings.cols-1)||d2c(13);',
  472.          'if "Q"==translate(key) then return;',
  473.          'if d2c(13)==key then _r=_r-1;',
  474.          'if "U"==translate(key) & _i>2*(settings.rows-settings.overlap) then _i=_i-2*(settings.rows-settings.overlap);',
  475.          'if "T"==translate(key) then do;call SysCls;_i=n;_r=1;end;',
  476.          'if length('list'_i)>settings.cols',
  477.             'then _r=_r+(length('list'_i)%settings.cols);',
  478.          'end;',
  479.       "if _i=1 | pos(translate(needle),translate("list"_i))\=0 then",
  480.          "do;",
  481.          "if _i<="list"0 then say "list"_i;",
  482.          "_r=_r+1;",
  483.          "end;",
  484.    "end"
  485.    return ""
  486.  
  487. /*------------------------------------------------------------------
  488.  * help
  489.  *------------------------------------------------------------------*/
  490. Help: procedure expose settings.
  491.    arg topic
  492.    if topic="" then topic="general"
  493.    if "TOPICS"==translate(topic) then
  494.       do
  495.       topics.=""
  496.       call SysFileTree settings.rexxnewsdir||'*.rxn','topics','FO'
  497.       do i=topics.0 to 1 by -1
  498.          n=i+2
  499.          topics.n=filespec('name',topics.i)
  500.          topics.n=left(topics.n,pos('.',topics.n)-1)
  501.       end
  502.       n=topics.0+3
  503.       topics.n='topics'
  504.       topics.0=n
  505.       topics.1=settings.version
  506.       topics.2="Help is available for the following topics:"
  507.       call SysCls
  508.       call Display 'topics.',1,' (RexxNews Help Topics)'
  509.       return 1
  510.       end
  511.    if filein('help.',settings.rexxnewsdir||topic||'.rxn')=0 then
  512.       do
  513.       say "No help available for '"topic"'."
  514.       say "Type HELP for general information or HELP INTRO for an introduction to RexxNews"
  515.       return 0
  516.       end
  517.    call SysCls
  518.    call Display 'help.',1,' ('||topic||' help)'
  519.    return 1
  520.  
  521. /*------------------------------------------------------------------
  522.  * get a response from the server
  523.  *------------------------------------------------------------------*/
  524. GetResponse:     procedure expose !. line. settings.
  525.    sock = arg(1)
  526.  
  527.    moreids = "100 215 220 221 222 230 231"
  528.  
  529.    progress="\|/-"
  530.  
  531.    line.0 = 1
  532.    line.1 = GetResponseLine(sock)
  533.  
  534.    parse var line.1 rid .
  535.  
  536.    if (wordpos(rid,moreids) = 0) then
  537.       return ""
  538.  
  539.    o=0
  540.  
  541.    do forever
  542.       call charout , substr(progress,1+o//length(progress),1)||d2c(13)
  543.       o = line.0 + 1
  544.  
  545.       line.o = GetResponseLine(sock)
  546.  
  547.       if (line.o = ".") then
  548.          do
  549.          call charout , " "||d2c(13)
  550.          return ""
  551.          end
  552.  
  553.       line.0 = o
  554.    end
  555.    call charout " "||d2c(13)
  556.  
  557.    return ""
  558.  
  559. /*------------------------------------------------------------------
  560.  * get a line from the server
  561.  *------------------------------------------------------------------*/
  562. GetResponseLine: procedure expose !.
  563.    sock = arg(1)
  564.  
  565.    crlf = d2c(13) || d2c(10)
  566.  
  567.    if (symbol('!.buff') = "LIT") then
  568.       !.buff = ""
  569.  
  570.    do while (pos(crlf,!.buff) = 0)
  571.       rc = SockRecv(sock,"data",8000)
  572.       !.buff = !.buff || data
  573.    end
  574.  
  575.    p = pos(crlf,!.buff)
  576.  
  577.    line = substr(!.buff,1,p-1)
  578.    !.buff = substr(!.buff,p+2)
  579.  
  580.    return line
  581.  
  582. /*------------------------------------------------------------------
  583.  * send a string to the server
  584.  *------------------------------------------------------------------*/
  585. SendMessage:     procedure expose !.
  586.    sock = arg(1)
  587.    data = arg(2) || d2c(13) || d2c(10)
  588.  
  589.    len = length(data)
  590.    do while (len > 0)
  591.       i = SockSend(sock,data);
  592.  
  593.       if (errno <> 0) then
  594.          Error(-1,rc,"Error sending data to server.")
  595.  
  596.       if (i <= 0) then
  597.          Error(sock,100,"Server closed the connection.")
  598.  
  599.       data = substr(data,len+1)
  600.       len  = length(data)
  601.    end
  602.  
  603.    return 0
  604.  
  605. /*------------------------------------------------------------------
  606.  * halting ...
  607.  *------------------------------------------------------------------*/
  608. Halting:
  609.    Error(sock,1,"error on line" sigl)
  610.  
  611. /*------------------------------------------------------------------
  612.  * exit with a message and return code
  613.  *------------------------------------------------------------------*/
  614. Error: procedure
  615.    sock = arg(1)
  616.    retc = arg(2)
  617.    msg  = arg(3)
  618.  
  619.    if (sock <> -1) then
  620.       rc = SockSoClose(sock)
  621.  
  622.    say msg
  623.  
  624.    exit retc
  625.  
  626. opening:
  627.    /*------------------------------------------------------------------
  628.     * initialize system function package
  629.     *------------------------------------------------------------------*/
  630.    if RxFuncQuery("SysLoadFuncs") then
  631.       do
  632.       rc = RxFuncAdd("SysLoadFuncs","RexxUtil","SysLoadFuncs")
  633.       rc = SysLoadFuncs()
  634.       end
  635.    
  636.    /*------------------------------------------------------------------
  637.     * initialize socket function package
  638.     *------------------------------------------------------------------*/
  639.    if RxFuncQuery("SockLoadFuncs") then
  640.       do
  641.       rc = RxFuncAdd("SockLoadFuncs","RxSock","SockLoadFuncs")
  642.       rc = SockLoadFuncs()
  643.       end
  644.  
  645.    call SysCls
  646.    say settings.version 
  647.    say 
  648.    say "A NNTP NewsReader Client in REXX and rxSock for OS/2 2.x"
  649.    say
  650.    return
  651.  
  652. filein:
  653.    arg stem, filename
  654.    if arg()<2 then return 0
  655.    if stem="" | filename="" then 
  656.       do
  657.       say "Error reading file "filename" into stem "stem
  658.       return 0
  659.       end
  660.    _i=0
  661.    if stream(filename,'c','OPEN READ')=='READY:' then
  662.       do
  663.       interpret 'do while stream(filename,"S")="READY";',
  664.          "_i=_i+1;",
  665.          stem"_i=linein(filename);",
  666.       'end;',
  667.       stem"0=_i"
  668.       end
  669.    call stream filename,'c','CLOSE'
  670.    interpret '_n='stem'0;if 'stem'_n="" then do;'stem'0=_n-1;_i=_n;end'
  671.    return _i
  672.  
  673. loadsettings: procedure expose settings.
  674.    if filein('set.',settings.etcdir||'\REXXNEWS.CFG')=0 then return 0
  675.    do i=1 to set.0
  676.       if set.i\="" & left(set.i,1)\=';' then call set(set.i)
  677.    end
  678.    return 1
  679.  
  680.  
  681. loadnewsrc:
  682.    say 'Processing newsrc file...'
  683.    say settings.newsrcname
  684.    if stream(settings.newsrcname,'c','query exists')\="" then
  685.       do
  686.       datetime=stream(settings.newsrcname ,'c','query datetime')
  687.       parse var datetime mo'-'dd'-'yy hh':'mm':'ss
  688.       settings.newsrcdate=yy||mo||dd
  689.       settings.newsrctime=hh||mm||ss
  690.       say filein('newsrc.',settings.newsrcname) 'lines processed.'
  691.       return 1
  692.       end
  693.    else
  694.       do
  695.       settings.newsrcdate=""
  696.       settings.newsrctime=""
  697.       newsrc.0=0
  698.       return 0
  699.       end
  700.  
  701. updatenewsrc: procedure expose settings. newsrc.
  702.    parse arg group, article
  703.    if translate(group)\=translate(settings.groupname) then
  704.       do
  705.       say "Error updating newsrc: wrong group."
  706.       return
  707.       end
  708.    i=settings.groupnewsrcline
  709.    if article<=settings.grouphighest then return
  710.    newsrc.i=group||settings.groupstat||' 1-'||article
  711.    return
  712.  
  713. prompt: procedure expose sock group first last current settings.
  714.    say
  715.    if group\="" then 
  716.       do
  717.       trc = SendMessage(sock,'stat')
  718.       trc = GetResponse(sock)
  719.       parse var line.1 code current msgid .
  720.       say group "("first"-"last") --- Current article "current" ["last-current" remaining]"
  721.       end
  722.    if settings.newuser then
  723.       do
  724.       say "Enter HELP INTRO to review the introduction to RexxNews."
  725.       if group="" then
  726.          say "Enter Group <groupname> to move to a group."
  727.       else say "Enter Display to see the current article or Next for the next article."
  728.       end
  729.    say "Enter RexxNews command (or help or quit)"
  730.    parse pull commandline
  731.    return commandline
  732.  
  733. fileout:
  734.    parse arg stem, filename, compress
  735.    if \datatype(compress,'B') | compress>1 then compress=0
  736.    if stem="" then return 0;
  737.    interpret "if "stem"0 = 0 then return 0"
  738.    call stream filename,'c','open write'
  739.    call lineout filename,,1
  740.    if stream(filename,'s')\="READY" then
  741.       do
  742.       say "Error writing to file "filename": "_state
  743.       return 0
  744.       end
  745.    _n=0
  746.    interpret 'do _i=1 to 'stem'0;',
  747.       'if (\compress) | strip('stem'_i)\="" then',
  748.          'do;',
  749.          '_n=_n+1;',
  750.          'call lineout filename, 'stem'_i;',
  751.          'end;',
  752.    'end'
  753.    say _n' line(s) written to 'filename
  754.    return _i
  755.  
  756. checknewsrc: procedure expose settings. newsrc.
  757.    parse arg group
  758.    if group="" then return 0
  759.    do i= 1 to newsrc.0
  760.       parse var newsrc.i name pointer
  761.       if name="" then iterate
  762.       stat=right(name,1)
  763.       if pos(stat,':!')=0 then
  764.          do
  765.          stat=" "
  766.          end
  767.       else
  768.          do
  769.          name=left(name,length(name)-1)
  770.          end
  771.       if translate(name)=translate(group) then
  772.          do
  773.          settings.groupstat=stat
  774.          settings.groupname=name
  775.          settings.groupnewsrcline=i
  776.          n=translate(pointer,"  ","-,")
  777.          n=word(n,words(n))
  778.          if \datatype(n,'w') then n=0
  779.          settings.grouphighest=n
  780.          return n
  781.          end
  782.    end
  783.    settings.groupnewsrcline=i
  784.    settings.grouphighest=0
  785.    settings.groupstat=' '
  786.    settings.groupname=group
  787.    newsrc.0=i
  788.    newsrc.i=group||' 0'
  789.    return 0
  790.  
  791. newgroups: procedure expose sock settings.
  792.    arg _datetime
  793.    parse var _datetime _date _time
  794.  
  795.    if _time="" then _time="000000"
  796.    if _date="" then
  797.       do
  798.       _date=settings.newsrcdate
  799.       _time=settings.newsrctime
  800.       end
  801.    trc = SendMessage(sock,'newgroups'||' '||_date||' '||_time)
  802.    trc = GetResponse(sock)
  803.    parse var line.1 code number id  .
  804.    if line.0=1 then
  805.       do
  806.       say 'No new groups since '_date _time'.'
  807.       return
  808.       end
  809.    line.1='New groups since '_date _time'.'
  810.    call SysCLS
  811.    call Display 'line.',1
  812.    return
  813.    
  814. xhdr: procedure  expose sock current settings.
  815.    parse arg tag, article
  816.    if \datatype(article,'W') then article=""
  817.    if tag="" then tag="subject"
  818.    if settings.usexhdr then 
  819.       do
  820.       trc = SendMessage(sock,'xhdr '||tag||' '||article)
  821.       trc = GetResponse(sock)
  822.       value=""
  823.       if line.0>1 then parse var line.2 article value
  824.       return value
  825.       end
  826.    trc = SendMessage(sock,'head '||article)
  827.    trc = GetResponse(sock)
  828.    parse var line.1 code .
  829.    if code\=221 then return ""
  830.    do i=2 to line.0
  831.       parse var line.i ln":"value
  832.       if translate(tag)=translate(ln) then leave
  833.       value=""
  834.    end
  835.    trc = SendMessage(sock,'stat '||current)
  836.    trc = GetResponse(sock)
  837.    return value
  838.  
  839. article: procedure expose settings. newsrc. sock articleavailable line. current first last
  840.    arg num
  841.    if \articleavailable | num\=current then
  842.       do
  843.       if settings.headers=1
  844.          then command='ARTICLE'
  845.          else command='BODY'
  846.       trc = SendMessage(sock,command||' '||num)
  847.       trc = GetResponse(sock)
  848.       parse var line.1 code number id .
  849.       if code\=220 then
  850.          do
  851.          say "Error retrieving article:  Code "code
  852.          articleavailable=0
  853.          return 0
  854.          end
  855.       articleavailable=1
  856.       call updatenewsrc settings.groupname, number
  857.       end
  858.    else number=current
  859.    call SysCLS
  860.    current=number
  861.    key=Display('line.', ,' (line "_i-1" of "'line.0'" article "current" of "last")', ,'NL')
  862.    if key='N' then return next()
  863.    if key='L' then return last()
  864.    return number
  865.  
  866. headers: procedure expose settings. sock first last current
  867.    parse arg tag, range, needle
  868.    if range="" then range=current||'-'||last
  869.    if range="*" then range=first||'-'||last
  870.    if tag="" then tag='subject'
  871.    if (translate(tag)\="GROUPS") then 
  872.       do
  873.       say "Retreiving the "tag" field for article(s) "range"..."
  874.       msg=tag||' fields for article(s) 'range
  875.       if needle\="" then msg=msg||' containing 'needle':'
  876.       else msg=msg||':'
  877.       if settings.usexhdr then
  878.          do
  879.          trc = SendMessage(sock,'xhdr '||tag||' '||range)
  880.          trc = GetResponse(sock)
  881.          parse var line.1 code number id .
  882.          end
  883.       else
  884.          do
  885.          parse var range begin'-'end
  886.          line.=""
  887.          n=2
  888.          do i=begin to end
  889.             line.n=i||' '||xhdr(tag,i)
  890.          end
  891.          code=221
  892.          end
  893.       end
  894.    else 
  895.       do
  896.       say "Retrieving a list of all groups... This may take a few moments."
  897.       msg='List of groups containing '||needle||' in their names:'
  898.       trc = SendMessage(sock,'list')
  899.       trc = GetResponse(sock)
  900.       parse var line.1 code number id .
  901.       end
  902.    if code\=221 & code\=215 then
  903.       do
  904.       say "Error retrieving list:  Code "code
  905.       return 0
  906.       end
  907.    if line.0=1 then
  908.       do
  909.       say "No articles in "range"."
  910.       return
  911.       end
  912.    line.1=msg
  913.    call SysCLS
  914.    if needle="" then call Display 'line.',1
  915.    else call DisplayN 'line.',1,,needle
  916.    return number
  917.  
  918. set: procedure expose settings.
  919.    parse arg command
  920.    if command\="" then
  921.       do
  922.       parse var command variable value 
  923.       i=wordpos(translate(variable),translate(settings.varnames))
  924.       if i=0 then
  925.          do
  926.          say "Unknown variable: "variable
  927.          return 0
  928.          end
  929.       else
  930.          do
  931.          if word(settings.vartypes,i)="A" | datatype(value,word(settings.vartypes,i)) then
  932.             interpret "settings."variable"=value"
  933.          else say "Improper argument type for "variable"."
  934.          return 1
  935.          end
  936.       end
  937.    set.=""
  938.    set.0=words(settings.varnames)+5
  939.    set.1=settings.version "Settings"
  940.    set.3=left("Variable",25)||left("Value",25)||"Type"
  941.    set.4=left("========",25)||left("=====",25)||"===="
  942.    do i=1 to words(settings.varnames)
  943.       n=i+4
  944.       interpret "set.n=left(word(settings.varnames,i),25)||left(settings."word(settings.varnames,i)",25)||word(settings.vartypes,i)"
  945.    end
  946.    call Display 'set.',1,' (RexxNews SET values)'
  947.    return
  948.  
  949. details:
  950.    say
  951.    say "Current group: "group
  952.    say "Available articles: "remain
  953.    say "First article: "first
  954.    say "Last article: "last
  955.    say "Current article: "current
  956.    if group\="" then 
  957.       do
  958.       say "Article can be saved:" articleavailable
  959.       say "Subject: "xhdr(subject)
  960.       say "From:    "xhdr(from)
  961.       say "Lines:   "xhdr(lines)
  962.       _i=settings.groupnewsrcline
  963.       say "newsrc line #"_i
  964.       say "newsrc line: "newsrc._i
  965.       end
  966.    return
  967.  
  968. search: procedure expose settings. sock first last current
  969.    arg args
  970.    if args="" then return 0
  971.    !field=""
  972.    parse var args field rest 1 "RANGE " range . 1 "FOR " needle
  973.    if field='RANGE' | field='FOR' | (needle="" & rest="") then !field='SUBJECT'
  974.    if (needle="" & rest="") then needle=field
  975.    if range="" then range=current||'-'||last
  976.    else if range="*" then range=first||'-'||last
  977.    if !field\="" then field=!field
  978.    if settings.groupname="" & "GROUPS"\=translate(field) then
  979.       do
  980.       say "You must select a group first."
  981.       return 0
  982.       end
  983.    if needle="" then
  984.       do
  985.       say "The syntax of the SEARCH command is:"
  986.       say "  SEarch [<field>] [RANGE <range>] [FOR] <string>"
  987.       say "  See HELP SEARCH for more information."
  988.       return 0
  989.       end
  990.    call headers field, range, needle
  991.    return 1
  992.  
  993. next: procedure expose settings. newsrc. sock articleavailable current first last line.
  994.    if current=last then
  995.       do 
  996.       say "No more articles."
  997.       return current
  998.       end
  999.    trc = SendMessage(sock,'next')
  1000.    trc = GetResponse(sock)
  1001.    articleavailable=0
  1002.    current=article()
  1003.    return current
  1004.  
  1005. last: procedure expose settings. newsrc. sock articleavailable current first last line.
  1006.    if current=first then
  1007.       do 
  1008.       say "No previous article."
  1009.       return current
  1010.       end
  1011.    trc = SendMessage(sock,'LAST')
  1012.    trc = GetResponse(sock)
  1013.    articleavailable=0
  1014.    current= article()
  1015.    return current
  1016.  
  1017. /*
  1018.  
  1019. The 'Simplified' newsrc file:
  1020.  
  1021. The date of the file is used to find 'new' newsgroups.
  1022.  
  1023. newsgroup.name[:|!] [current_article]
  1024.  
  1025. Where:  ':' indicates a 'subscribed' group
  1026.         '!' indicates an 'unsubscribed' group
  1027.  
  1028. The 'current_article' is the number of the last article read in the group.
  1029.  
  1030. NO USE OF THE SUBSCRIBED/UNSUBSCRIBED STATUS IS MADE AT PRESENT!!
  1031.  
  1032. Note:  This reader *is* capable of using a Unix .newsrc file.
  1033.        Unimplemented features are ignored.
  1034.  
  1035. */
  1036.  
  1037. /* Comments:
  1038.  
  1039. The first release will not include a post command.
  1040.  
  1041. The second release may add the subscribed concept by adding a
  1042. nextgroup command.   The nextgroup command will move to the next
  1043. subscribed group in the .newsrc that has articles available to read.
  1044.  
  1045. */
  1046.