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