PROGRAM Internet_Addresses
!-----------------------------------------------------------------------------
! Program to read TCP/IP addresses from a file and produce a list of distinct
! addresses and a count of how many times each appeared in the file.  The
! addresses and counts are stored in a linked list.
! Variables used are:
!   FileName    : name of data file containing addresses
!   OpenStatus  : status variable for OPEN statement
!   InputStatus : status variable for READ statement
!   Address     : an address read from the file
!   AddressList : pointer to first node in the linked list of addresses
! Subroutines used to process linked list:
!   Add_To_List, Search, Output_Addresses
!
! Input (keyboard): FileName
! Input (file):     Addresses
! Output:           A list of distinct addresses and their counts
!-----------------------------------------------------------------------------

  IMPLICIT NONE

  ! Define an address node type
  TYPE :: List_Node
    CHARACTER(15) :: TCP_IP_Address      ! Address data
    INTEGER :: Count                     ! Counter for this address
    TYPE(List_Node), POINTER :: Next     ! Pointer to next node
  END TYPE List_Node

  CHARACTER(15) :: Address, FileName
  INTEGER :: OpenStatus, InputStatus
  TYPE(List_Node), POINTER :: AddressList

  ! Get name of data file and open it for reading
  WRITE (*, '(1X, A)', ADVANCE = "NO") "Enter name of file of addresses: "
  READ *, FileName
  OPEN (UNIT = 10, FILE = FileName, STATUS = "OLD", IOSTAT = OpenStatus)
  IF (OpenStatus > 0) STOP "*** Cannot open address file ***"

  ! Create empty linked list
  NULLIFY(AddressList)

  ! Read addresses from the file and store them in the list,
  ! until end of file reached
  DO
     READ(10, '(A)', IOSTAT = InputStatus) Address
     IF (InputStatus > 0) STOP "*** Input error ***"
     IF (InputStatus < 0) EXIT  ! end of file

     CALL Add_To_List(AddressList, Address)
  END DO

  CALL Output_Addresses(AddressList)
  STOP
CONTAINS

  !---------------------------------------------------------------------------
  ! This subroutine determines if Address is already in the linked list
  ! AddressList (using Search).  If it is not, it is added at the beginning
  ! of the list; if it is, its count is incremented by 1.  Local variables:
  !   AllocateStatus : status variable for OPEN statement
  !   LocPtr         : pointer to a node containing Address or null if 
  !                    not found
  !   In_the_List    : indicates if Address is already in AddressList
  ! Accepts:  AddressList and Address
  ! Returns:  Modified AddressList
  !---------------------------------------------------------------------------

  SUBROUTINE Add_To_List(AddressList, Address)

    TYPE(List_Node), POINTER :: AddressList
    CHARACTER(*), INTENT(IN) :: Address
    TYPE(List_Node), POINTER :: LocPtr
    INTEGER :: AllocateStatus
    LOGICAL :: In_the_List

    IF (.NOT. ASSOCIATED(AddressList)) THEN  ! List is empty

      ALLOCATE(AddressList, STAT = AllocateStatus)
      IF (AllocateStatus /= 0) STOP "*** Out of memory *** "

      AddressList%TCP_IP_Address = Address
      AddressList%Count = 1
      NULLIFY(AddressList%Next)

    ELSE  ! List not empty -- determine if Address is already in the list

       CALL Search(AddressList, Address, LocPtr, In_the_List)

       IF (In_the_List) THEN   ! Increment its count by 1
          LocPtr%Count = LocPtr%Count + 1

       ELSE                    ! Create a new node and insert it at the front
          ALLOCATE(LocPtr, STAT = AllocateStatus)
          IF (AllocateStatus /= 0) STOP "*** Out of memory *** "

          LocPtr%TCP_IP_Address = Address
          LocPtr%Count = 1
          LocPtr%Next => AddressList
          AddressList => LocPtr
       END IF
    END IF
  RETURN
  END SUBROUTINE Add_To_List


  !---------------------------------------------------------------------------
  ! This subroutine searches AddressList for a node containing Address.  
  ! If it is found, LocPtr points to the node and In_the_List is set to 
  ! true; otherwise LocPtr is NULL and In_the_List is false.
  !
  ! Accepts: AddressList, Address
  ! Returns: LocPtr, In_the_List
  !---------------------------------------------------------------------------

  SUBROUTINE Search(AddressList, Address, LocPtr, In_the_List)

    TYPE(List_Node), POINTER :: AddressList, LocPtr
    CHARACTER(*), INTENT(IN) :: Address
    LOGICAL,INTENT(OUT) :: In_the_List

    LocPtr => AddressList
    In_the_List = .FALSE.

    ! Traverse the list until the address is found 
    ! or the end of list is encountered
    DO
       IF( In_the_List .OR. .NOT. ASSOCIATED(LocPtr)) EXIT
       ! Address found or end of list -- terminate repetition

       IF(LocPtr%TCP_IP_Address == Address) THEN  ! Address found
          In_the_List = .TRUE.
       ELSE                                       ! Move to next node
          LocPtr => LocPtr%Next
       END IF
    END DO
     RETURN
  END SUBROUTINE Search


  !---------------------------------------------------------------------------
  ! This subroutine prints the contents of the linked list pointed to by
  ! AddressList.  For each node, it prints the address and count.  Local
  ! variable used:
  !   Ptr : pointer that runs through the list
  !
  ! Accepts: AddressList
  ! Output:  Addresses and counts stored in nodes of AddressList
  !---------------------------------------------------------------------------

  SUBROUTINE Output_Addresses(AddressList)

     TYPE(List_Node), POINTER :: AddressList, Ptr

     Ptr => AddressList

     PRINT *, "Summary of Internet address data"
     PRINT *
     PRINT *, "   Address         Count "
     PRINT *, "--------------------------- "

     ! Print node information until end of list reached
     DO
        IF (.NOT. ASSOCIATED(Ptr)) EXIT  ! End of list reached

        ! Otherwise display contents of node pointed to by Ptr
        PRINT '(1X, A, 4X, I4)', Ptr%TCP_IP_Address, Ptr%Count

        ! Move to next node
        Ptr => Ptr%Next
     END DO
     RETURN
  END SUBROUTINE Output_Addresses

END PROGRAM Internet_Addresses
