PROGRAM Lista_Ordinata
!--------------------------------------------------------------------------
! Costruisce una lista ordinata di interi
! leggendo i dati da un file di testo contenente un dato per riga.
!--------------------------------------------------------------------------
  IMPLICIT NONE
  TYPE :: Lista                  ! Elemento della lista
    INTEGER            :: dato
    TYPE(Lista),POINTER:: next
  END TYPE Lista
  INTEGER            :: elem, ok
  CHARACTER(LEN=20)  :: nomef
  TYPE(Lista),POINTER:: radice

  WRITE(*,*) "Nome del file? "
  READ(*,*) nomef
  OPEN(UNIT=1,FILE=nomef,STATUS='OLD')
!   Connessione di un file di testo ("formattato").
!   Argomenti:
!     UNIT, numero di unita' associato al file (arbitrario)
!     FILE, nome esterno completo di percorso, se necessario
!     STATUS, stato all'apertura:
!       'OLD', il file deve esistere, viene aperto e posizionato all'inizio
!       'NEW', il file non deve esistere, viene creato
!       'REPLACE', se esiste, viene cancellato e ricreato vuoto,
!                  se non esiste, viene creato
!   NOTA: lo stato va fornito come stringa!
!         maiuscole e minuscole sono in questo caso equivalenti.

  NULLIFY(radice)            !! La lista e' inizialmente vuota
  DO
    READ(1,*,IOSTAT=ok) elem  ! Leggi un elemento e controlla "fine file"
!     Valori di IOSTAT: 0, lettura corretta
!                      -1, fine file, nessun dato letto
!                      >0, errore di lettura
    IF (ok/=0)  EXIT
    CALL In_Lista(radice,elem)
  END DO
  WRITE(*,*) "Ecco la lista:"
  CALL Stampa(radice)
  STOP

CONTAINS
!--------------------------------------------------------------------------
SUBROUTINE In_Lista(radice,elem)
! Inserisce il dato "elem" nella lista ordinata di cui "radice" e' il
! puntatore iniziale.
!--------------------------------------------------------------------------
  INTEGER,INTENT(IN) :: elem
  TYPE(Lista),POINTER:: radice  ! NO INTENT per argomenti POINTER !!

  TYPE(Lista),POINTER:: corr, & ! Puntatore all'elemento corrente
                        prec, & ! Puntatore all'elemento precedente
                        aus     ! Puntatore ausiliario

  IF (.NOT.ASSOCIATED(radice)) THEN
!!-   La lista e' vuota
    ALLOCATE(radice)     ! Alloca il primo elemento
    NULLIFY(radice%next) ! Termina la lista
    radice%dato = elem   ! Inserisci il dato

  ELSE
!!-   La lista non e' vuota
!--   Trova la posizione del prossimo elem.
    corr => radice
    DO
      IF (.NOT.ASSOCIATED(corr))  EXIT
      IF (elem <= corr%dato) THEN ! Se questa e' la posizione
        EXIT                      ! esci
      ELSE                        ! altrimenti
        prec => corr              ! avanza i puntatori
        corr => corr%next
      END IF
    END DO
!!-   corr punta all'elemento successivo, se c'e'
!!-   prec punta a quello precedente, se c'e'

    IF (.NOT.ASSOCIATED(corr)) THEN
!!-   elem e' l'ultimo della lista
!--   Accodalo in fondo, dopo prec
      ALLOCATE(aus)      ! Alloca il nuovo elemento
      NULLIFY(aus%next)  ! E' l'ultimo, termina la lista
      prec%next => aus   ! Accodalo al precedente
      aus%dato = elem    ! Inserisci il dato

    ELSE IF (.NOT.ASSOCIATED(corr,radice)) THEN
!       Con due argomenti, ASSOCIATED controlla che il puntatore sia
!       associato ad un target specifico;
!       in questo caso, il puntatore corr al target di radice.

!!-     elem non e' ne' il primo ne' l'ultimo
!--     Inseriscilo tra prec e corr
      ALLOCATE(aus)      ! Alloca il nuovo elemento
      prec%next => aus   ! Inseriscilo dopo "prec"
      aus%next => corr   ! Agganciagli "corr"
      aus%dato = elem    ! Inserisci il dato

    ELSE           ! Inserimento in testa
!!-     elem e' il primo della lista
!--     Inseriscilo tra radice e corr
      ALLOCATE(aus)      ! Alloca il nuovo elemento
      aus%next => radice ! Agganciagli il vecchio primo elemento
      radice => aus      ! Accodalo alla radice
      aus%dato = elem    ! Inserisci il dato
    END IF
  END IF
  RETURN
END SUBROUTINE In_Lista
!--------------------------------------------------------------------------
SUBROUTINE Stampa(radice)
!--------------------------------------------------------------------------
  TYPE(Lista),POINTER:: radice
  TYPE(Lista),POINTER:: corr
  corr => radice
  DO
    IF (.NOT.ASSOCIATED(corr))  EXIT
    WRITE(*,'(16I5)',ADVANCE='NO') corr%dato
!     ADVANCE='NO' consente di continuare a scrivere sulla riga,
!     il salto riga e' comandato dal formato.
    corr => corr%next
  END DO
  WRITE(*,*)        ! Esegue un salto riga
  RETURN
END SUBROUTINE Stampa
END PROGRAM Lista_Ordinata
