dorl@vms.macc.wisc.edu (Michael Dorl - MACC) (07/29/88)
	If (L .ge. Group(G).Active_End) Then
	  Cmd_ArticleCatchUp = .true.
	Else
	  Cmd_ArticleCatchUp = .false.
	EndIf
C Find the first range beyond L
	R = Group(G).Range_First
	X = 0
	Do While ((R .ne. 0) .and. (L .ge. Range(R).End))
	  X = R
	  R = Range(R).Next	    
	EndDo
C R is the Range following L or zero, X is the Range in front of
C L or zero.
C Release all ranges from Group(G).Range_First - X.  These ranges
C contain articles less than L.
	If (X .ne. 0) Then
	  Range(X).Next = 0
	  X = Group(G).Range_First
	  Call Range_Deallocate (X)
	  Group(G).Range_First = R
	EndIf
C Several cases can arise.
C
C   R = 0			All ranges have been released, get
C				a new one and set it to 1-L
C
C   L < R.Start			Add a new range to the
C				Group from 1-L
C
C   R.Start <= L < R.End        Set R.Start = 1
C
C   L >= R.End			Impossible
	If (R .eq. 0) Then
	  R = Range_Allocate ()
	  Range(R).Start = 1
	  Range(R).End   = L
	  Range(R).Next  = 0
	  Group(G).Range_First = R
	  Group(G).Range_Last = R
	Else If (L .lt. Range(R).Start) Then
	  X = Range_Allocate ()
	  Group(G).Range_First = X
	  Range(X).Start = 1
	  Range(X).End = L
	  Range(X).Next = R
	  Call Range_Combine (X, R)
	Else If
     $    ((L .ge. Range(R).Start) .and. (L .lt. Range(R).End)) Then
	  Range(R).Start = 1
	Else 
	  Stop 'Catchup error'
	EndIf
	A = L
	U = UnRead (G)			! Update unread mail count
	Return
	End ! Cmd_ArticleCatchUp
	Subroutine Cmd_ArticleDisplay (G, A, Rotate_Flag)
	Include 'News.Def'
C Parameter definitions
	Integer   *4	G	! Group number
	Integer   *4	A	! Article number
	Logical		Rotate_Flag
C External routines
	Integer		Srv_Cmd
	External 	Header_Check
C Local definitions
	Character *128	Cmd
	Integer   *4	Lg
	Integer   *4	Status
	Character *8	Num
C Begin Cmd_ArticleDisplay
	Call More_Heading
	Write (Cmd, '(A, I6)') 'head ', A
	Status = Srv_Cmd (Cmd(1:11), Cmd, Lg)
	If (.not. Status) Then
	  Call SMG_All_Print ('Server failed', '|')
	  Stop
	EndIf
	If (Cmd(1:3) .eq. '221') Then
	  Call Srv_RdTxt (.true., .false., Header_Check)
	Else
	  Call SMG_All_Print ('Unexpected server response', '|')
	  Call SMG_All_Print (Cmd(1:Lg), '|')
	  Stop
	EndIf
	Call SMG_All_Print (' ', '|')
	Write (Cmd, '(A, I6)') 'body ', A
	Status = Srv_Cmd (Cmd(1:11), Cmd, Lg)
	If (.not. Status) Then
	  Call SMG_All_Print ('Server failed', '|')
	  Stop
	EndIf
	If (Cmd(1:3) .eq. '222') Then
	  Call Srv_RdTxt (.true., Rotate_Flag, %Val(0))
	Else
	  Call SMG_All_Print ('Unexpected server response', '|')
	  Call SMG_All_Print (Cmd(1:Lg), '|')
	  Stop
	EndIf
	End ! Cmd_ArticleDisplay
	Integer Function Header_Check (Img)
	Include 'News.Def'
C Parameter definition
	Character *(*)	Img
C Local definitions
	Integer   *4	X
	Character *16	Fld
C Begin Header_Check
	Header_Check = .true.
	If ((Img .eq. ' ') .or. (.not. Header_Present)) Then
	  Return
	EndIf
	X = Index (Img, ':') - 1
	If (X .le. 0) Then
	  Fld = Img
	Else
	  Fld = Img(1:X)
	EndIf
	Call Str$Upcase (Fld, Fld)
	X = 1
	Do While (X .le. Header_Count)
	  If (Header(X) .eq. Fld) Then
	    Return
	  EndIf
	  X = X + 1
	EndDo
	Header_Check = .false.
	Return
	End ! Header_Check
	Subroutine Cmd_ArticleNone
	Call SMG_All_Print ('No article selected', '|')
	End ! Cmd_ArticleNone
	Integer Function Cmd_ArticleNumber (G, Text, S)
	Include 'News.Def'
