dorl@vms.macc.wisc.edu (Michael Dorl - MACC) (07/29/88)
******** copyright.doc ********
Copyright (C) 1988, University of Wisconsin Board of Regents,
all rights reserved.
Anyone may reproduce this work, in whole or in part, provided
that: (1) any copy of the entire work must show University of
Wisconsin as the source, and must include this notice; and (2)
any other use of this work must acknowledge the fact that the
material is coprighted by the University of Wisconsin Board of
Regents and is used by permission.
******** install.doc ********
News - A Native VMS News Reading Program
1.0 Introduction
News is a user agent program for reading News. The news database
is not kept on the VMS machine, instead the nntp protocol is used
to access the data on a server machine. The VMS machine must be
connected to an IP network and must run the Wollongong WIN/VX
network software.
2.0 Distribution Description
The distribution consists of 19 files. Each file is preceded by
a line of the form...
******** file_name ********
The following files are included...
FileName ND Description
COPYRIGHT.DOC Copyright notice
INSTALL.DOC This file
CONFIG.DAT * Configuration data
CONVERT.COM * Com file to rename Eunice save files
GETUSERNAME.FOR News source (subroutine)
INSTALL.COM * Com file to install news.exe
LINKNEWS.COM Com file to link News.Exe
MAKE.COM Com file to compile News source
NEWS.CLD * News command syntax definition
NEWS.DEF News source (include)
NEWS.DOC A user description of News
NEWS.FOR News source (main program and subroutines)
NEWS_CLD_INSTALL.COM * Com file to add News command syntax to system
SMG.DEF News source (include)
SMG_ROUTINES.FOR News source (include)
TNEWS.CLD Com file to define TNews command for testing
UP_DN_PRIV.FOR News source (subroutine)
USER_OPEN.COMMON News source (include)
USER_OPEN.FOR News source (subroutine )
Use an editor to break up the distribution into its parts. I
keep the file in directory .xxnews in my home directory.
3.0 Building News
The source code needs to be compiled, use the make.com file to do
this. Make accpets a list of Fortran options. If you want a
listing of the source do...
@make list
otherwise do
@make
Now link News using the linknews.com. If you intend to install
News sytem wide, you might want to include the notraceback
parameter....
@linknews notraceback
4.0 Configuring Site Dependent News Data
All site dependent data is kept in newsdir:config.dat. This
includes...
siteid the name of the machine, used when posting
articles. Include everything needed after
the username. eg. @a.b.c.edu
distribution a list of valid distributions.
organization text describing the organization controlling
your machine. Used when posting articles.
server the numeric ip number of the server.
Edit config.dat to use your definitions.
5.0 Running News
You can run News from your account or you can install it in a
system directory.
5.1 Running News without Installing It
Define symbol newsdir pointing to the directory containing
news.exe and config.dat. Edit config.dat so that contains
data for your site. Edit TNews.Cld so that it points at the
file containing news.ex. Define the TNews parameter with...
$set command tnews
Now run News with the TNews command.
5.2 Running News after Installing It
In order to install news on a system wide basis you need to
do the following...
compile news.
link news with the notraceback option.
edit config.dat to conform to data at your site.
create a system wide logical name, newsdir, pointing
at a file containing files news.exe and those marked
in the ND column in section 2.0.
install news.exe using newsdir:install.com. This is
not strictly required but does allow News to obtain
the user personal name from the Sys$Sytem:VMSMail.Dat
file for use when posting articles.
add the News command to the system command tables
using newsdir:news_cld_install.com.
update your boot procedure to define newsdir and to
install news.
6.0 User Documentation
File news.doc contains a user description of the program.
This file was written to describe News to the editor
developing a final user document. Its quite terse,
contains instructions to the editor, and is probably full
of gramatical and spelling errors. The final user
document may be made available at a later date.
7.0 Copyright Notice
Copyright (C) 1988, University of Wisconsin Board of Regents,
all rights reserved.
Anyone may reproduce this work, in whole or in part, provided
that: (1) any copy of the entire work must show University of
Wisconsin as the source, and must include this notice; and (2)
any other use of this work must acknowledge the fact that the
material is coprighted by the University of Wisconsin Board of
Regents and is used by permission.
******** config.dat ********
siteid: @vms.macc.wisc.edu
distribution: local, cs, uw, wi, usa, na, net, world, mod, comp, news
distribution: sci, rec, misc, soc, talk, macc
organization: University of Wisconsin Academic Computing Center
server: 128.104.30.17
******** convert.com ********
$ ! Convert.Com
$ !
$ ! Renames files in sys$login:news.dir form Eunice rrn form to
$ ! VMS news form.
$
$ ! Get next file
$
$NextFile:
$ File = F$Search ("*.*;*")
$ If File .eqs. "" Then Goto Exit
$ L = F$Length(File)
$ X = F$Locate ("]",File)+1
$ Dir = F$Extract(0,X,File)
$ File = F$Extract (X,L-X,File)
$
$ ! Change all $5 to _
$
$ NewFile = File
$
$NextDot:
$ L = F$Length (NewFile)
$ X = F$Locate ("$5",NewFile)
$ If X .eq. L Then Goto ExitDot
$ NewFile = F$Extract(0,X,NewFile) + "_" + F$Extract(X+2,L-X-2,NewFile)
$ Goto NextDot
$ExitDot:
$
$ ! Change rest of $ to nothing
$
$NextDollar:
$ L = F$Length (NewFile)
$ X = F$Locate ("$",NewFile)
$ If X .eq. L Then Goto ExitDollar
$ NewFile = F$Extract(0,X,NewFile) + F$Extract(X+1,L-X-1,NewFile)
$ Goto NextDollar
$ExitDollar:
$
$ ! Now display result
$
$ If File .eqs. NewFile Then Goto NoChange
$ Rename/Log 'File' 'NewFile'
$
$NoChange:
$ Goto NextFile
$
$Exit:
$ Exit
******** getusername.for ********
Integer Function GetUserName (UserId)
C Description:
C
C Function to obtain the user's name
C Returns success or failure.
Include 'News.Def'
C Parameter Definitions
Character *32 UserId
C External Routines
External Sys$GetJPI
Integer Sys$GetJPI
C Local Definitions
Include '($JPIDEF)'
Character *(UserIdSz) LUserId
Integer *4 LUserIdLg
Integer *2 WJPIItmLst(12)
Integer *4 JPIItmLst(6)
Equivalence (JPIItmLst,WJPIItmLst)
C Begin GetUserName
WJPIItmLst(1) = UserIdSz
WJPIItmLst(2) = JPI$_UserName
JPIItmLst(2) = %Loc(LUserId)
JPIItmLst(3) = %Loc(LUserIdLg)
JPIItmLst(4) = 0
GetUserName = Sys$GetJPI (,,,JPIItmLst,,,)
If (GetUserName) Then
UserId = LUserId(1:LUserIdLg)
EndIf
Return
End ! GetUserName
******** install.com ********
$Run Sys$system:Install
NewsDir:News/Delete
NewsDir:News/Open/Shared/Header/Priv=Bypass
$
$Run Sys$System:Install
NewsDir:News/Full
$
******** linknews.com ********
$If P1 .nes. "" Then P1 = "/" + P1
$Link'P1 news.obj,smg_routines,user_open,Up_Dn_Priv,-
twg$tcp:[netdist.lib]libnet.olb/lib,-
twg$tcp:[netdist.lib]libnetacc.olb/lib,-
twg$tcp:[netdist.lib]libnet.olb/lib,-
twg$tcp:[netdist.lib]libnetacc.olb/lib,-
twg$tcp:[netdist.lib]libnet.olb/lib,-
twg$tcp:[netdist.lib]libnetacc.olb/lib,-
twg$tcp:[netdist.lib]libnet.olb/lib,-
twg$tcp:[netdist.lib]libnetacc.olb/lib,-
twg$tcp:[netdist.lib]libnet.olb/lib,-
twg$tcp:[netdist.lib]libnetacc.olb/lib,-
twg$tcp:[netdist.lib]libnet.olb/lib,-
twg$tcp:[netdist.lib]libnetacc.olb/lib,-
twg$tcp:[netdist.lib]libnet.olb/lib
$
******** make.com ********
$If P1 .nes. "" Then P1 = "/" + P1
$For'P1 news
$For'P1 getusername
$For'P1 smg_routines
$For'P1 up_dn_priv
$For'P1 user_open
$
******** news.cld ********
Define Verb News
Image "NewsDir:News"
Qualifier Header Value(List)
Qualifier Mark Value(Type=$Rest_Of_Line)
******** news.def ********
Implicit None
C VMS Constants
Parameter UserIdSz = 31 ! Length of a username
C Define incore group control structure
Structure /GroupDef/
Character *32 Name ! Name
Logical Subscribed ! User has subscribed
Logical Newsrc_File ! Found in Newsrc file
Logical Active_File ! Found in active file
Integer *4 Active_Start ! Active file start
Integer *4 Active_End ! Active file end
Logical Active_Post ! Active file post flag
Integer *2 Range_First ! Newsrc range list start
Integer *2 Range_Last ! Newsrc range list end
End Structure ! GroupDef
C Define group article range structure
Structure /RangeDef/
Integer *2 Next
Integer *2 Start
Integer *2 End
End Structure ! RangeDef
C Define Socket Structure
Structure /Socket_IN_Def/
Integer *2 SIN_Family
Integer *2 SIN_Port
Integer *4 SIN_Address
Byte SIN_Fill(8)
End Structure ! Socket_IN_Def
C Define news parameters
Parameter LU_Newsrc = 10
Parameter LU_Save = 11
Parameter LU_EditIn = 12
Parameter LU_EditOut = 13
Parameter LU_Init = 14
Parameter LU_VMSMail = 15
Parameter LU_Signature = 16
Parameter File_Name_Size = 128
Parameter Mx_Range = 10000
Parameter Mx_Group = 1000
C Define News common block
Integer *4 Group_Count
Record /GroupDef/ Group(Mx_Group)
Integer *4 Range_Count
Record /RangeDef/ Range(Mx_Range)
Integer *4 Range_Free_List
Integer *4 Distribution_Count
Character *16 Distribution(50)
Character *31 UserName
Character *64 UserDirectory
Character *128 UserPersonalName
Character *64 UserMailDirectory
Character *64 SiteId
Character *64 Organization
Byte Server_IP_Number(4)
Logical Newsrc_Is_Open ! .true. if user had a
! xx.newsrc file
Character *20 Newsrc_CDT_VMS ! xx.newsrc creation date time
! 'dd-mmm-yyyy hh:mm:ss'
Character *13 Newsrc_CDT_News ! xx.newsrc creation date time
! 'ddmmyy hhmmss'
Character *16 Header(16) ! /Header = fields
Integer *4 Header_Count ! Number of /header fields
Logical Header_Present ! .true. if /header present
Character Mark_Character ! Followup mark character
! .true. if logical name
! MAIL$EDIT defined
Logical Mail_Cmd_Mail$Edit
Logical Rotated ! .true. if article last seen
! in rotated mode
Logical Debug ! .true. if debug mode turned
! on by J command
Logical FirstFlag ! Used to skip multiple
! copies of subject line
! in Cmd_ArticleList
Common /News/
$ Group_Count, Group,
$ Range_Count, Range, Range_Free_List,
$ Distribution_Count, Distribution,
$ UserName, UserDirectory, UserPersonalName, UserMailDirectory,
$ SiteId, Organization,
$ Server_IP_Number,
$ Newsrc_CDT_VMS, Newsrc_CDT_News, Newsrc_Is_Open,
$ Header_Present, Header_Count, Header,
$ Mark_Character, Mail_Cmd_Mail$Edit, Rotated, Debug, FirstFlag
C WIN TCP/IP Services
Integer Connect
External Connect
Integer HtoNS
External HtoNS
Integer Recv
External Recv
Integer Send
External Send
Integer Socket
External Socket
C WIN TCP/IP Constants
Parameter AF_INet = 2
Parameter Sock_Stream = 1
Parameter Sock_DGram = 2
C TCP/IP Common Definitions
Integer *4 Channel
Character *512 Recv_Buf
Integer *4 Recv_BufE
Integer *4 Recv_BufS
Common /Server/ Channel, Recv_Buf, Recv_BufS, Recv_BufE
******** news.doc ********
News
1.0 Introduction
News is a native VMS news reading client that uses the network news
transport protocol to access news stored on a remote server. It
was written by Michael Dorl at the Madison Academic Computing
Center, University of Wisconsin as a personal project to gain
familiarity with The Wollongong WIN/VX program interface to
the TCP/IP network.
Much of the look and feel of the user interface was shamelessly
borrowed from the many fine news reading programs in the Unix
world.
2.0 User Documentation
News works best on screen oriented terminal. It uses the DEC
SMG routines and supports all of the standard VMS terminal types.
The best way to learn about news is to try it. But before you
do that, a few words on converting from your current Eunice rrn
news environment to your new news environment.
2.1 Converting from Eunice rrn to news
News keeps your news database in file sys$login:xx.newsrc.
This file has the same format as a rrn .newsrc file
except that its in RMS format rather than in Eunice stream format.
To make a xx.newsrc:
$ Set default sys$login
$ Copy .newsrc xx.newsrc
$ Unixtovms xx.newsrc
News keeps your saved articles in directory sys$login:news.dir
instead of the Eunice rrn sys$login:$n$ews.dir directory.
Create this directory and copy your saved news articles there
with the following commands:
$ Set default sys$login
$ Create/Dir [.news]
$ Copy [.$n$ews]*.*;* [.news]*.*;*
$ Set default [.news]
$ Unixtovms *.*;*
The files in your [.news] directory still have the old Eunice
names. These include $ characters used as shift markers and
also $5 pairs used as second and subsequent . characters. For
example if you have been saving your comp.os.vms articles in the
default file, you will have file $c$omp.os$5vms. Since the news
equivalent of this file is comp.os_vms, you may want to
rename files in your .news directory to the news equivalents.
No set of wild card cards will do this for you but there is a
command procedure which you can run to convert these names.
$ Set default sys$login
$ Set default [.news]
$ @newsdir:convert
Once you satisfy yourself that news works and want to switch from
Eunice and rrn, don't forget to go back and delete the files in
your sys$login:$n$ews.dir and that directory.
2.2 First Time News Users
If you are a first time news user, you need take no special action.
The best thing to do is to dive right in. News will notice that you have
no xx.newsrc file and assumme you want to look at all groups. Run news
and start reading. If a group looks like you are not interested in it,
unsubscribe by typing a u command. Try not to be offended by anything you
see, the news system serves a diverse set of users. Groups that might
offend the faint of heart include ???, ???, and ???. You might want
to unsubscribe to these without reading anything in them.
2.3 News Operation
To run news, type:
$ News
News starts up and performs a series of initialization operations.
Since some of these might take a while, news tells you what's going
on:
Reading your XX.Newsrc file...
Connecting to remote news server...
200 dogie.macc.wisc.edu NNTP server version 1.5 GAMMA (6 feb 88) ready
at Tue Apr 26 21:47:46 1988 (posting ok).
Retrieving active file...
Last News execution at 25 Apr 1988 21:12:13
News now goes through the news groups you have subscribed to prompting
you for an action for each one. For example:
Group comp.os.vms available 242 - 396 unread: 1
Group comp.os.vms action (<CR> b c d g h p n q u):
Legal actions at the group prompt include:
<CR> Start reading
b Go back to the previous group
c catchup, mark all articles as read
c # catchup through and including article #
d directory of unread articles
d/g directory of groups
d/g pattern directory of groups matching pattern. Pattern may
include * match any number of any character or $
match any single character
p post an article
p/x post a rotated article
g group go to group 'group'
h help
n next group
q quit
u unsubscribe from this group
# display article #
^ first unread article
$ last unread article
If you answer with a <CR>, news will cycle through the unread articles
in this group. After displaying each article, news will prompt you for
an action.
Group comp.os.vms available: 242 - 396 unread: 0
Article 396
From: jackjones@vms3.macc.wisc.edu
Subject: Has anyone ever figured out how logical names work?
...
Action (<CR> b c d f p g h k m n q r s u x):
Legal actions at the article prompt include:
<CR> Next article
b Go back to the previous article
c catchup, mark all articles as read
c # catchup through and including article #
d directory of unread articles
d/g directory of groups
d/g pattern directory of groups matching pattern. Pattern may
include * match any number of any character or $
match any single character
f post a follow up article
p post an article
p/x post a rotated article
g group go to group 'group'
h help
k kill, mark as read, articles with this subject
m mark this article unread
n next group
q quit
r refresh this article
s save this article
u unsubscribe from this group
x refresh this article in rotate 13 mode.
z next article with the same subject
# display article #
^ first unread article
$ last unread article
When news has processed all groups, it displays a end of groups
prompt:
End groups, action (q p g):
Legal actions at the end of groups prompt include:
<CR> exit from news
b Go back to the previous group
d/g directory of groups
d/g pattern directory of groups matching pattern. Pattern may
include * match any number of any character or $
match any single character
p post an article
p/x post a rotated article
g group go to group 'group'
h help
q quit
When you answer <CR> or q, news writes an updated xx.newsrc to your
home directory and exits.
Closing News server connection...
205 dogie.macc.wisc.edu closing connection. Goodbye.
Updating XX.Newsrc
3.0 Customizing News
News accepts the following qualifiers:
/Header=(list)
/Mark
Header selects which header lines you wish to see. Since most articles
contain 10 - 15 header lines most of which you have no interest in,
you probably want to select only the few lines you need. The following
works well for most folks:
/Header=(Subject, From, Sender)
Mark allows you to select the character to be used to 'mark' the original
text in followup articles. Its mainly used to defeat certain smart
news software attempting to limit the amount of followup text posted
to the net.
A good way to customize news is to include a line such as:
news = "news/header=(from,subject,sender)/mark=""{"""
in your login.com file.
News uses the editor specified by MAIL$EDIT when composing posted
or followup articles. It also appends the signature.mai file
from your VMS mail directory to articles you post.
4.0 Helpfull Hints
One of the most productive ways to use news is to print a directory
of unread articles in the group with the d command. Look at these
and decide which ones you want to read. Read these by specifying their
numbers or by using the z command to follow all articles with the
same subject. When you have seen all you want, answer c to skip
the remainder in the group.
An alternative is to do a c # to catchup to all articles before the
one you wish to read, read that one with a <CR>, do another d to get
a refreshed list, and repeat until done.
5.0 Netiquette
******** news.for ********
Include 'News.Def/list'
Include 'SMG.Def/list'
C External Routines
Integer CLI$Get_Value
Integer CLI$Present
Integer Close_Newsrc
Logical Cmd_ArticleCatchUp
Logical Cmd_ArticleFirst
Logical Cmd_ArticleLast
Integer Cmd_ArticleNumber
Integer Cmd_ArticleSameSubj
Integer GetUserName
Integer GetUserDirectory
Integer GetInteger
Integer Group_Find
Integer Open_Newsrc
Integer Range_Find
Integer Range_Allocate
Logical SMG_More_Print
Integer SMG_Prompt
Integer Srv_Cmd
Integer Srv_Connect
Integer Srv_NetClose
Integer Srv_RdTxt
Integer Srv_Recv
Integer Srv_Send
Integer TransLog
Integer TrimLg
Integer UnRead
C Local Definitions
Logical A_Continue
Logical Action
Logical Any
Character *512 Buf
Logical ByReadChk
Logical BySubscribedChk
Integer *4 C
Character *1 Ch
Character *2 CRLF
Logical Done
Integer *4 E
Integer *4 G
Logical G_Continue
Integer *4 Next_G
Integer *4 I
Character *512 Image
Integer *4 Lg
Integer *4 Lg2
Integer *4 Lg3
Integer *4 N
Character *8 Num
Character *8 Num2
Character *8 Num3
Logical Ok
Integer *4 R
Logical RefreshArticle
Logical RefreshGroup
Integer *4 RS, RE, RU
Integer *4 S
Integer *4 Status
Integer *4 U
Integer *4 Unavail_Start
Integer *4 Unavail_End
Integer *4 X
C Begin News
Debug = .false. ! Debug is off
Range_Free_List = 0 ! Initialize Range item free list
Group_Count = 0 ! Initialize next group
Range_Count = 0 ! Initialize next range
Header_Count = 0 ! Number of /Header fields
Call SetUpPriv ! Initialize privileges
C Determine if logical name MAIL$EDIT is defined
If (TransLog('MAIL$EDIT',Buf,0)) Then
Mail_Cmd_Mail$Edit = .true.
Else
Mail_Cmd_Mail$Edit = .false.
EndIf
C Get our user name
Status = GetUserName (UserName)
If (.not. Status) Then
Call SMG_All_Print ('Error obtaining your username', '|')
Stop
EndIf
C Get our .news directory
Status = GetUserDirectory (UserDirectory)
If (.not. Status) Then
Call SMG_All_Print
$ ('Error obtaining your login directory', '|')
Stop
EndIf
Call Get_Mail_Control ! Get Mail directory and
! personal name
Call Ctrl_C ! Initialize control C handler
Call SMG_Initialize ! Initialize screen routines
Call SMG_All_Print ! Welcome user to news
$ ('News', '|')
Call SMG_All_Print (' ', '|')
Call Read_Init ! Read News configuration file
CRLF(1:1) = Char (13)
CRLF(2:2) = Char (10)
C Get /Header parameter telling us what header lines to print
Status = 1
Do While (Status)
Header_Count = Header_Count + 1
Status = CLI$Get_Value ('HEADER', Header(Header_Count))
EndDo
Header_Count = Header_Count - 1
Header_Present = Cli$Present ('HEADER')
C Get followup command mark character
Status = CLI$Get_Value ('MARK', Mark_Character)
If (.not. Status) Then
Mark_Character = '>'
EndIf
C Read users XX.NEWSRC file
Call SMG_All_Print ('Reading your XX.Newsrc file...', '|')
Call SMG_All_Print (' ', '|')
Status = Open_Newsrc()
If (.not. Status) Then
Write (Buf, '(A,I)')
$ ' Can''t open xx.newsrc, status = ', Status
Call SMG_All_Print (Buf, '|')
EndIf
C Connect to server
Call SMG_All_Print
$ ('Connecting to remote news server...', '|')
Status = Srv_Connect()
If (.not. Status) Then
Call SMG_All_Print
$ (
$ ' Can not connect to remote news server!',
$ '|'
$ )
Stop
EndIf
C Get initial signon line
Status = Srv_Recv (Buf, Lg)
If (.not. Status) Then
Write (Buf, '(A,I)')
$ ' Error receiving data from news server, status = ',
$ Status
Call SMG_All_Print (Buf, '|')
Stop
EndIf
If (Lg .gt. 0) Then
Call SMG_All_Print (Buf (1:Lg), '|')
EndIf
Call SMG_All_Print (' ', '|')
C Send a list command
Call SMG_All_Print ('Retrieving active file...', '|')
If (.not. Srv_Cmd('list',Buf,Lg)) Then
Stop 'Server failed'
EndIf
If (Buf(1:3) .ne. '215') Then
Call SMG_All_Print
$ (
$ ' Unexpected LIST command result, ' // Buf(1:Lg),
$ '|'
$ )
Stop
EndIf
C Retrieve list output and update group
Status = Srv_Recv (Buf, Lg)
Do While (Status .and. (Buf(1:Lg) .ne. '.'))
I = Index (Buf(1:Lg), ' ')
If (I .eq. 0) Then
Stop 'Bad active file entry'
EndIf
X = 1
Do While
$ (
$ (X .le. Group_Count)
$ .and.
$ (Group(X).Name .ne. Buf(1:I))
$ )
X = X + 1
EndDo
If (X .gt. Group_Count) Then
Group_Count = X
Group(X).Name = Buf(1:I)
Group(X).Active_File = .true.
Group(X).Newsrc_File = .false.
Group(X).Subscribed = .false.
Group(X).Range_First = 0
Group(X).Range_Last = 0
End If
C If user had no XX.Newsrc file, assumme he is subscribed to this group
If (.not. Newsrc_Is_Open) Then
Group(X).Subscribed = .true.
EndIf
C Now get last first and p flags
Group(X).Active_End = GetInteger (Buf, I, Lg)
Group(X).Active_Start = GetInteger (Buf, I, Lg)
Call GetField (C, Buf, I, Lg)
If ((C .eq. 'y') .or. (C .eq. 'Y')) Then
Group(X).Active_Post = .true.
Else
Group(X).Active_File = .true.
EndIf
Status = Srv_Recv (Buf, Lg)
End Do
C Now get list of groups created since XX.NEWSRC created
If (Newsrc_Is_Open) Then
Call SMG_All_Print (' ', '|')
Call SMG_All_Print
$ (
$ 'Last News execution at ' // Newsrc_CDT_VMS // '.',
$ '|'
$ )
Call SMG_All_Print (' ', '|')
Call SMG_All_Print ('Retrieving new groups...', '|')
Buf = 'newgroups ' // Newsrc_CDT_News
If (.not. Srv_Cmd(Buf(1:23),Buf,Lg)) Then
Stop 'Server failed'
EndIf
If (Buf(1:3) .ne. '231') Then
Call SMG_All_Print
$ (
$ ' Unexpected LIST command result, ' // Buf(1:Lg),
$ '|'
$ )
Stop
EndIf
C Retrieve newgroups output and update group
Any = .false.
Status = Srv_Recv (Buf, Lg)
Do While (Status .and. (Buf(1:Lg) .ne. '.'))
If (Buf(1:Lg) .ne. '.') Then
G = Group_Find (Buf(1:Lg))
If (G .ne. 0) Then
Any = .true.
Group(G).Subscribed = .true.
Call SMG_All_Print ('New group ' // Buf(1:Lg), '|')
EndIf
EndIf
Status = Srv_Recv (Buf, Lg)
EndDo
C If any new groups found, give user a chance to see them
If (Any) Then
Call SMG_Prompt (Buf, 'Type <return> to continue',Lg)
EndIf
EndIf
C Scan through groups displaying new messages
G = 1
G_Continue = .true.
BySubscribedChk = .false.
More_Input = ' '
Done = .false.
1 Do While (G_Continue .and. (G .le. Group_Count))
Next_G = G + 1
If (BySubscribedChk .or. (Group(G).Subscribed)) Then
Call CacheHdr_Init (G) ! Initialize header cache
S = Group(G).Active_Start ! Starting article
E = Group(G).Active_End ! Ending article
R = Group(G).Range_First
If (S .eq. 0) Then
S = 1
End If
C Adjust S depending on articles we have read to avoid some overhead
Do While (R .ne. 0)
C Trim range S:E if S is in this range
If
$ (
$ (S .ge. Range(R).Start)
$ .and.
$ (S .le. Range(R).End)
$ )
$ Then
S = Range(R).End + 1
EndIf
C Do next range
R = Range(R).Next
End Do
U = UnRead (G)
C If there is something to read, open the group
If (BySubscribedChk .or. (S .le. E)) Then
BySubscribedChk = .false.
C Attempt to select group
If
$ (.not. Srv_Cmd ('group ' // Group(G).Name, Buf, Lg))
$ Then
Stop 'Server failed'
End If
If (Buf(1:3) .eq. '211') Then
C Good status indicates group selected
A_Continue = .true.
C Find out what user wants with this group
ByReadChk = .false.
Action = .false.
RefreshGroup = .true.
Do While (.not. Action)
If (RefreshGroup) Then
RefreshGroup = .false.
Call ItoS (Group(G).Active_Start, Num, Lg)
Call ItoS (Group(G).Active_End, Num2, Lg2)
Call ItoS (U, Num3, Lg3)
More_Hdg_One = 'Group ' // Group(G).Name //
$ ' available: ' // Num(1:Lg) //
$ ' - ' // Num2(1:Lg2) //
$ ' unread: ' // Num3(1:Lg3)
More_Hdg_Two = '@'
Call More_Heading
EndIf
If (More_Input .ne. ' ') Then
Buf = More_Input
Lg = TrimLg(More_Input)
More_Input = ' '
Else
Status = SMG_Prompt
$ (
$ Buf,
$ 'Group ' //
$ Group(G).Name(1:TrimLg(Group(G).Name)) //
$ ' action (<CR> b c d g h n p q u): ',
$ Lg
$ )
EndIf
Action = .true.
If (Buf(1:1) .eq. ' ') Then ! Process group
Else If (Buf(1:1) .eq. 'b') Then ! Backup
Next_G = G - 1
BySubscribedChk = .true.
A_Continue = .false.
Else If (Buf(1:1) .eq. 'c') Then ! Catchup
Action = Cmd_ArticleCatchUp (G, S, Buf(1:Lg), U)
RefreshGroup = .true. ! Need new heading
Next_G = G + 1
Else If (Buf(1:3) .eq. 'd/g') Then ! Dir/Group
Call Cmd_GroupList (Buf(1:Lg))
Action = .false.
Else If (Buf(1:1) .eq. 'd') Then ! Dir
Call Cmd_ArticleList (G, Buf(1:Lg))
Action = .false.
Else If (Buf(1:1) .eq. 'f') Then ! Followup
Call Cmd_ArticleNone
Action = .false.
Else If (Buf(1:1) .eq. 'p') Then ! Post
Call Cmd_ArticlePost (G, Buf(1:Lg))
Action = .false.
Else If (Buf(1:1) .eq. 'g') Then ! Group g
I = Group_Find (Buf(3:34))
If (I .eq. 0) Then
Action = .false.
Call SMG_All_Print
$ (
$ ' No such group as ' //
$ Buf (3:2+TrimLg(Buf(3:34))),
$ '|'
$ )
Else
BySubscribedChk = .true.
Next_G = I
A_Continue = .false.
Group(G).Subscribed = .true.
End If
Else If (Buf(1:1) .eq. 'h') Then ! Help
Call Cmd_Help
Action = .false.
Else If (Buf(1:1) .eq. 'k') Then ! Kill
Call Cmd_ArticleNone
Action = .false.
Else If (Buf(1:1) .eq. 'm') Then ! Mark unread
Call Cmd_ArticleNone
Action = .false.
Else If (Buf(1:1) .eq. 'n') Then ! Next group
A_Continue = .false.
Else If (Buf(1:1) .eq. 'q') Then ! Quit
G_Continue = .false.
A_Continue = .false.
Else If (Buf(1:1) .eq. 'r') Then ! Refresh
Call Cmd_ArticleNone
Action = .false.
Else If (Buf(1:1) .eq. 's') Then ! Save
Call Cmd_ArticleNone
Action = .false.
Else If (Buf(1:1) .eq. 'u') Then ! Unsubscribe
Group(G).Subscribed = .false.
A_Continue = .false.
Else If (Buf(1:1) .eq. 'x') Then ! Refresh rot mode
Call Cmd_ArticleNone
Action = .false.
Else If (Buf(1:1) .eq. 'z') Then ! Next article
Call Cmd_ArticleNone ! same subject
Action = .false.
Else If (Buf(1:1) .eq. '^') Then ! First unread article
Action = Cmd_ArticleFirst (G,S)
Else If (Buf(1:1) .eq. '$') Then ! Last unread article
Action = Cmd_ArticleLast (G,S)
Else If
$ (
$ (Buf(1:1) .ge. '0')
$ .and.
$ (Buf(1:1) .le. '9')
$ )
$ Then
If (Cmd_ArticleNumber (G, Buf(1:Lg), S)) Then
ByReadChk = .true.
Else
Action = .false.
EndIf
Else If (Buf(1:1) .eq. 'j') Then
Action = .false.
Debug = .not. Debug
If (Debug) Then
Call SMG_All_Print ('Debug is on', '|')
Else
Call SMG_All_Print ('Debug is off', '|')
EndIf
Else
Call SMG_All_Print ('Huh?', '|')
Action = .false.
End If
End Do
C Now page through the articles in range S - E
Unavail_Start = 0 ! No unavailable articles
Unavail_End = 0 ! so far
Do While ((E .gt. 0 ) .and. (S .le. E) .and. A_Continue)
If
$ (
$ ByReadChk
$ .or.
$ (Range_Find(G, S, .false.) .eq. 0)
$ )
$ Then
ByReadChk = .false.
Write (Buf, '(A, I7)') 'stat ', S
If (.not. Srv_Cmd (Buf(1:15), Buf, Lg)) Then
Stop 'Server failed'
End If
If (Buf(1:3) .ne. '223') Then
If (Unavail_Start .eq. 0) Then
Unavail_Start = S
End If
Unavail_End = S
Status = Range_Find (G, S, .true., U)
EndIf
If
$ (
$ ((Unavail_Start .ne. 0) .and. (S .eq. E))
$ .or.
$ (Buf(1:3) .eq. '223')
$ )
$ Then
More_Input = ' '
If (Unavail_Start .ne. 0) Then
S = Unavail_End
c If (Buf(1:3) .eq. '220') Then
c Call Srv_RdTxt
c $ (.false., .false., %Val(0)) ! Skip article
c EndIf
Call Unavail_Print (Unavail_Start, Unavail_End)
Else
Status = Range_Find (G, S, .true., U)
Call ItoS (Group(G).Active_Start, Num, Lg)
Call ItoS (Group(G).Active_End, Num2, Lg2)
Call ItoS (U, Num3, Lg3)
More_Hdg_One = 'Group ' // Group(G).Name //
$ ' available: ' // Num(1:Lg) //
$ ' - ' // Num2(1:Lg2) //
$ ' unread: ' // Num3(1:Lg3)
Call ItoS (S, Num, Lg)
More_Hdg_Two = 'article ' // Num
Rotated = .false.
Call Cmd_ArticleDisplay (G, S, Rotated)
EndIf
C What does user want to do with this article?
Action = .false.
RefreshArticle = .false.
Do While (.not. Action)
Call ItoS (Group(G).Active_Start, Num, Lg)
Call ItoS (Group(G).Active_End, Num2, Lg2)
Call ItoS (U, Num3, Lg3)
More_Hdg_One = 'Group ' // Group(G).Name //
$ ' available: ' // Num(1:Lg) //
$ ' - ' // Num2(1:Lg2) //
$ ' unread: ' // Num3(1:Lg3)
Call ItoS (S, Num, Lg)
More_Hdg_Two = 'article ' // Num
If (RefreshArticle) Then
RefreshArticle = .false.
Call More_Heading
EndIf
Call ItoS (S, Num, Lg)
If (More_Input .ne. ' ') Then
Buf = More_Input
Lg = TrimLg (More_Input)
More_Input = ' '
Else
Call SMG_Print (' ','|')
Status = SMG_Prompt
$ (
$ Buf,
$ 'End article ' // Num(1:Lg) //
$ ' action (<CR> ' //
$ 'b c d f g h k m n p q r s u x z): ',
$ Lg
$ )
EndIf
Action = .true.
If (Buf(1:1) .eq. ' ') Then ! Next article
S = S + 1
Else If (Buf(1:1) .eq. 'b') Then ! Backup
S = S - 1
ByReadChk = .true.
Else If (Buf(1:1) .eq. 'c') Then ! Catchup
Action = Cmd_ArticleCatchUp(G, S, Buf(1:Lg), U)
RefreshArticle = .true.
Else If (Buf(1:3) .eq. 'd/g') Then ! Dir/Group
Call Cmd_GroupList (Buf(1:Lg))
Action = .false.
Else If (Buf(1:1) .eq. 'd') Then ! Dir
Call Cmd_ArticleList (G, Buf(1:Lg))
Action = .false.
Else If (Buf(1:1) .eq. 'f') Then ! Followup
Call Cmd_ArticleFollowUp (G, S, Buf(1:Lg))
Action = .false.
Else If (Buf(1:1) .eq. 'p') Then ! Post
Call Cmd_ArticlePost (G, Buf(1:Lg))
Action = .false.
Else If (Buf(1:1) .eq. 'g') Then ! Group g
I = Group_Find (Buf(3:34))
If (I .eq. 0) Then
Action = .false.
Call SMG_All_Print
$ (
$ 'No such group as ' //
$ Buf(2:2+TrimLg(Buf(3:34))),
$ '|'
$ )
Else
BySubscribedChk = .true.
Next_G = I
A_Continue = .false.
Group(G).Subscribed = .true.
End If
Else If (Buf(1:1) .eq. 'h') Then ! Help
Call Cmd_Help
Action = .false.
Else If (Buf(1:1) .eq. 'k') Then ! Kill
Call Cmd_ArticleKill (G, S, U)
RefreshArticle = .true.
Action = .false.
Else If (Buf(1:1) .eq. 'm') Then ! Mark unread
Call Cmd_ArticleMark (G, S, U)
RefreshArticle = .true.
Action = .false.
Else If (Buf(1:1) .eq. 'n') Then ! Next article
S = S + 1
Else If (Buf(1:1) .eq. 'q') Then ! Quit
A_Continue = .false.
Else If (Buf(1:1) .eq. 'r') Then ! Refresh
ByReadChk = .true.
Else If (Buf(1:1) .eq. 's') Then ! Save
Call Cmd_ArticleSave (G,S,Buf)
Action = .false.
Else If (Buf(1:1) .eq. 'u') Then ! Unsubscribe
Group(G).Subscribed = .false.
A_Continue = .false.
Else If (Buf(1:1) .eq. 'x') Then ! Refresh rot mode
Rotated = .true.
Call Cmd_ArticleDisplay (G, S, Rotated)
Action = .false.
Else If (Buf(1:1) .eq. 'z') Then ! Next article
! same subject
Action = Cmd_ArticleSameSubj (G, S)
Else If (Buf(1:1) .eq. '^') Then ! First unread article
Action = Cmd_ArticleFirst (G, S)
Else If (Buf(1:1) .eq. '$') Then ! Last unread article
Action = Cmd_ArticleLast (G,S)
Else If ! Article number
$ (
$ (Buf(1:1) .ge. '0')
$ .and.
$ (Buf(1:1) .le. '9')
$ )
$ Then
If (Cmd_ArticleNumber (G, Buf(1:Lg), S)) Then
ByReadChk = .true.
Else
Action = .false.
EndIf
Else If (Buf(1:1) .eq. 'j') Then
Action = .false.
Debug = .not. Debug
If (Debug) Then
Call SMG_All_Print ('Debug is on', '|')
Else
Call SMG_All_Print ('Debug is off', '|')
EndIf
Else
Call SMG_All_Print ('Huh?', '|')
Action = .false.
End If
End Do ! (.not. Action)
Else
S = S + 1
End If
Else
S = S + 1
End If
End Do ! (S .le. E)
If (Unavail_Start .ne. 0) Then
Call Unavail_Print (Unavail_Start, Unavail_End)
EndIf
Else If (Buf(1:3) .eq. '411') Then
Call SMG_All_Print
$ (
$ 'No such group as ' // Group(G).Name,
$ '|'
$ )
Call SMG_All_Print (Buf(1:Lg), '|')
Else
Call SMG_All_Print (Buf(1:Lg), '|')
Stop 'Unexpected GROUP command response '
End If
End If
End If
G = Next_G
End Do ! (G_Continue .and. (G .le. Group_Count))
C Done with news groups, find out what user want's to do now
More_Input = ' '
Action = .false.
Do While (.not. Action)
If (More_Input .ne. ' ') Then
Buf = More_Input
Lg = TrimLg (More_Input)
More_Input = ' '
Else
Status = SMG_Prompt
$ (
$ Buf,
$ 'End groups, action (<CR> h p q g): ',
$ Lg
$ )
EndIf
Action = .true.
If (Buf(1:1) .eq. ' ') Then ! Process group
Done = .true.
Else If (Buf(1:1) .eq. 'b') Then ! Backup
G = G - 1
Else If (Buf(1:1) .eq. 'c') Then ! Catchup
Call Cmd_GroupNone
Action = .false.
Else If (Buf(1:3) .eq. 'd/g') Then ! Dir/Group
Call Cmd_GroupList (Buf(1:Lg))
Action = .false.
Else If (Buf(1:1) .eq. 'd') Then ! Dir
Call Cmd_GroupNone
Action = .false.
Else If (Buf(1:1) .eq. 'f') Then ! Followup
Call Cmd_ArticleNone
Action = .false.
Else If (Buf(1:1) .eq. 'p') Then ! Post
Call Cmd_ArticlePost (G, Buf(1:Lg))
Action = .false.
Else If (Buf(1:1) .eq. 'g') Then ! Group g
I = Group_Find (Buf(3:34))
If (I .eq. 0) Then
Action = .false.
Call SMG_All_Print
$ (
$ ' No such group as ' //
$ Buf (3:2+TrimLg(Buf(3:34))),
$ '|'
$ )
Else
BySubscribedChk = .true.
G = I
A_Continue = .false.
Group(G).Subscribed = .true.
End If
Else If (Buf(1:1) .eq. 'h') Then ! Help
Call Cmd_Help
Action = .false.
Else If (Buf(1:1) .eq. 'k') Then ! Kill
Call Cmd_ArticleNone
Action = .false.
Else If (Buf(1:1) .eq. 'm') Then ! Mark unread
Call Cmd_ArticleNone
Action = .false.
Else If (Buf(1:1) .eq. 'n') Then ! Next group
Done = .true.
Else If (Buf(1:1) .eq. 'q') Then ! Quit
Done = .true.
Else If (Buf(1:1) .eq. 'r') Then ! Refresh
Call Cmd_ArticleNone
Action = .false.
Else If (Buf(1:1) .eq. 's') Then ! Save
Call Cmd_ArticleNone
Action = .false.
Else If (Buf(1:1) .eq. 'u') Then ! Unsubscribe
Call Cmd_GroupNone
Action = .false.
Else If (Buf(1:1) .eq. 'x') Then ! Refresh rot mode
Call Cmd_ArticleNone
Action = .false.
Else If (Buf(1:1) .eq. 'z') Then ! Next article
Call Cmd_ArticleNone ! same subject
Action = .false.
Else If (Buf(1:1) .eq. '^') Then ! First unread article
Call Cmd_ArticleNone ! same subject
Action = .false.
Else If (Buf(1:1) .eq. '$') Then ! Last unread article
%% end part a
Michael Dorl (608) 262-0466
dorl@vms.macc.wisc.edu
dorl@wiscmacc.bitnet