C Parameter definition
	Integer   *4	G
	Character *(*)	Text
	Integer   *4	S
C Local delinitions
	Integer   *4	Lg
	Integer   *4	N
	Integer   *4	X
	Character *8    Num
C Begin Cmd_ArticleNUmber
	Lg = Len(Text)
	X = 1
	N = 0
	Cmd_ArticleNumber = .true.
	Do While ((X .le. Lg) .and. Cmd_ArticleNumber)
	  If ((Text(X:X) .ge. '0') .and. (Text(X:X) .le. '9')) Then
	    N = 10 * N + IChar(Text(X:X)) - IChar('0')
	  Else
	    Cmd_ArticleNumber = .false.
	  EndIf
	  X = X + 1
	EndDo
	If (Cmd_ArticleNumber) Then
	  If 
     $       (
     $        (N .ge. Group(G).Active_Start) 
     $       .and.
     $	      (N .le. Group(G).Active_End)
     $	     )
     $	  Then
	    S = N
	  Else
	    Call ItoS (N, Num, Lg)
	    Print '(A)', ' Article ' // Num(1:Lg) // ' not available'
	    Cmd_ArticleNumber = .false.
	  EndIf
	EndIf
	End ! Cmd_ArticleNumber
	Subroutine Cmd_ArticleKill (G, A, U)
	Include 'News.Def'
C Parameter definition
	Integer   *4		G	! Group number
	Integer   *4		A	! Article number
	Integer   *4            U	! Unread article count, updated
C External routines
	Integer			Range_Find
C Local definitions
	Logical 		Any
	Integer   *4		I
	Integer   *4		I_Lg
	Character *8		I_C
	Integer	  *4		Subject_Lg
	Integer   *4		Status
	Character *128		Subject
	Integer   *4		XSubject_Lg
	Character *128		XSubject
C Begin Cmd_ArticleKill
	Any = .false.
C Send XHdg Subject command to retrieve subject line for article
	Call CacheHdr (G, A, Subject, Subject_Lg)
C%	Call XHdr (A, 'subject', Subject, Subject_Lg)
C Remove article number from front of subject line
	Call ItoS (A, I_C, I_Lg)
        If (I_Lg .lt. Subject_Lg) Then
	
	  Subject(1:Subject_Lg-I_Lg) = Subject(1+I_Lg:Subject_Lg)
	  Subject_Lg = Subject_Lg - I_Lg
C Display subject being killed
	  Call SMG_All_Print
     $      ('Searching for: ' // Subject(1:Subject_Lg), '|')
C Page through articles looking for this subject
	  Do I = Group(G).Active_Start, Group(G).Active_End
	    If (Range_Find(G, I, .false.) .eq. 0) Then
c	      Write (XSubject, '(A,I)') 'Looking at article ', I
c	      Call SMG_ALL_Print (XSubject, '|')
	      Call CacheHdr (G, I, XSubject, XSubject_Lg)
c%	      Call XHdr (I, 'subject', XSubject, XSubject_Lg)
c	      Call SMG_All_Print (XSubject(1:XSubject_Lg), '|')
C Remove article number from front of subject line
	      Call ItoS (I, I_C, I_Lg)
	      If (XSubject_Lg .gt. I_Lg) Then
	        XSubject(1:XSubject_Lg-I_Lg) = 
     $ 		  XSubject(1+I_Lg:XSubject_Lg)
	        XSubject_Lg = XSubject_Lg - I_Lg
	        If 
     $		  (Subject(1:Subject_Lg) .eq. XSubject(1:XSubject_Lg))
     $	        Then
	          Status = Range_Find (G, I, .true., U)
	          Call ItoS (I, I_C, I_Lg)
	          Call SMG_All_Print ('Killed: ' // I_C(1:I_Lg), '|')
	          Any = .true.
	        EndIf
	      EndIf ! (XSubject_Lg .gt. I_Lg)
	    EndIf ! (Range_Find(G, I, .false.) .eq. 0)
	  End Do
        EndIf ! (I_Lg .lt. Subject_Lg)
	If (.not. Any) Then
	  Call SMG_All_Print ('No articles killed', '|')
	End If
	End ! Cmd_ArticleKill
	Integer Function Cmd_ArticleSameSubj (G, A)
	Include 'News.Def'
C Parameter definition
	Integer   *4		G	! Group number
	Integer   *4		A	! Article number
C External routines
	Integer			Range_Find
C Local definitions
	Integer   *4		I
	Integer   *4		I_Lg
	Character *8		I_C
	Integer	  *4		Subject_Lg
	Integer   *4		Status
	Character *128		Subject
	Integer   *4		XSubject_Lg
	Character *128		XSubject
C Begin Cmd_ArticleSameSubj
C Send XHdg Subject command to retrieve subject line for article
	Call CacheHdr (G, A, Subject, Subject_Lg)
C Remove article number from front of subject line
	Call ItoS (A, I_C, I_Lg)
        If (I_Lg .lt. Subject_Lg) Then
	
	  Subject(1:Subject_Lg-I_Lg) = Subject(1+I_Lg:Subject_Lg)
	  Subject_Lg = Subject_Lg - I_Lg
C Display subject being looked for
	  Call SMG_All_Print
     $      ('Searching for: ' // Subject(1:Subject_Lg), '|')
C Page through articles looking for this subject
	  Do I = Group(G).Active_Start, Group(G).Active_End
	    If (Range_Find(G, I, .false.) .eq. 0) Then
	      Call CacheHdr (G, I, XSubject, XSubject_Lg)
C Remove article number from front of subject line
	      Call ItoS (I, I_C, I_Lg)
	      If (XSubject_Lg .gt. I_Lg) Then
	        XSubject(1:XSubject_Lg-I_Lg) = 
     $ 		  XSubject(1+I_Lg:XSubject_Lg)
	        XSubject_Lg = XSubject_Lg - I_Lg
	        If 
     $		  (Subject(1:Subject_Lg) .eq. XSubject(1:XSubject_Lg))
     $	        Then
		  Cmd_ArticleSameSubj = .true.
		  A = I
		  Return
	        EndIf
	      EndIf ! (XSubject_Lg .gt. I_Lg)
	    EndIf ! (Range_Find(G, I, .false.) .eq. 0)
	  End Do
        EndIf ! (I_Lg .lt. Subject_Lg)
	Call SMG_All_Print ('No other articles found', '|')
	Cmd_ArticleSameSubj = .false.
	Return
	End ! Cmd_ArticleSameSubj 
	Integer Function Cmd_ArticleFirst (G, A)
	Include 'News.Def'
C Parameter definition
	Integer   *4		G	! Group number
	Integer   *4		A	! Article number
C Entry point definitions
	Integer			Cmd_ArticleLast
C External routines
	Integer			Range_Find
C Local definitions
	Integer   *4		E, D, I, S
C Begin Cmd_ArticleFirst
	S = Group(G).Active_Start
	E = Group(G).Active_End
        D = +1
	Goto 1
C Begin Cmd_ArticleLast
	Entry Cmd_ArticleLast (G, A)
	S = Group(G).Active_End
        E = Group(G).Active_Start
        D = -1
	Goto 1
C Page through articles looking for first unread
1	Do I = S, E, D 
	  If (Range_Find(G, I, .false.) .eq. 0) Then
	    A = I
	    Cmd_ArticleFirst = .true.
	    Return
	  EndIf
	End Do
C No unread articles found
	Call SMG_All_Print ('No unread articles found', '|')
	Cmd_ArticleFirst = .false.
	Return
	End ! Cmd_ArticleFirst
	Subroutine XHdr (A, What, Result, Lg)
	Include 'News.Def'
C Parameter definitions
	Integer   *4		A
	Character *(*)		What
	Character *(*)		Result
	Integer   *4		Lg
C External routines
	Integer  		Srv_Cmd
	Integer			Srv_Recv
	Integer			Srv_RdTxt
C local definitions
	Integer   *4		Status
C Begin XHdr
C Request header line with a 'xhdr what article_number' command
	Write (Result, '(A,I)') 'xhdr ' // What // ' ', A
	Status = Srv_Cmd (Result, Result, Lg)
	If (.not. Status) Then
	  Call SMG_All_Print ('Server failed', '|')
	  Stop
	EndIf
	If (Result(1:3) .ne. '221') Then
	  Call SMG_All_Print (Result(1:Lg), '|')
	  Call SMG_All_Print ('Unexpect server response', '|')
	  Stop
	EndIf
C Get header line
	Status = Srv_Recv (Result, Lg)
	If (.not. Status) Then
	  Call SMG_All_Print ('Server failed', '|')
	  Stop
	EndIf
C Skip rest of response
	If (Result(1:Lg) .ne. '.') Then
	  Status = Srv_RdTxt (.false., .false., %Val(0))	
	  If (.not. Status) Then
	    Call SMG_All_Print ('Server failed', '|')
	    Stop
	  EndIf
	EndIf
C Return
	Return
	End ! XHdr
	Subroutine Cmd_Help
	Include 'News.Def'
C Begin Cmd_Help
	Call SMG_Erase
	Call SMG_All_Print ('b             backup', '|')
	Call SMG_All_Print ('c [#]         catchup', '|')
	Call SMG_All_Print ('d             directory', '|')
	Call SMG_All_Print ('d/g pattern   group directory', '|')
	Call SMG_All_Print ('f             followup', '|')
	Call SMG_All_Print ('g group       go group', '|')
	Call SMG_All_Print ('h             help', '|')
	Call SMG_All_Print ('k             kill', '|')
	Call SMG_All_Print ('m             mark unread', '|')
	Call SMG_All_Print ('n             next', '|')
	Call SMG_All_Print ('p             post', '|')
	Call SMG_All_Print ('q             quit', '|')
	Call SMG_All_Print ('r             refresh', '|')
	Call SMG_All_Print ('s             save', '|')
	Call SMG_All_Print ('u             unsubscribe', '|')
	Call SMG_All_Print ('x             rotate', '|')
	Call SMG_All_Print ('z             next article same subject', '|') 
	Call SMG_All_Print ('#             article number', '|')
	Call SMG_All_Print ('^             first unread article', '|')
	Call SMG_All_Print ('$             last unread article', '|')
	Call SMG_All_Print (' ', '|')
	Return
	End ! Cmd_Help
	Integer Function Open_Newsrc ()
	Include 'News.Def'
C External routines
	Integer     	Range_Allocate
	Integer		TrimLg
	External	User_Open
C Local Definitions
	Character  *1		C
	Integer    *4		Status
	Character  *512		Image
	Integer    *4		R
	Integer    *4		S, L, N, E, X
C Begin Open_Newsrc
	Call User_Open_Init ('STATUS_OLD',0,0,0,0,0,0,0,0)
	Open
     $    (
     $     Unit 	= LU_Newsrc,
     $	   File		= 'Sys$Login:XX.Newsrc',
     $     Status	= 'Old',
     $     Form		= 'Formatted',
     $	   Recl		= 512,
     $	   UserOpen	= User_Open,
     $     IOStat	= Status
     $    )
	Group_Count = 0
	If (Status .ne. 0) Then
	  Newsrc_Is_Open = .false.
	  Open_Newsrc = 0
	  Return
	EndIf
	Newsrc_Is_Open = .true.
C Get date of creation of XX.Newsrc
	Call User_Open_Get_CDT (LU_Newsrc, Newsrc_CDT_VMS)
	Newsrc_CDT_News(1:2) = Newsrc_CDT_VMS(10:11)	! Year
	X = Index
     $    (
     $     'JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC',
     $     Newsrc_CDT_VMS(4:6)
     $    )
	Write (Newsrc_CDT_News(3:4), '(I2.2)') X/3 + 1	! Month
        Newsrc_CDT_News( 5: 6) = Newsrc_CDT_VMS(1:2)	! Day
	If (Newsrc_CDT_News(5:5) .eq. ' ') Then
	  Newsrc_CDT_News(5:5) = '0'
	EndIf
	Newsrc_CDT_News( 7: 7) = ' '
	Newsrc_CDT_News( 8: 9) = Newsrc_CDT_VMS(13:14)	! Hour
	If (Newsrc_CDT_News(8:8) .eq. ' ') Then
	  Newsrc_CDT_News(8:8) = '0'
	EndIf
	Newsrc_CDT_News(10:11) = Newsrc_CDT_VMS(16:17)	! Minute
	If (Newsrc_CDT_News(10:10) .eq. ' ') Then
	  Newsrc_CDT_News(10:10) = '0'
	EndIf
	Newsrc_CDT_News(12:13) = Newsrc_CDT_VMS(19:20)	! Second
	If (Newsrc_CDT_News(12:12) .eq. ' ') Then
	  Newsrc_CDT_News(12:12) = '0'
	EndIf
C Now read users XX.Newsrc
	Status = 1
	Do While (Status)
	  Read
     $	    (
     $       Unit	= LU_Newsrc,
     $       Fmt	= '(A)',
     $       IOStat	= Status
     $      )
     $      Image
	  If (Status .lt. 0) Then
	    Open_Newsrc = 1
	    Status = 0
	  Else If (Status .ne. 0) Then
	    Close (LU_Newsrc)
	    Open_Newsrc = 0
	    Status = 0
	  Else
	    Status = 1
	    X = Index (Image, ':')
	    If (X .eq. 0) Then
	      X = Index (Image, '!')
	    EndIf
	    If (X .ne. 0) Then
C We have a new group
	      Group_Count = Group_Count + 1
	      Group(Group_Count).Range_First = 0
	      Group(Group_Count).Range_Last  = 0
	      Group(Group_Count).Newsrc_File = .true.
C Record group's name
	      Group(Group_Count).Name = Image (1:X-1)
C Get subscribed information
	      If (Image(X:X) .eq. ':') Then
	        Group(Group_Count).Subscribed = .true.
	      Else
	        Group(Group_Count).Subscribed = .false.
	      EndIf
C Clear moderated and activefile flags, they will get set properly
C when we page through the news groups later.
	      Group(Group_Count).Active_Post = .false.
	      Group(Group_Count).Active_File = .false.
	      
C Get article ranges
	      S = 0
	      N = 0
	      E = 0
	      L = 0
	      Do While (X .le. 512)
	        X = X + 1
		If (X .eq. 512) Then
	          C = ','
	        Else
	          C = Image (X:X)
	        EndIf
	        If ((C .ge. '0') .and. (C .le. '9')) Then
	          N = 10 * N + IChar(C) - IChar('0')
	        Else If (C .eq. '-') Then
	          S = N
	          N = 0
	        Else If (C .eq. ',') Then
	          E = N
	          If (S .eq. 0) Then
		    S = E
	          EndIf
C Now add a new range entry to this group's control structure
		  If (S .ne. 0) Then
		    If ((S .le. L) .or. (S .gt. E)) Then
		      Call SMG_All_Print
     $			(
     $                   'Error in XX.Newsrc entry for group ' //
     $                   Group(Group_Count).Name
     $                     (1:TrimLg(Group(Group_Count).Name)),
     $			 '|'
     $		        )
		    Else
	              R = Range_Allocate ()
	              Range(R).Next  = 0
	              Range(R).Start = S
		      Range(R).End   = E
	              If (Group(Group_Count).Range_First .eq. 0) Then
	                Group(Group_Count).Range_First = R
	              Else
	                Range(Group(Group_Count).Range_Last).Next
     $                    = R
	              EndIf
	              Group(Group_Count).Range_Last = R
		      L = E
		    EndIf
		  EndIf
C Get ready for next range
		  S = 0
	          E = 0
	          N = 0
	        EndIf
	      EndDo
	    Else
	
	    EndIf
	  EndIf
	End Do	  
	End ! Open_Newsrc
	Integer Function Close_Newsrc ()
	Include 'News.Def'
C External definitions
	Integer			TrimLg
C Local Definitions
	Logical			First
	Integer    *4		G
	Character  *512		Image
	Integer	   *4		R
	Integer    *4		Status
	Integer    *4		X
	Integer    *4           S,    E
	Character  *8		S_C,  E_C
	Integer    *4		S_Lg, E_Lg
C Begin Close_Newsrc
	If (Newsrc_Is_Open) Then
	  Close (LU_Newsrc)
	EndIf
	Open
     $    (
     $     Unit 	   = LU_Newsrc,
     $	   File		   = 'Sys$Login:XX.Newsrc',
     $     Status	   = 'New',
     $     Form		   = 'Formatted',
     $     CarriageControl = 'List',
     $	   Recl		   = 512,
     $     IOStat	   = Status
     $    )
	G = 1
	If (Status .ne. 0) Then
	  Close_Newsrc = 0
	  Return
	EndIf
C Wander through all groups
	Do While (G .le. Group_Count)
	  If
     $       (
     $        (Group(G).Active_File .and. Group(G).Subscribed)
     $       .or.
     $        Group(G).Newsrc_File
     $       )
     $    Then
C Start image with group name
	    X = TrimLg (Group(G).Name)
	    Image(1:X) = Group(G).Name(1:X)
C Now add subscribed flag
	    X = X + 1
	    If (Group(G).Subscribed) Then
	      Image(X:X) = ':'
	    Else
	      Image(X:X) = '!'
	    EndIf
C Wander through all ranges for this group
	    R = Group(G).Range_First
	    First = .true.
	    Do While (R .ne. 0)
	      If (.not. First) Then
	        X = X + 1
	        Image(X:X) = ','
	      Else
	        First = .false.
	      EndIf
	      S = Range(R).Start
	      Call ItoS (S, S_C, S_Lg)
	      Image(X+1:X+S_Lg) = S_C(1:S_Lg)
	      X = X + S_Lg
	      If (Range(R).End .ne. Range(R).Start) Then
                X = X + 1
	        Image(X:X) = '-'
		E = Range(R).End
	        Call ItoS (E, E_C, E_Lg)
	        Image(X+1:X+E_Lg) = E_C(1:E_Lg)
	        X = X + E_Lg
	      EndIf
	      R = Range(R).Next
	    End Do ! (R .ne. 0)
	    Write (LU_Newsrc, '(A)') Image(1:X)
	 
	  EndIf
	  G = G + 1
	End Do ! (G .le. Group_Count)
C Return success
	Close_Newsrc = 1
	End ! Close_Newsrc
	Integer Function Range_Allocate
	Include 'News.Def'
C Begin Range_Allocate
	If (Range_Free_List .ne. 0) Then
	  Range_Allocate = Range_Free_List
	  Range_Free_List = Range(Range_Free_List).Next
	Else
	  If (Range_Count .lt. Mx_Range) Then
	    Range_Count = Range_Count + 1
	    Range_Allocate = Range_Count
	  Else
	    Call SMG_All_Print ('Range array exceeded', '|')
	    Stop
	  EndIf
	EndIf
	Range(Range_Allocate).Next  = 0
	Range(Range_Allocate).Start = 0
	Range(Range_Allocate).End   = 0
	Return
	End ! Range_Allocate
	Subroutine Range_Deallocate (R)
 	Include 'News.Def'
C Parameter definitions
	Integer   *4	R	! Range index
C Local definitions
	Integer   *4 	N, X
C Begin Range_Deallocate
	X = R
	Do While (X .ne. 0)
	  N = Range(X).Next
	  Range(X).Next = Range_Free_List
	  Range_Free_List = X
	  X = N
	End Do
	End ! Range_Deallocate
	Integer Function Range_Find (G, A, Create, U)
	Include 'News.Def'
C Description
C
C   Locates the Range entry for article number A in group G.
C   If a range entry does not exist for A and Create is true,
C   one is created.
C
C   Returns the Range entry index or zero if none.
C
C Parameter definitions
	Integer   *4		G	! Group number
	Integer	  *4		A	! Article number
	Logical			Create  ! .true. if create on no find
	Integer   *4		U	! Unread article count, updated
C External routines
	Integer			Range_Allocate
C Local definitions
	Integer   *4		P
	Integer   *4		R, RR
	Logical			Found
C Begin Range_Find
	P = 0
	R = Group(G).Range_First
	Found = .false.
	Range_Find = 0
	Do While ((R .ne. 0) .and. (.not. Found))	
	  If ((A .ge. Range(R).Start) .and. (A .le. Range(R).End)) Then
	    Found = .true.
	    Range_Find = R
	  Else If (A .lt. Range(R).Start) Then
	    Found = .true.
	  Else
	    P = R
	    R = Range(R).Next
	  End If
	End Do
	If ((Range_Find .eq. 0) .and. Create) Then
	  If 
     $      (
     $       (P .ne. 0) 
     $      .and. 
     $       ((Range(P).End+1) .eq. A)
     $      ) Then
            Range(P).End = Range(P).End + 1
	    Range_Find = P
	    Call Range_Combine (P, R)
	  Else If
     $      (
     $       (R .ne. 0) 
     $      .and.
     $       ((Range(R).Start-1) .eq. A)
     $      ) Then
	    Range(R).Start = Range(R).Start - 1
	    Range_Find = R
	    Call Range_Combine (P, R)
	  Else
	    RR = Range_Allocate ()
	    Range_Find = RR
	    Range(RR).Start = A
	    Range(RR).End   = A
	    Range(RR).Next  = R
	    If (P .eq. 0) Then
	      Group(G).Range_First = Range_Find
	      Group(G).Range_Last  = Range_Find
	    Else
	      Range(P).Next = Range_Find
	    EndIf
	  EndIf
	  U = U - 1
	  If (U .lt. 0) Then
	    U = 0
	  EndIf
	EndIf
	End ! Range_Find
	Subroutine Range_Combine (P, R)
	Include 'News.Def'
C Parameter definitions
	Integer   *4		P
	Integer   *4		R
C Begin Range_Combine
	If ((P .ne. 0) .and. (R .ne. 0)) Then
C Are range entries ajacent?
	  If ((Range(P).End + 1) .eq. Range(R).Start) Then
C Mark entry P with entire range
	    Range(P).End  = Range(R).End
C Remove entry R from the chain
	    Range(P).Next = Range(R).Next
C Release entry R
	    Range(R).Next = 0
	    Call Range_Deallocate (R)
	  EndIf
	EndIf
	End ! Range_Combine
	Integer Function Srv_Connect
	Include 'News.Def'
C Description
C
C   Connects to the Remote News server.  
C
C   Returns success or failure.
C   
C Local Variables
	Integer     *4          I
        Integer     *4          Lg
	Record /Socket_IN_Def/  Server_Socket
	Integer	    *4		Status
	Integer     *4		IP_Address
	Equivalence		(IP_Address, Server_IP_Number)
C Empty Recv buffer
	Recv_BufS = 2
	Recv_BufE = 1
C Open a channel to INET0:
	Channel = Socket
     $    (
     $     %Val(AF_INet),
     $     %Val(Sock_Stream),
     $     %Val(0)
     $    )
	If (Channel .eq. -1) Then
	  Srv_Connect = 2
	  Return
	EndIf
C Connect to remote machine
	Server_Socket.SIN_Family  = AF_INet
	Server_Socket.SIN_Port    = HtoNS (%Val(119))
        Server_Socket.SIN_Address = IP_Address 	! '111e6880'X
	Do I = 1,8
	  Server_Socket.SIN_Fill(I) = 0
	EndDo
	Status = Connect 
     $    (
     $     %Val(Channel),
     $     Server_Socket,
     $     %Val(16)
     $    )
	If (Status .ne. 0) Then
	  Srv_Connect = 4
	  Return
	EndIf
C Return success
	Srv_Connect = 1
	Return
	End ! Srv_Connect
	Integer Function Srv_NetClose()
	Include 'News.Def'
C Description
C
C   Close Connection to the Remote News server.  
C
C   Returns success or failure.
C   
C External routines
	Integer  	NetClose
C Local variables
	Integer   *4	Status
C Begin Srv_NetClose
C Disconnect from remote machine
	Status = NetClose (%Val(Channel))
	If (Status .ne. 0) Then
	  Srv_NetClose = 4
	  Return
	EndIf
C Return success
	Srv_NetClose = 1
	Return
	End ! Srv_NetClose
	Integer Function Srv_Recv (Buf, Lg)
	Include 'News.Def'
C Description
C
C   Read data form server to Buf.
C
C   Returns success or failure.
C Parameter Definitions
	Character *(*)		Buf
	Integer   *4		Lg
C Local definitions
	Integer	  *4		Buf_Lg
	Character *2		CRLF
	Logical 		CR
	Logical			Done
	Integer   *4		I
	Integer   *4		III
	Integer   *4		N
	Integer   *4		NN
	Integer	  *4		Recv_Buf_Addr
	Integer   *4		Recv_Buf_Lg
	Logical			Skip
C Begin Srv_Recv
	Buf_Lg = Len(Buf)
	CRLF(1:1) = Char(13)
	CRLF(2:2) = Char(10)
	Lg = 0			! No bytes xferred so far
	Done = .false.
	CR = .false.
       Do While (.not. Done)
C If there is no data in the receive buffer, get some
	If (Recv_BufS .gt. Recv_BufE) Then
	  Recv_Buf_Addr = %Loc(Recv_Buf)
	  Recv_Buf_Lg   = Len(Recv_Buf)
	  I = Recv 
     $      (
     $       %Val(Channel),
     $       %Val(Recv_Buf_Addr),
     $       %Val(Recv_Buf_Lg),
     $       %Val(0)
     $      )
	  If (I .eq. -1) Then
	    Srv_Recv = 0
	  Else
	    Srv_Recv = 1
	    If (Debug) Then
	      Print '(8(x,o3.3))', (IChar(Recv_Buf(III:III)),III=1,I)
	    EndIf
	  EndIf
	
	  Recv_BufS = 1
	  Recv_BufE = I
	EndIf
C Now we have some data
C If we last saw a carriage return and the next character is a LF
C then we have found the end of the image and we are done
	If (CR .and. (Recv_Buf(Recv_BufS:Recv_BufS) .eq. CRLF(2:2))) Then
	  Done = .true.
	  Recv_BufS = Recv_BufS + 1
	Else
C No terminator, look for the next hunk to transfer.
	  CR = .false.
	  I = Index (Recv_Buf(Recv_BufS:Recv_BufE), CRLF(1:1))
	  If (I .eq. 0) Then
	    N = Recv_BufE - Recv_BufS + 1
	    Skip = 0
	  Else 
	    N = I - 1
	    Skip = 1
	    CR = .true.
	  EndIf
C Anything to tranfer?
	  If (N .gt. 0) Then
	    NN = Buf_Lg - Lg
	    If (NN .gt. N) Then
	      NN = N
	    EndIf
	    If (NN .gt. 0) Then
	      Buf(Lg+1:Lg+NN) = Recv_Buf(Recv_BufS:Recv_BufS+NN-1)
	      Lg = Lg + NN
	    EndIf
	  EndIf
	  Recv_BufS = Recv_BufS + N + Skip
	EndIf
       EndDo
	If (Lg .le. 0) Then
	  Lg = 1
	  Buf(1:1) = ' '
	EndIf
	Return
	End ! Srv_Recv
	Integer Function Srv_Send (Msg)
	Include 'News.Def'
C Description
C
C   Send data from Buffer to News Server
C
C   Returns success or failure.
C Parameter Definitions
	Character *(*)		Msg
C Local definitions
	Character *512		Buf
	Integer	  *4		Buf_Addr
	Integer   *4		Buf_Lg
	Integer   *4		CC
  
C Begin Srv_Send
	Buf_Lg   = Len(Msg)
	Buf      = Msg(1:Buf_Lg) // Char(13) // Char(10)
	Buf_Addr = %Loc(Buf)
	Buf_Lg   = Buf_Lg+2
	CC = Send
     $    (
     $     %Val(Channel),
     $     %Val(Buf_Addr),
     $     %Val(Buf_Lg),
     $     %Val(0)
     $    )
	If (CC .eq. -1) Then
	  Srv_Send = 0
	Else
	  Srv_Send = 1
	EndIf
	Return
	End ! Srv_Send
	Integer Function Srv_Cmd (Cmd, Rsp, Lg)
	Include 'News.Def'
C Description
C
C   Send Cmd to the news server and retrieve its response in Rsp
C
C   Returns
C
C     Srv_Cmd	Success or failure
C     Rsp 	Response text from server
C     Lg	Length of Rsp text
C Parameter definitions
	
	Character *(*)		Cmd
	Character *(*)		Rsp
	Integer	  *4		Lg
C External Routines
	Integer			Srv_Send
	Integer			Srv_Recv
C Begin Srv_Cmd
	Srv_Cmd = Srv_Send (Cmd)
	If (.not. Srv_Cmd) Then
	  Return
	EndIf
	Srv_Cmd = Srv_Recv (Rsp, Lg)
C Return 
	Return
	End ! Srv_Cmd
	Integer Function Srv_RdTxt (P, Rot, Image_Routine)
	Include 'News.Def'
C Parameters
	Logical		P		! .true. means print
					! .false. means skip
	Logical		Rot		! .true. means rotate
	External	Image_Routine   ! Called for each image
	Integer		Image_Routine
C External Routines
	Integer		Srv_Recv
	Logical		SMG_More_Print
C Local definitions
	Character *512 	Buf
	Integer	  *4	Lg
        Logical	  	Ok
C Begin Srv_RdTxt
	Buf(1:1) = ' '
	Ok = P
	Do While (Buf(1:Lg) .ne. '.')
	  Srv_RdTxt = Srv_Recv (Buf, Lg)
	  If (.not. Srv_RdTxt) Then
	    Return
	  EndIf
	  If (Buf(1:Lg) .ne. '.') Then
	    If (Ok) Then
	      If (Rot) Then
		Call Rotate (Buf(1:Lg))
	      EndIf
	      If
     $	        (
     $		 (%Loc(Image_Routine) .eq. 0)
     $          .or.
     $		 (Image_Routine(Buf(1:Lg)))
     $          )
     $	      Then
		If (Debug) Then
		  Call SMG_More_Print ('RdTxt image:', '|')
		EndIf
	        OK = SMG_More_Print (Buf(1:Lg), '|')
	      EndIf
	    EndIf
	  EndIf
	EndDo
C All done, return
	End ! Srv_RdTxt
	Subroutine Srv_CopyTxt (LU, Pre, Rotate_Flag)
	Include 'News.Def'
C Parameter definitions
	Integer   *4	LU
	Character *(*)	Pre
	Logical		Rotate_Flag
C External routines
	Integer		Srv_Recv
C Local definitions
	Character *1024	Buf
	Integer   *4    Buf_S, Buf_E	! Buf pointers
	Integer   *4    Lg
	Integer	  *4	MxLg		! Recl for LU_Save
	Integer	  *4    Pre_Lg
	Integer	  *4	X
C Begin Srv_CopyTxt
	If (Pre .eq. ' ') Then
	  Pre_Lg = 0
	Else
 	  Pre_Lg = Len (Pre)
	EndIf
	Inquire (Unit = LU, Recl = MxLg)
	MxLg = MxLg - 4
	Buf = ' '
	Lg = 1
	Do While ((Buf(1:Lg) .ne. '.') .and. (Srv_Recv(Buf,Lg)))
	  If (Lg .eq. 0) Then
	    Lg = 1
	    Buf(1:1) = ' '
	  EndIf
	  If (Buf(1:lg) .ne. '.') Then
	    If (Rotate_Flag) Then
	      Call Rotate (Buf(1:Lg))
%%end part c
Michael Dorl (608) 262-0466
dorl@vms.macc.wisc.edu
dorl@wiscmacc.bitnet