Index: /IOIPSL/tags/v2_2_5/IOIPSL_License_CeCILL.txt
===================================================================
--- /IOIPSL/tags/v2_2_5/IOIPSL_License_CeCILL.txt	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/IOIPSL_License_CeCILL.txt	(revision 5058)
@@ -0,0 +1,36 @@
+The following licence information concerns ONLY the IOIPSL directory
+====================================================================
+
+Copyright (C) Institut Pierre Simon Laplace : IPSL
+
+This software is composed by a set of subroutines working as an
+interface between climate models and NETCDF files following CF
+convention. Library sources, examples of use and tools based on
+IOIPSL are provided. This library requires NetCDF library :
+http://www.unidata.ucar.edu/software/netcdf/
+
+This software is governed by the CeCILL license under French law and
+abiding by the rules of distribution of free software. You can use,
+modify and/or redistribute the software under the terms of the CeCILL
+license as circulated by CEA, CNRS and INRIA at the following URL
+"http://www.cecill.info".
+
+As a counterpart to the access to the source code and rights to copy,
+modify and redistribute granted by the license, users are provided only
+with a limited warranty and the software's author, the holder of the
+economic rights, and the successive licensors have only limited
+liability.
+
+In this respect, the user's attention is drawn to the risks associated
+with loading, using, modifying and/or developing or reproducing the
+software by the user in light of its specific status of free software,
+that may mean that it is complicated to manipulate, and that also
+therefore means that it is reserved for developers and experienced
+professionals having in-depth computer knowledge. Users are therefore
+encouraged to load and test the software's suitability as regards their
+requirements in conditions enabling the security of their systems and/or
+data to be ensured and, more generally, to use and operate it in the
+same conditions as regards security.
+
+The fact that you are presently reading this means that you have had
+knowledge of the CeCILL license and that you accept its terms.
Index: /IOIPSL/tags/v2_2_5/arch/arch-X64_ADA.env
===================================================================
--- /IOIPSL/tags/v2_2_5/arch/arch-X64_ADA.env	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/arch/arch-X64_ADA.env	(revision 5058)
@@ -0,0 +1,10 @@
+module unload compilerwrappers
+module unload netcdf
+module unload hdf5
+
+module load netcdf/mpi/4.3.3.1
+module load hdf5/mpi/1.8.14
+#NETCDF_INC_DIR=/smplocal/pub/NetCDF/4.1.3/mpi/include
+#NETCDF_LIB_DIR=/smplocal/pub/NetCDF/4.1.3/mpi/lib
+#HDF5_INC_DIR=/smplocal/pub/HDF5/1.8.9/par/linclude
+#HDF5_LIB_DIR=/smplocal/pub/HDF5/1.8.9/par/lib
Index: /IOIPSL/tags/v2_2_5/arch/arch-X64_ADA.fcm
===================================================================
--- /IOIPSL/tags/v2_2_5/arch/arch-X64_ADA.fcm	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/arch/arch-X64_ADA.fcm	(revision 5058)
@@ -0,0 +1,16 @@
+%COMPILER            mpiifort
+%LINK                mpiifort
+%FPP                 mpiifort -E
+%AR                  ar
+%MAKE                gmake
+%FPP_FLAGS           -P
+%FPP_DEF             KEY_NONE CPP_NETCDF4
+%BASE_FFLAGS         -i4 -r8 -auto -align all -I${MKLROOT}/include
+%PROD_FFLAGS         -g -traceback -O3 -vec-report2
+%DEV_FFLAGS          -g -O1 -traceback
+%DEBUG_FFLAGS        -g -check bounds -fp-model strict
+%MPI_FFLAGS
+%OMP_FFLAGS          -openmp -openmp-threadprivate=compat
+%BASE_LD             -i4 -r8 -auto
+%MPI_LD
+%OMP_LD              -openmp -openmp-threadprivate=compat
Index: /IOIPSL/tags/v2_2_5/arch/arch-X64_ADA.path
===================================================================
--- /IOIPSL/tags/v2_2_5/arch/arch-X64_ADA.path	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/arch/arch-X64_ADA.path	(revision 5058)
@@ -0,0 +1,23 @@
+#NETCDF_INCDIR="-I $NETCDF_INC_DIR"
+#NETCDF_LIBDIR="-L $NETCDF_LIB_DIR"
+#NETCDF_LIB="-lnetcdff -lnetcdf"
+
+MPI_INCDIR=""
+MPI_LIBDIR=""
+MPI_LIB=""
+
+#HDF5_INCDIR="-I $HDF5_INC_DIR"
+#HDF5_LIBDIR="-L $HDF5_LIB_DIR"
+#HDF5_LIB="-lhdf5_hl -lhdf5 -lhdf5 -lz -lcurl"
+
+XIOS_INCDIR="-I$ROOT/../XIOS/inc"
+XIOS_LIBDIR="-L$ROOT/../XIOS/lib"
+XIOS_LIB="-lxios -lstdc++"
+
+ORCHIDEE_INCDIR="-I$ROOT/../ORCHIDEE/inc"
+ORCHIDEE_LIBDIR="-L$ROOT/../ORCHIDEE/lib"
+ORCHIDEE_LIB="-lorchidee "
+
+IOIPSL_INCDIR="-I$ROOT/../IOIPSL/src"
+IOIPSL_LIBDIR="-L$ROOT/../IOIPSL/src"
+IOIPSL_LIB="-lioipsl"
Index: /IOIPSL/tags/v2_2_5/arch/arch-X64_IRENE-AMD.fcm
===================================================================
--- /IOIPSL/tags/v2_2_5/arch/arch-X64_IRENE-AMD.fcm	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/arch/arch-X64_IRENE-AMD.fcm	(revision 5058)
@@ -0,0 +1,16 @@
+%COMPILER            mpif90 
+%LINK                mpif90 
+%FPP                 mpif90 -E
+%AR                  ar
+%MAKE                gmake
+%FPP_FLAGS           -P
+%FPP_DEF             KEY_NONE CPP_NETCDF4
+%BASE_FFLAGS         -i4 -r8 -auto -align all -I${MKLROOT}/include
+%PROD_FFLAGS         -g -traceback -O3 -mavx2
+%DEV_FFLAGS          -g -O1 -traceback
+%DEBUG_FFLAGS        -g -traceback -check bounds -fp-model strict
+%MPI_FFLAGS
+%OMP_FFLAGS          -qopenmp
+%BASE_LD             -i4 -r8 -auto $MKL_LDFLAGS
+%MPI_LD
+%OMP_LD              -qopenmp
Index: /IOIPSL/tags/v2_2_5/arch/arch-X64_IRENE-AMD.path
===================================================================
--- /IOIPSL/tags/v2_2_5/arch/arch-X64_IRENE-AMD.path	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/arch/arch-X64_IRENE-AMD.path	(revision 5058)
@@ -0,0 +1,12 @@
+NETCDF_INCDIR="-I $NETCDFC_INCDIR -I $NETCDFFORTRAN_INCDIR"
+NETCDF_LIBDIR="-L $NETCDFC_LIBDIR -L $NETCDFFORTRAN_LIBDIR"
+NETCDF_LIB="-lnetcdf -lnetcdff"
+
+HDF5_INCDIR=""
+HDF5_LIBDIR=""
+HDF5_LIB=""
+
+MPI_INCDIR=""
+MPI_LIBDIR=""
+MPI_LIB=""
+
Index: /IOIPSL/tags/v2_2_5/arch/arch-X64_IRENE.fcm
===================================================================
--- /IOIPSL/tags/v2_2_5/arch/arch-X64_IRENE.fcm	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/arch/arch-X64_IRENE.fcm	(revision 5058)
@@ -0,0 +1,16 @@
+%COMPILER            mpif90 
+%LINK                mpif90 
+%FPP                 mpif90 -E
+%AR                  ar
+%MAKE                gmake
+%FPP_FLAGS           -P
+%FPP_DEF             KEY_NONE CPP_NETCDF4
+%BASE_FFLAGS         -i4 -r8 -auto -align all -I${MKLROOT}/include
+%PROD_FFLAGS         -g -traceback -O3 -xAVX
+%DEV_FFLAGS          -g -O1 -traceback
+%DEBUG_FFLAGS        -g -traceback -check bounds -fp-model strict
+%MPI_FFLAGS
+%OMP_FFLAGS          -qopenmp
+%BASE_LD             -i4 -r8 -auto $MKL_LDFLAGS
+%MPI_LD
+%OMP_LD              -qopenmp
Index: /IOIPSL/tags/v2_2_5/arch/arch-X64_IRENE.path
===================================================================
--- /IOIPSL/tags/v2_2_5/arch/arch-X64_IRENE.path	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/arch/arch-X64_IRENE.path	(revision 5058)
@@ -0,0 +1,12 @@
+NETCDF_INCDIR="-I $NETCDFC_INCDIR -I $NETCDFFORTRAN_INCDIR"
+NETCDF_LIBDIR="-L $NETCDFC_LIBDIR -L $NETCDFFORTRAN_LIBDIR"
+NETCDF_LIB="-lnetcdf -lnetcdff"
+
+HDF5_INCDIR=""
+HDF5_LIBDIR=""
+HDF5_LIB=""
+
+MPI_INCDIR=""
+MPI_LIBDIR=""
+MPI_LIB=""
+
Index: /IOIPSL/tags/v2_2_5/arch/arch-X64_JEANZAY.fcm
===================================================================
--- /IOIPSL/tags/v2_2_5/arch/arch-X64_JEANZAY.fcm	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/arch/arch-X64_JEANZAY.fcm	(revision 5058)
@@ -0,0 +1,15 @@
+%COMPILER            mpiifort
+%LINK                mpiifort
+%AR                  ar
+%MAKE                gmake
+%FPP_FLAGS           -P -traditional
+%FPP_DEF              
+%BASE_FFLAGS         -integer-size 32 -real-size 64 -align all -fp-model precise 
+%PROD_FFLAGS         -O3
+%DEV_FFLAGS          -g -O2 -traceback -fp-stack-check -ftrapuv -check bounds
+%DEBUG_FFLAGS        -fpe0 -O0 -g -traceback -fp-stack-check -ftrapuv -check bounds -check all -init=arrays -init=snan
+%MPI_FFLAGS
+%OMP_FFLAGS          -qopenmp
+%BASE_LD             
+%MPI_LD
+%OMP_LD              -qopenmp
Index: /IOIPSL/tags/v2_2_5/arch/arch-X64_JEANZAY.path
===================================================================
--- /IOIPSL/tags/v2_2_5/arch/arch-X64_JEANZAY.path	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/arch/arch-X64_JEANZAY.path	(revision 5058)
@@ -0,0 +1,12 @@
+NETCDF_INCDIR=""
+NETCDF_LIBDIR=""
+NETCDF_LIB="-lnetcdf -lnetcdff"
+
+HDF5_INCDIR=""
+HDF5_LIBDIR=""
+HDF5_LIB=""
+
+MPI_INCDIR=""
+MPI_LIBDIR=""
+MPI_LIB=""
+
Index: /IOIPSL/tags/v2_2_5/arch/arch-X64_OCCIGEN.fcm
===================================================================
--- /IOIPSL/tags/v2_2_5/arch/arch-X64_OCCIGEN.fcm	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/arch/arch-X64_OCCIGEN.fcm	(revision 5058)
@@ -0,0 +1,16 @@
+%COMPILER            mpif90 
+%LINK                mpif90 
+%FPP                 mpif90 -E
+%AR                  ar
+%MAKE                gmake
+%FPP_FLAGS           -P
+%FPP_DEF             KEY_NONE CPP_NETCDF4
+%BASE_FFLAGS         -i4 -r8 -auto -align all -I${MKLROOT}/include
+%PROD_FFLAGS         -g -traceback -O3 -vec-report2 -xAVX
+%DEV_FFLAGS          -g -O1 -traceback
+%DEBUG_FFLAGS        -g -traceback -check bounds -fp-model strict
+%MPI_FFLAGS
+%OMP_FFLAGS          -openmp -openmp-threadprivate=compat
+%BASE_LD             -i4 -r8 -auto $MKL_LIBS
+%MPI_LD
+%OMP_LD              -openmp -openmp-threadprivate=compat
Index: /IOIPSL/tags/v2_2_5/arch/arch-ifort_CICLAD.fcm
===================================================================
--- /IOIPSL/tags/v2_2_5/arch/arch-ifort_CICLAD.fcm	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/arch/arch-ifort_CICLAD.fcm	(revision 5058)
@@ -0,0 +1,16 @@
+%COMPILER            mpif90 
+%LINK                mpif90 
+%FPP                 mpif90 -E
+%AR                  ar
+%MAKE                gmake
+%FPP_FLAGS           -P
+%FPP_DEF             KEY_NONE CPP_NETCDF4
+%BASE_FFLAGS         -fPIC -i4 -r8 -auto -align all 
+%PROD_FFLAGS         -g -traceback -O3
+%DEV_FFLAGS          -g -O1 -traceback
+%DEBUG_FFLAGS        -g -traceback -check bounds -fp-model strict
+%MPI_FFLAGS
+%OMP_FFLAGS          -openmp -openmp-threadprivate compat
+%BASE_LD             -fPIC -i4 -r8 -auto 
+%MPI_LD
+%OMP_LD              -openmp
Index: /IOIPSL/tags/v2_2_5/arch/arch-ifort_CICLAD.path
===================================================================
--- /IOIPSL/tags/v2_2_5/arch/arch-ifort_CICLAD.path	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/arch/arch-ifort_CICLAD.path	(revision 5058)
@@ -0,0 +1,12 @@
+NETCDF_INCDIR=$(nf-config --fflags)
+NETCDF_LIBDIR=$(nf-config --flibs)
+NETCDF_LIB=""
+
+MPI_INCDIR=""
+MPI_LIBDIR=""
+MPI_LIB=""
+
+HDF5_INCDIR=""
+HDF5_LIBDIR=""
+HDF5_LIB=""
+
Index: /IOIPSL/tags/v2_2_5/arch/arch-ifort_LSCE.fcm
===================================================================
--- /IOIPSL/tags/v2_2_5/arch/arch-ifort_LSCE.fcm	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/arch/arch-ifort_LSCE.fcm	(revision 5058)
@@ -0,0 +1,15 @@
+%COMPILER            mpif90
+%LINK                mpif90
+%AR                  ar
+%MAKE                gmake
+%FPP_FLAGS           -P -traditional
+%FPP_DEF              
+%BASE_FFLAGS         -i4 -r8 -fp-model precise  
+%PROD_FFLAGS         -O3
+%DEV_FFLAGS          -g -O2 -traceback -fp-stack-check -ftrapuv -check bounds
+%DEBUG_FFLAGS        -fpe0 -O0 -g -traceback -fp-stack-check -ftrapuv -check bounds -check all
+%MPI_FFLAGS
+%OMP_FFLAGS          -openmp -openmp-threadprivate compat
+%BASE_LD             -i4 -r8
+%MPI_LD
+%OMP_LD              -openmp
Index: /IOIPSL/tags/v2_2_5/arch/arch-ifort_LSCE.path
===================================================================
--- /IOIPSL/tags/v2_2_5/arch/arch-ifort_LSCE.path	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/arch/arch-ifort_LSCE.path	(revision 5058)
@@ -0,0 +1,13 @@
+NETCDF_INCDIR="-I/usr/local/install/netcdf-4.3.2p/include"
+NETCDF_LIBDIR="-L/usr/local/install/netcdf-4.3.2p/lib -L/usr/local/install/hdf5-1.8.9p/lib"
+NETCDF_LIB="-lnetcdff -lnetcdf -lhdf5_hl -lhdf5 -lhdf5 -lz -lcurl -lstdc++"
+
+HDF5_INCDIR=""
+HDF5_LIBDIR=""
+HDF5_LIB=""
+
+MPI_INCDIR=""
+MPI_LIBDIR=""
+MPI_LIB=""
+
+
Index: /IOIPSL/tags/v2_2_5/bld.cfg
===================================================================
--- /IOIPSL/tags/v2_2_5/bld.cfg	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/bld.cfg	(revision 5058)
@@ -0,0 +1,40 @@
+# ----------------------- FCM extract configuration file -----------------------
+cfg::type                           bld
+cfg::version                        1.0
+
+
+# ------------------------------------------------------------------------------
+# Build information
+# ------------------------------------------------------------------------------
+
+inc arch.fcm
+inc config.fcm
+
+%FFLAGS              %BASE_FFLAGS %COMPIL_FFLAGS
+%ROOT_PATH           $PWD
+%SRC_PATH            %ROOT_PATH
+
+dir::root            %ROOT_PATH/build
+
+src::src             %SRC_PATH/src
+src::rebuild         %SRC_PATH/rebuild
+search_src           false
+bld::lib ioipsl
+bld::target libioipsl.a
+bld::target flio_rbld.exe rebuild
+bld::exe_dep             
+
+bld::tool::fc        %COMPILER
+bld::tool::ld        %LINK  
+bld::tool::ldflags   %LD_FLAGS %LIB 
+bld::tool::fflags    %FFLAGS  
+bld::tool::fppkeys   %CPP_KEY %FPP_DEF
+bld::tool::fpp       %FPP
+bld::tool::fppflags  %FPP_FLAGS
+
+# Pre-process code before analysing dependencies
+bld::pp              false
+
+bld::excl_dep        use::netcdf
+bld::excl_dep        use::iso_fortran_env
+bld::tool::SHELL   /bin/bash
Index: /IOIPSL/tags/v2_2_5/build.sh
===================================================================
--- /IOIPSL/tags/v2_2_5/build.sh	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/build.sh	(revision 5058)
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+export PATH=$PWD/../ICOSAGCM/tools/FCM/bin:$PATH
+fcm build  $*
Index: /IOIPSL/tags/v2_2_5/example/AA_make
===================================================================
--- /IOIPSL/tags/v2_2_5/example/AA_make	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/example/AA_make	(revision 5058)
@@ -0,0 +1,333 @@
+#-
+#- $Id$
+#-
+USER_DIR = $(shell /bin/pwd)
+MAKE_DIR = '??'
+ifneq ($(USER_DIR),$(MAKE_DIR))
+ @$(error You must invoke this Makefile from its directory)
+endif
+#-
+IOIPSL_LIB = -L$(LIBDIR) -lioipsl
+ifeq ($(L_X),1)
+IOIPSL_LIB = -L$(LIBDIR) -lsxioipsl
+endif
+#-
+all: testhist1 testhist2 testrest testopp testcalendar testflio
+	echo don t forget to do : $(M_K) clean !!!
+
+testhist1:
+	$(M_K) libioipsl
+	$(M_K) testhist1.x
+#-Q- sx8brodie	echo cd $(USER_DIR) > Job_testhist1
+#-Q- sx8brodie	echo '\
+#-Q- sx6nec	echo cd $(USER_DIR) > Job_testhist1
+#-Q- sx6nec	echo '\
+#-Q- sx8mercure	echo cd $(USER_DIR) > Job_testhist1
+#-Q- sx8mercure	echo '\
+	./testhist1.x \
+#-Q- sx8brodie ' >> Job_testhist1
+#-Q- sx8brodie	qsub -j o -S /usr/bin/ksh \
+#-Q- sx8brodie	 -o $(USER_DIR)/Out_testhist1 Job_testhist1
+#-Q- sx8brodie	echo job Job_testhist1 has been submitted
+#-Q- sx6nec ' >> Job_testhist1
+#-Q- sx6nec	qsub -j o -S /usr/bin/ksh \
+#-Q- sx6nec	 -o $(USER_DIR)/Out_testhist1 Job_testhist1
+#-Q- sx6nec	echo job Job_testhist1 has been submitted
+#-Q- sx8mercure ' >> Job_testhist1
+#-Q- sx8mercure	qsub -j o -S /usr/bin/ksh \
+#-Q- sx8mercure	 -o $(USER_DIR)/Out_testhist1 Job_testhist1
+#-Q- sx8mercure	echo job Job_testhist1 has been submitted
+
+	@echo ' '
+	@echo 'file testhist1.nc should be created'
+	@echo 'to see it, type : ncdump -h testhist1.nc'
+	@echo ' '
+
+testhist1.x: testhist1.o
+	$(F_L) -o testhist1.x testhist1.o \
+               $(L_O) $(IOIPSL_LIB) $(NCDF_LIB)
+
+testhist2:
+	$(M_K) libioipsl
+	$(M_K) testhist2.x
+#-Q- sx8brodie	echo cd $(USER_DIR) > Job_testhist2
+#-Q- sx8brodie	echo '\
+#-Q- sx6nec	echo cd $(USER_DIR) > Job_testhist2
+#-Q- sx6nec	echo '\
+#-Q- sx8mercure	echo cd $(USER_DIR) > Job_testhist2
+#-Q- sx8mercure	echo '\
+	./testhist2.x \
+#-Q- sx8brodie ' >> Job_testhist2
+#-Q- sx8brodie	qsub -j o -S /usr/bin/ksh \
+#-Q- sx8brodie	 -o $(USER_DIR)/Out_testhist2 Job_testhist2
+#-Q- sx8brodie	echo job Job_testhist2 has been submitted
+#-Q- sx6nec ' >> Job_testhist2
+#-Q- sx6nec	qsub -j o -S /usr/bin/ksh \
+#-Q- sx6nec	 -o $(USER_DIR)/Out_testhist2 Job_testhist2
+#-Q- sx6nec	echo job Job_testhist2 has been submitted
+#-Q- sx8mercure ' >> Job_testhist2
+#-Q- sx8mercure	qsub -j o -S /usr/bin/ksh \
+#-Q- sx8mercure	 -o $(USER_DIR)/Out_testhist2 Job_testhist2
+#-Q- sx8mercure	echo job Job_testhist2 has been submitted
+
+	@echo ' '
+	@echo 'file testhist2.nc should be created'
+	@echo 'to see it, type : ncdump -h testhist2.nc'
+	@echo ' '
+	@echo 'file testhist2_bis.nc should be created'
+	@echo 'to see it, type : ncdump -h testhist2_bis.nc'
+	@echo ' '
+
+testhist2.x: testhist2.o
+	$(F_L) -o testhist2.x testhist2.o \
+               $(L_O) $(IOIPSL_LIB) $(NCDF_LIB)
+
+testrest:
+	$(M_K) libioipsl
+	$(M_K) testrest.x
+	$(RM) restfile.nc restfilebis.nc
+#-Q- sx8brodie	echo cd $(USER_DIR) > Job_testrest
+#-Q- sx8brodie	echo '\
+#-Q- sx6nec	echo cd $(USER_DIR) > Job_testrest
+#-Q- sx6nec	echo '\
+#-Q- sx8mercure	echo cd $(USER_DIR) > Job_testrest
+#-Q- sx8mercure	echo '\
+	./testrest.x \
+#-Q- sx8brodie ' >> Job_testrest
+#-Q- sx8brodie	qsub -j o -S /usr/bin/ksh \
+#-Q- sx8brodie	 -o $(USER_DIR)/Out_testrest Job_testrest
+#-Q- sx8brodie	echo job Job_testrest has been submitted
+#-Q- sx6nec ' >> Job_testrest
+#-Q- sx6nec	qsub -j o -S /usr/bin/ksh \
+#-Q- sx6nec	 -o $(USER_DIR)/Out_testrest Job_testrest
+#-Q- sx6nec	echo job Job_testrest has been submitted
+#-Q- sx8mercure ' >> Job_testrest
+#-Q- sx8mercure	qsub -j o -S /usr/bin/ksh \
+#-Q- sx8mercure	 -o $(USER_DIR)/Out_testrest Job_testrest
+#-Q- sx8mercure	echo job Job_testrest has been submitted
+
+	@echo ' '
+	@echo 'file restfile.nc should be created'
+	@echo 'to see it, type : ncdump -h restfile.nc'
+	@echo ' '
+	@echo 'file restfilebis.nc should be created'
+	@echo 'to see it, type : ncdump -h restfilebis.nc'
+	@echo ' '
+
+testrest.x: testrest.o
+	$(F_L) -o testrest.x testrest.o \
+               $(L_O) $(IOIPSL_LIB) $(NCDF_LIB)
+
+testopp:
+	$(M_K) libioipsl
+	$(M_K) testopp.x
+#-Q- sx8brodie	echo cd $(USER_DIR) > Job_testopp
+#-Q- sx8brodie	echo '\
+#-Q- sx6nec	echo cd $(USER_DIR) > Job_testopp
+#-Q- sx6nec	echo '\
+#-Q- sx8mercure	echo cd $(USER_DIR) > Job_testopp
+#-Q- sx8mercure	echo '\
+	./testopp.x \
+#-Q- sx8brodie ' >> Job_testopp
+#-Q- sx8brodie	qsub -j o -S /usr/bin/ksh \
+#-Q- sx8brodie	 -o $(USER_DIR)/Out_testopp Job_testopp
+#-Q- sx8brodie	echo job Job_testopp has been submitted
+#-Q- sx6nec ' >> Job_testopp
+#-Q- sx6nec	qsub -j o -S /usr/bin/ksh \
+#-Q- sx6nec	 -o $(USER_DIR)/Out_testopp Job_testopp
+#-Q- sx6nec	echo job Job_testopp has been submitted
+#-Q- sx8mercure ' >> Job_testopp
+#-Q- sx8mercure	qsub -j o -S /usr/bin/ksh \
+#-Q- sx8mercure	 -o $(USER_DIR)/Out_testopp Job_testopp
+#-Q- sx8mercure	echo job Job_testopp has been submitted
+
+testopp.x: testopp.o
+	$(F_L) -o testopp.x testopp.o \
+               $(L_O) $(IOIPSL_LIB) $(NCDF_LIB)
+
+testcalendar:
+	$(M_K) libioipsl
+	$(M_K) testcalendar.x
+#-Q- sx8brodie	echo cd $(USER_DIR) > Job_testcalendar
+#-Q- sx8brodie	echo '\
+#-Q- sx6nec	echo cd $(USER_DIR) > Job_testcalendar
+#-Q- sx6nec	echo '\
+#-Q- sx8mercure	echo cd $(USER_DIR) > Job_testcalendar
+#-Q- sx8mercure	echo '\
+	echo; \
+#-Q- sx8brodie ' >> Job_testcalendar
+#-Q- sx8brodie	echo '\
+#-Q- sx6nec ' >> Job_testcalendar
+#-Q- sx6nec	echo '\
+#-Q- sx8mercure ' >> Job_testcalendar
+#-Q- sx8mercure	echo '\
+        ./testcalendar.x; \
+#-Q- sx8brodie ' >> Job_testcalendar
+#-Q- sx8brodie	echo '\
+#-Q- sx6nec ' >> Job_testcalendar
+#-Q- sx6nec	echo '\
+#-Q- sx8mercure ' >> Job_testcalendar
+#-Q- sx8mercure	echo '\
+        echo; \
+#-Q- sx8brodie ' >> Job_testcalendar
+#-Q- sx8brodie	echo '\
+#-Q- sx6nec ' >> Job_testcalendar
+#-Q- sx6nec	echo '\
+#-Q- sx8mercure ' >> Job_testcalendar
+#-Q- sx8mercure	echo '\
+        ./testcalendar.x noleap; \
+#-Q- sx8brodie ' >> Job_testcalendar
+#-Q- sx8brodie	echo '\
+#-Q- sx6nec ' >> Job_testcalendar
+#-Q- sx6nec	echo '\
+#-Q- sx8mercure ' >> Job_testcalendar
+#-Q- sx8mercure	echo '\
+        echo; \
+#-Q- sx8brodie ' >> Job_testcalendar
+#-Q- sx8brodie	echo '\
+#-Q- sx6nec ' >> Job_testcalendar
+#-Q- sx6nec	echo '\
+#-Q- sx8mercure ' >> Job_testcalendar
+#-Q- sx8mercure	echo '\
+        ./testcalendar.x 360d; \
+#-Q- sx8brodie ' >> Job_testcalendar
+#-Q- sx8brodie	qsub -j o -S /usr/bin/ksh \
+#-Q- sx8brodie	 -o $(USER_DIR)/Out_testcalendar Job_testcalendar
+#-Q- sx8brodie	echo job Job_testcalendar has been submitted
+#-Q- sx6nec ' >> Job_testcalendar
+#-Q- sx6nec	qsub -j o -S /usr/bin/ksh \
+#-Q- sx6nec	 -o $(USER_DIR)/Out_testcalendar Job_testcalendar
+#-Q- sx6nec	echo job Job_testcalendar has been submitted
+#-Q- sx8mercure ' >> Job_testcalendar
+#-Q- sx8mercure	qsub -j o -S /usr/bin/ksh \
+#-Q- sx8mercure	 -o $(USER_DIR)/Out_testcalendar Job_testcalendar
+#-Q- sx8mercure	echo job Job_testcalendar has been submitted
+
+testcalendar.x: testcalendar.o
+	$(F_L) -o testcalendar.x testcalendar.o \
+               $(L_O) $(IOIPSL_LIB) $(NCDF_LIB)
+
+testflio:
+	$(M_K) libioipsl
+	$(M_K) testflio.x
+
+#-Q- sx8brodie	echo cd $(USER_DIR) > Job_testflio
+#-Q- sx8brodie	echo '\
+#-Q- sx6nec	echo cd $(USER_DIR) > Job_testflio
+#-Q- sx6nec	echo '\
+#-Q- sx8mercure	echo cd $(USER_DIR) > Job_testflio
+#-Q- sx8mercure	echo '\
+	./testflio.x \
+#-Q- sx8brodie ' >> Job_testflio
+#-Q- sx8brodie	qsub -j o -S /usr/bin/ksh \
+#-Q- sx8brodie	 -o $(USER_DIR)/Out_testflio Job_testflio
+#-Q- sx8brodie	echo job Job_testflio has been submitted
+#-Q- sx6nec ' >> Job_testflio
+#-Q- sx6nec	qsub -j o -S /usr/bin/ksh \
+#-Q- sx6nec	 -o $(USER_DIR)/Out_testflio Job_testflio
+#-Q- sx6nec	echo job Job_testflio has been submitted
+#-Q- sx8mercure ' >> Job_testflio
+#-Q- sx8mercure	qsub -j o -S /usr/bin/ksh \
+#-Q- sx8mercure	 -o $(USER_DIR)/Out_testflio Job_testflio
+#-Q- sx8mercure	echo job Job_testflio has been submitted
+
+	@echo ' '
+	@echo 'file testflio_0001.nc should be created'
+	@echo 'to see it, type : ncdump -h testflio_0001.nc'
+	@echo ' '
+
+testflio.x: testflio.o
+	$(F_L) -o testflio.x testflio.o \
+               $(L_O) $(IOIPSL_LIB) $(NCDF_LIB)
+
+testconfig:
+	$(M_K) libioipsl
+	$(M_K) testconfig.x
+#-Q- sx8brodie	echo cd $(USER_DIR) > Job_testconfig
+#-Q- sx8brodie	echo '\
+#-Q- sx6nec	echo cd $(USER_DIR) > Job_testconfig
+#-Q- sx6nec	echo '\
+#-Q- sx8mercure	echo cd $(USER_DIR) > Job_testconfig
+#-Q- sx8mercure	echo '\
+	./testconfig.x \
+#-Q- sx8brodie ' >> Job_testconfig
+#-Q- sx8brodie	qsub -j o -S /usr/bin/ksh \
+#-Q- sx8brodie	 -o $(USER_DIR)/Out_testconfig Job_testconfig
+#-Q- sx8brodie	echo job Job_testconfig has been submitted
+#-Q- sx6nec ' >> Job_testconfig
+#-Q- sx6nec	qsub -j o -S /usr/bin/ksh \
+#-Q- sx6nec	 -o $(USER_DIR)/Out_testconfig Job_testconfig
+#-Q- sx6nec	echo job Job_testconfig has been submitted
+#-Q- sx8mercure ' >> Job_testconfig
+#-Q- sx8mercure	qsub -j o -S /usr/bin/ksh \
+#-Q- sx8mercure	 -o $(USER_DIR)/Out_testconfig Job_testconfig
+#-Q- sx8mercure	echo job Job_testconfig has been submitted
+
+testconfig.x: testconfig.o
+	$(F_L) -o testconfig.x testconfig.o \
+               $(L_O) $(IOIPSL_LIB) $(NCDF_LIB)
+
+testconfig2:
+	$(M_K) libioipsl
+	$(M_K) testconfig2.x
+#-Q- sx8brodie	echo cd $(USER_DIR) > Job_testconfig2
+#-Q- sx8brodie	echo '\
+#-Q- sx6nec	echo cd $(USER_DIR) > Job_testconfig2
+#-Q- sx6nec	echo '\
+#-Q- sx8mercure	echo cd $(USER_DIR) > Job_testconfig2
+#-Q- sx8mercure	echo '\
+	./testconfig2.x \
+#-Q- sx8brodie ' >> Job_testconfig2
+#-Q- sx8brodie	qsub -j o -S /usr/bin/ksh \
+#-Q- sx8brodie	 -o $(USER_DIR)/Out_testconfig2 Job_testconfig2
+#-Q- sx8brodie	echo job Job_testconfig2 has been submitted
+#-Q- sx6nec ' >> Job_testconfig2
+#-Q- sx6nec	qsub -j o -S /usr/bin/ksh \
+#-Q- sx6nec	 -o $(USER_DIR)/Out_testconfig2 Job_testconfig2
+#-Q- sx6nec	echo job Job_testconfig2 has been submitted
+#-Q- sx8mercure ' >> Job_testconfig2
+#-Q- sx8mercure	qsub -j o -S /usr/bin/ksh \
+#-Q- sx8mercure	 -o $(USER_DIR)/Out_testconfig2 Job_testconfig2
+#-Q- sx8mercure	echo job Job_testconfig2 has been submitted
+
+testconfig2.x: testconfig2.o
+	$(F_L) -o testconfig2.x testconfig2.o \
+               $(L_O) $(IOIPSL_LIB) $(NCDF_LIB)
+
+config: libioipsl
+	(cd ../src; $(M_K) -f Makefile config)
+	$(BINDIR)/Fparser -main -name testconfig testconfig.f90
+	$(SCPDIR)/build_tkmenus
+	./config.tk
+	$(RM) Config.*
+
+libioipsl:
+	(cd ../src; $(M_K) -f Makefile)
+
+clean:
+	$(RM) *.*~ Makefile~ core *.o *.nc Out_t* Job_t* *.x *_run.def
+
+testhist1.o: testhist1.f90
+	$(F_C) $(F_O) testhist1.f90
+
+testhist2.o: testhist2.f90
+	$(F_C) $(F_O) testhist2.f90
+
+testrest.o: testrest.f90
+	$(F_C) $(F_O) testrest.f90
+
+testopp.o: testopp.f90
+	$(F_C) $(F_O) testopp.f90
+
+testcalendar.o: testcalendar.f90
+	$(F_C) $(F_O) testcalendar.f90
+
+testflio.o: testflio.f90
+	$(F_C) $(F_O) testflio.f90
+
+testconfig.o: testconfig.f90
+	$(F_C) $(F_O) testconfig.f90
+
+testconfig2.o: testconfig2.f90
+	$(F_C) $(F_O) testconfig2.f90
+
Index: /IOIPSL/tags/v2_2_5/example/AA_make.ldef
===================================================================
--- /IOIPSL/tags/v2_2_5/example/AA_make.ldef	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/example/AA_make.ldef	(revision 5058)
@@ -0,0 +1,13 @@
+#-
+#- $Id$
+#-
+#---------------------------------------------------------------------
+#- Samples related to IOIPSL
+#---------------------------------------------------------------------
+SHELL = /bin/ksh
+#---------------------------------------------------------------------
+LIBDIR = ../../../lib
+BINDIR = ../../../bin
+SCPDIR = ../../../scripts
+MODDIR = $(LIBDIR)
+#---------------------------------------------------------------------
Index: /IOIPSL/tags/v2_2_5/example/testcalendar.f90
===================================================================
--- /IOIPSL/tags/v2_2_5/example/testcalendar.f90	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/example/testcalendar.f90	(revision 5058)
@@ -0,0 +1,70 @@
+PROGRAM testcalendar
+!-
+!$Id$
+!-
+! This software is governed by the CeCILL license
+! See IOIPSL/IOIPSL_License_CeCILL.txt
+!---------------------------------------------------------------------
+!- This program will do some basic tests on the calendar
+!---------------------------------------------------------------------
+  USE calendar
+!-
+  IMPLICIT NONE
+!-
+  REAL :: julian, sec
+  INTEGER :: year, month, day
+  INTEGER :: iread, iret
+  CHARACTER(LEN=20) :: tmp
+!-
+  INTEGER :: iargc, getarg
+  EXTERNAL iargc, getarg
+!---------------------------------------------------------------------
+!-
+! Get the command line arguments
+!-
+  iread = iargc()
+  IF (iread .EQ. 0) THEN
+    WRITE(*,*) 'Using the default calendar'
+  ELSE
+    iret = getarg(1,tmp)
+    WRITE(*,*) 'Using the calendar named :',TRIM(tmp)
+    CALL ioconf_calendar (TRIM(tmp))
+  ENDIF
+!-
+! Get the origine of the julian days
+!-
+  julian = 0.0
+  CALL ju2ymds (julian, year, month, day, sec)
+  WRITE(*,*) 'Day at which the julian day was zero :'
+  WRITE(*,*) 'Year : ',year,' Month : ',month,' Day : ',day
+!-
+! Do we get the same day back when we go back and forth
+!-
+  year = 1997
+  month = 8
+  day = 21
+  sec = 0.0
+  WRITE(*,*) 'Day transformed into julian :'
+  WRITE(*,*) 'Year : ',year,' Month : ',month,' Day : ',day
+  CALL ymds2ju (year, month, day, sec, julian)
+  WRITE(*,*) ' --> The resulting julian day : ',julian
+  CALL ju2ymds (julian, year, month, day, sec)
+  WRITE(*,*) 'The day which comes out again :'
+  WRITE(*,*) 'Year : ',year,' Month : ',month,' Day : ',day
+!-
+! Yet another test but this time with a strange dat !
+!-
+  year = 1997
+  month = 3
+  day = 34
+  sec = 0.0
+  WRITE(*,*) 'Strange day transformed into julian :'
+  WRITE(*,*) 'Year : ',year,' Month : ',month,' Day : ',day
+  CALL ymds2ju (year, month, day, sec, julian)
+  WRITE(*,*) ' --> The resulting julian day : ',julian
+  CALL ju2ymds (julian, year, month, day, sec)
+  WRITE(*,*) &
+ &  'The day which comes out again, does it make more sense ? :'
+  WRITE(*,*) 'Year : ',year,' Month : ',month,' Day : ',day
+!-----------------------
+END PROGRAM testcalendar
Index: /IOIPSL/tags/v2_2_5/example/testconfig.f90
===================================================================
--- /IOIPSL/tags/v2_2_5/example/testconfig.f90	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/example/testconfig.f90	(revision 5058)
@@ -0,0 +1,98 @@
+PROGRAM testconfig
+!-
+!$Id$
+!-
+! This software is governed by the CeCILL license
+! See IOIPSL/IOIPSL_License_CeCILL.txt
+  !
+  USE getincom
+  !
+  !
+  !      This program will do some basic tests on the getin module
+  !
+  !
+  IMPLICIT NONE
+  !
+  LOGICAL           :: debug
+  CHARACTER(LEN=80) :: filename
+  CHARACTER(LEN=10), DIMENSION(3) :: strvec
+  INTEGER           :: split
+  REAL              :: g
+  REAL, DIMENSION(3) :: realvec
+  !
+  !-
+  !- set debug to have more information
+  !-
+  !Config  Key  = DEBUG_INFO
+  !Config  Desc = Flag for debug information
+  !Config  Def  = n
+  !Config  Help = This option allows to switch on the output of debug
+  !Config         information without recompiling the code.
+  !-
+  debug = .FALSE.
+  CALL getin('DEBUG_INFO',debug) 
+  !
+  !Config  Key  = FORCING_FILE
+  !Config  Desc = Name of file containing the forcing data
+  !Config  Def  = islscp_for.nc
+  !Config  Help = This is the name of the file which should be opened
+  !Config         for reading the forcing data of the dim0 model.
+  !Config         The format of the file has to be netCDF and COADS
+  !Config         compliant.
+  !-
+  filename='islscp_for.nc'
+  CALL getin('FORCING_FILE',filename)
+  !
+  !
+  !Config  Key  = SPLIT_DT
+  !Config  Desc = splits the timestep imposed by the forcing
+  !Config  Def  = 12
+  !Config  Help = With this value the time step of the forcing
+  !Config         will be devided. In principle this can be run
+  !Config         in explicit mode but it is strongly suggested
+  !Config         to use the implicit method so that the
+  !Config         atmospheric forcing has a smooth evolution.
+  !-
+  split = 12
+  CALL getin('SPLIT_DT', split)
+  !
+  !
+  !Config  Key  = GRAVIT
+  !Config  Desc = Gravitation constant
+  !Config  Def  = 9.98
+  !Config  Help = In theory these parameters could also be defined through
+  !Config         this mechanisme to ensure that the same value is used by
+  !Config         all components of the model.
+  !-
+  g = 9.98
+  CALL getin('GRAVIT', g)
+  !
+  !Config  Key  = WORDS
+  !Config  Desc = A vector of words
+  !Config  Def  = here there anywhere
+  !Config  Help = An example for a vector of strings
+  !-
+  strvec(1) = "here"
+  strvec(2) = "there"
+  strvec(3) = "anywhere"
+  CALL getin('WORDS', strvec)
+  !
+  !Config  Key  = VECTOR
+  !Config  Desc = A vector of reals
+  !Config  Def  = 1, 2, 3
+  !Config  Help = An example for a vector of REALs
+  !-
+  realvec=(/1,2,3/)
+  CALL getin('VECTOR', realvec)
+  !
+  WRITE(*,*) 'From the run.def we have extracted the following information :'
+  WRITE(*,*) 'DEBUG : ', debug
+  WRITE(*,*) 'FILENAME : ', filename(1:len_trim(filename))
+  WRITE(*,*) 'SPLIT : ', split
+  WRITE(*,*) 'G : ', g
+  WRITE(*,*) 'WORDS : ', strvec
+  WRITE(*,*) 'VECTOR : ', realvec
+  !
+  CALL getin_dump()
+  !
+END PROGRAM testconfig
Index: /IOIPSL/tags/v2_2_5/example/testconfig2.f90
===================================================================
--- /IOIPSL/tags/v2_2_5/example/testconfig2.f90	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/example/testconfig2.f90	(revision 5058)
@@ -0,0 +1,486 @@
+PROGRAM testconfig2
+!-
+!$Id$
+!-
+! This software is governed by the CeCILL license
+! See IOIPSL/IOIPSL_License_CeCILL.txt
+  !
+  USE getincom
+  !
+  !
+  !      This program will do some basic tests on the getin module
+  !
+  !
+  IMPLICIT NONE
+  !
+  INTEGER :: dayref=181
+  INTEGER :: anneeref = 1998
+  INTEGER :: nday = 10
+  INTEGER :: day_step = 240 
+  INTEGER :: iperiod = 5
+  INTEGER :: iconser = 240  
+  INTEGER :: iecri = 1
+  INTEGER :: idissip = 10
+  INTEGER :: nitergdiv = 1
+  INTEGER :: nitergrot = 2
+  INTEGER :: niterh = 2
+  INTEGER :: iphysiq = 5
+  INTEGER :: ecritphy = 1
+  INTEGER :: nbapp_rad = 12
+  INTEGER :: iflag_con = 2
+
+  REAL :: periodav = 1.
+  REAL :: tetagdiv = 7200.
+  REAL :: tetagrot = 7200.
+  REAL :: tetatemp  = 7200.
+  REAL :: coefdis = 0.
+  REAL :: clonn = 0.
+  REAL :: clatt = 0.
+  REAL :: grossismxx = 1.0
+  REAL :: grossismyy = 1.0
+  REAL :: dzoomxx = 0.0
+  REAL :: dzoomyy = 0.0
+  REAL :: tauxx = 3.0
+  REAL :: tauyy = 3.0
+  REAL :: clon = 0.
+  REAL :: clat = 0.
+  REAL :: grossismx = 1.0
+  REAL :: grossismy = 1.0
+  REAL :: dzoomx = 0.0
+  REAL :: dzoomy = 0.0
+  REAL :: taux = 3.0
+  REAL :: tauy = 3.0
+
+  LOGICAL :: lstardis = .TRUE.
+  LOGICAL :: purmats = .FALSE.
+  LOGICAL :: physic = .TRUE.
+  LOGICAL :: cycle_diurne = .TRUE.
+  LOGICAL :: soil_model = .TRUE.
+  LOGICAL :: new_oliq = .TRUE.
+  LOGICAL :: ok_orodr = .TRUE.
+  LOGICAL :: ok_orolf = .TRUE.
+  LOGICAL :: ok_limitvrai = .FALSE.
+  LOGICAL :: fxyhypbb = .TRUE.
+  LOGICAL :: ysinuss = .TRUE.
+  LOGICAL :: fxyhypb = .TRUE.
+  LOGICAL :: ysinus = .TRUE.
+!
+! For time keeping
+!
+  INTEGER :: date_time(8), date_start, date_end
+  CHARACTER(LEN=10) big_ben(3)
+!
+!
+!
+  CALL DATE_AND_TIME(big_ben(1), big_ben(2), big_ben(3), date_time)
+  date_start = date_time(8) + 1000*date_time(7) + 60*1000*date_time(7)
+!
+!Config  Key  = dayref
+!Config  Desc = Jour de l'etat initial
+!Config  Def  = 181
+!Config  Help = Jour de l'etat initial ( = 350  si 20 Decembre ,
+!Config         par expl. ,comme ici ) ... A completer
+  dayref=181
+  CALL getin('dayref', dayref)
+
+!Config  Key  = anneeref
+!Config  Desc = Annee de l'etat initial
+!Config  Def  = 1998
+!Config  Help = Annee de l'etat  initial 
+!Config         (   avec  4  chiffres   ) ... A completer
+  anneeref = 1998
+  CALL getin('anneeref',anneeref)
+
+!Config  Key  = nday
+!Config  Desc = Nombre de jours d'integration
+!Config  Def  = 10
+!Config  Help = Nombre de jours d'integration
+!Config         ... On pourait aussi permettre des mois ou des annees !
+  nday = 10
+  CALL getin('nday',nday)
+
+!Config  Key  = day_step
+!Config  Desc = nombre de pas par jour
+!Config  Def  = 240 
+!Config  Help = nombre de pas par jour (multiple de iperiod) (
+!Config          ici pour  dt = 1 min ) 
+  day_step = 240 
+  CALL getin('day_step',day_step)
+
+!Config  Key  = iperiod
+!Config  Desc = periode pour le pas Matsuno 
+!Config  Def  = 5
+!Config  Help = periode pour le pas Matsuno (en pas de temps) 
+  iperiod = 5
+  CALL getin('iperiod',iperiod)
+
+
+!Config  Key  = iconser
+!Config  Desc = periode de sortie des variables de controle
+!Config  Def  = 240  
+!Config  Help = periode de sortie des variables de controle
+!Config         (En pas de temps)
+  iconser = 240  
+  CALL getin('iconser', iconser)
+
+!Config  Key  = iecri
+!Config  Desc = periode d'ecriture du fichier histoire
+!Config  Def  = 1
+!Config  Help = periode d'ecriture du fichier histoire (en jour) 
+  iecri = 1
+  CALL getin('iecri',iecri)
+
+
+!Config  Key  = periodav
+!Config  Desc = periode de stockage fichier histmoy
+!Config  Def  = 1
+!Config  Help = periode de stockage fichier histmoy (en jour) 
+  periodav = 1.
+  CALL getin('periodav',periodav)
+
+!Config  Key  = idissip
+!Config  Desc = periode de la dissipation 
+!Config  Def  = 10
+!Config  Help = periode de la dissipation 
+!Config         (en pas) ... a completer !
+  idissip = 10
+  CALL getin('idissip',idissip)
+
+!Config  Key  = lstardis
+!Config  Desc = choix de l'operateur de dissipation
+!Config  Def  = y
+!Config  Help = choix de l'operateur de dissipation
+!Config         'y' si on veut star et 'n' si on veut non-start !
+!Config         Moi y en a pas comprendre ! 
+  lstardis = .TRUE.
+  CALL getin('lstardis',lstardis)
+
+
+!Config  Key  = nitergdiv
+!Config  Desc = Nombre d'iteration de gradiv
+!Config  Def  = 1
+!Config  Help = nombre d'iterations de l'operateur de dissipation 
+!Config         gradiv
+  nitergdiv = 1
+  CALL getin('nitergdiv',nitergdiv)
+
+!Config  Key  = nitergrot
+!Config  Desc = nombre d'iterations de nxgradrot
+!Config  Def  = 2
+!Config  Help = nombre d'iterations de l'operateur de dissipation  
+!Config         nxgradrot
+  nitergrot = 2
+  CALL getin('nitergrot',nitergrot)
+
+
+!Config  Key  = niterh
+!Config  Desc = nombre d'iterations de divgrad
+!Config  Def  = 2
+!Config  Help = nombre d'iterations de l'operateur de dissipation
+!Config         divgrad
+  niterh = 2
+  CALL getin('niterh',niterh)
+
+
+!Config  Key  = tetagdiv
+!Config  Desc = temps de dissipation pour div
+!Config  Def  = 7200
+!Config  Help = temps de dissipation des plus petites longeur 
+!Config         d'ondes pour u,v (gradiv)
+  tetagdiv = 7200.
+  CALL getin('tetagdiv',tetagdiv)
+
+!Config  Key  = tetagrot
+!Config  Desc = temps de dissipation pour grad
+!Config  Def  = 7200
+!Config  Help = temps de dissipation des plus petites longeur 
+!Config         d'ondes pour u,v (nxgradrot)
+  tetagrot = 7200.
+  CALL getin('tetagrot',tetagrot)
+
+!Config  Key  = tetatemp 
+!Config  Desc = temps de dissipation pour h
+!Config  Def  = 7200
+!Config  Help =  temps de dissipation des plus petites longeur 
+!Config         d'ondes pour h (divgrad)   
+  tetatemp  = 7200.
+  CALL getin('tetatemp',tetatemp )
+
+!Config  Key  = coefdis
+!Config  Desc = coefficient pour gamdissip
+!Config  Def  = 0
+!Config  Help = coefficient pour gamdissip  
+  coefdis = 0.
+  CALL getin('coefdis',coefdis)
+
+!Config  Key  = purmats
+!Config  Desc = Schema d'integration
+!Config  Def  = n
+!Config  Help = Choix du schema d'integration temporel.
+!Config         y = pure Matsuno sinon c'est du Matsuno-leapfrog
+  purmats = .FALSE.
+  CALL getin('purmats',purmats)
+
+!Config  Key  = physic
+!Config  Desc = Avec ls physique 
+!Config  Def  = y
+!Config  Help = Permet de faire tourner le modele sans 
+!Config         physique.
+  physic = .TRUE.
+  CALL getin('physic',physic)
+
+
+!Config  Key  =  iphysiq
+!Config  Desc = Periode de la physique
+!Config  Def  = 5
+!Config  Help = Periode de la physique en pas de temps de la dynamique.
+  iphysiq = 5
+  CALL getin('iphysiq', iphysiq)
+
+!Config  Key  = ecritphy
+!Config  Desc = Frequence d'ecriture de la physique
+!Config  Def  = 1
+!Config  Help = frequence  de l'ecriture du fichier histphy
+!Config         en jours.
+  ecritphy = 1
+  CALL getin('ecritphy',ecritphy)
+
+!Config  Key  = cycle_diurne
+!Config  Desc = Cycle ddiurne
+!Config  Def  = y
+!Config  Help = Cette option permet d'eteidre le cycle diurne.
+!Config         Peut etre util pour accelerer le code !
+  cycle_diurne = .TRUE.
+  CALL getin('cycle_diurne',cycle_diurne)
+
+!Config  Key  = soil_model
+!Config  Desc = Modele de sol
+!Config  Def  = y
+!Config  Help = Choix du modele de sol (Thermique ?)
+!Config         Option qui pourait un string afin de pouvoir
+!Config         plus de choix ! Ou meme une liste d'options !
+  soil_model = .TRUE.
+  CALL getin('soil_model',soil_model)
+
+!Config  Key  = new_oliq
+!Config  Desc = Nouvelle eau liquide
+!Config  Def  = y
+!Config  Help = Permet de mettre en route la
+!Config         nouvelle parametrisation de l'eau liquide !
+  new_oliq = .TRUE.
+  CALL getin('new_oliq',new_oliq)
+
+!Config  Key  = ok_orodr
+!Config  Desc = Orodr ???
+!Config  Def  = y
+!Config  Help = Y en a pas comprendre !
+!Config         
+  ok_orodr = .TRUE.
+  CALL getin('ok_orodr',ok_orodr)
+
+!Config  Key  =  ok_orolf
+!Config  Desc = Orolf ??
+!Config  Def  = y
+!Config  Help = Connais pas !
+  ok_orolf = .TRUE.
+  CALL getin('ok_orolf', ok_orolf)
+
+!Config  Key  = ok_limitvrai
+!Config  Desc = Force la lecture de la bonne annee
+!Config  Def  = n
+!Config  Help = On peut forcer le modele a lire le
+!Config         fichier SST de la bonne annee. C'est une tres bonne
+!Config         idee, pourquoi ne pas mettre toujours a y ???
+  ok_limitvrai = .FALSE.
+  CALL getin('ok_limitvrai',ok_limitvrai)
+
+!Config  Key  = nbapp_rad
+!Config  Desc = Frequence d'appel au rayonnement
+!Config  Def  = 12
+!Config  Help = Nombre  d'appels des routines de rayonnements
+!Config         par jour.
+  nbapp_rad = 12
+  CALL getin('nbapp_rad',nbapp_rad)
+
+!Config  Key  = iflag_con
+!Config  Desc = Flag de convection
+!Config  Def  = 2
+!Config  Help = Flag  pour la convection les options suivantes existent :
+!Config         1 pour LMD,
+!Config         2 pour Tiedtke,
+!Config         3 pour CCM(NCAR)  
+  iflag_con = 2
+  CALL getin('iflag_con',iflag_con)
+
+!Config  Key  = clon
+!Config  Desc = centre du zoom, longitude
+!Config  Def  = 0
+!Config  Help = longitude en degres du centre 
+!Config         du zoom
+  clonn = 0.
+  CALL getin('clon',clonn)
+
+!Config  Key  = clat
+!Config  Desc = centre du zoom, latitude
+!Config  Def  = 0
+!Config  Help = latitude en degres du centre du zoom
+!Config         
+  clatt = 0.
+  CALL getin('clat',clatt)
+
+!Config  Key  = grossismx 
+!Config  Desc = zoom en longitude
+!Config  Def  = 1.0
+!Config  Help = facteur de grossissement du zoom,
+!Config         selon la longitude
+  grossismxx = 1.0
+  CALL getin('grossismx',grossismxx)
+
+!Config  Key  = grossismy
+!Config  Desc = zoom en latitude
+!Config  Def  = 1.0
+!Config  Help = facteur de grossissement du zoom,
+!Config         selon la latitude
+  grossismyy = 1.0
+  CALL getin('grossismy',grossismyy)
+
+!Config  Key  = fxyhypb
+!Config  Desc = Fonction  hyperbolique
+!Config  Def  = y
+!Config  Help = Fonction  f(y)  hyperbolique  si = .true.  
+!Config         sinon  sinusoidale
+  fxyhypbb = .TRUE.
+  CALL getin('fxyhypb',fxyhypbb)
+
+!Config  Key  = dzoomx
+!Config  Desc = extension en longitude
+!Config  Def  = 0
+!Config  Help = extension en longitude  de la zone du zoom  
+!Config         ( fraction de la zone totale)
+  dzoomxx = 0.0
+  CALL getin('dzoomx',dzoomxx)
+
+!Config  Key  = dzoomy
+!Config  Desc = extension en latitude
+!Config  Def  = 0
+!Config  Help = extension en latitude de la zone  du zoom  
+!Config         ( fraction de la zone totale)
+  dzoomyy = 0.0
+  CALL getin('dzoomy',dzoomyy)
+      
+!Config  Key  = taux
+!Config  Desc = raideur du zoom en  X
+!Config  Def  = 3
+!Config  Help = raideur du zoom en  X
+  tauxx = 3.0
+  CALL getin('taux',tauxx)
+
+!Config  Key  = tauyy
+!Config  Desc = raideur du zoom en  Y
+!Config  Def  = 3
+!Config  Help = raideur du zoom en  Y
+  tauyy = 3.0
+  CALL getin('tauy',tauyy)
+
+!Config  Key  = ysinus
+!Config  IF   = !fxyhypb
+!Config  Desc = Fonction en Sinus
+!Config  Def  = y
+!Config  Help = Fonction  f(y) avec y = Sin(latit.) si = .true. 
+!Config         sinon y = latit.
+  ysinuss = .TRUE.
+  CALL getin('ysinus',ysinuss)
+
+!Config  Key  = clon
+!Config  Desc = centre du zoom, longitude
+!Config  Def  = 0
+!Config  Help = longitude en degres du centre 
+!Config         du zoom
+  clon = 0.
+  CALL getin('clon',clon)
+
+!Config  Key  = clat
+!Config  Desc = centre du zoom, latitude
+!Config  Def  = 0
+!Config  Help = latitude en degres du centre du zoom
+!Config         
+  clat = 0.
+  CALL getin('clat',clat)
+
+!Config  Key  = grossismx 
+!Config  Desc = zoom en longitude
+!Config  Def  = 1.0
+!Config  Help = facteur de grossissement du zoom,
+!Config         selon la longitude
+  grossismx = 1.0
+  CALL getin('grossismx',grossismx)
+
+!Config  Key  = grossismy
+!Config  Desc = zoom en latitude
+!Config  Def  = 1.0
+!Config  Help = facteur de grossissement du zoom,
+!Config         selon la latitude
+  grossismy = 1.0
+  CALL getin('grossismy',grossismy)
+
+!Config  Key  = fxyhypb
+!Config  Desc = Fonction  hyperbolique
+!Config  Def  = y
+!Config  Help = Fonction  f(y)  hyperbolique  si = .true.  
+!Config         sinon  sinusoidale
+  fxyhypb = .TRUE.
+  CALL getin('fxyhypb',fxyhypb)
+
+!Config  Key  = dzoomx
+!Config  Desc = extension en longitude
+!Config  Def  = 0
+!Config  Help = extension en longitude  de la zone du zoom  
+!Config         ( fraction de la zone totale)
+  dzoomx = 0.0
+  CALL getin('dzoomx',dzoomx)
+
+!Config  Key  = dzoomy
+!Config  Desc = extension en latitude
+!Config  Def  = 0
+!Config  Help = extension en latitude de la zone  du zoom  
+!Config         ( fraction de la zone totale)
+  dzoomy = 0.0
+  CALL getin('dzoomy',dzoomy)
+
+!Config  Key  = taux
+!Config  Desc = raideur du zoom en  X
+!Config  Def  = 3
+!Config  Help = raideur du zoom en  X
+  taux = 3.0
+  CALL getin('taux',taux)
+
+!Config  Key  = tauy
+!Config  Desc = raideur du zoom en  Y
+!Config  Def  = 3
+!Config  Help = raideur du zoom en  Y
+  tauy = 3.0
+  CALL getin('tauy',tauy)
+
+!Config  Key  = ysinus
+!Config  IF   = !fxyhypb
+!Config  Desc = Fonction en Sinus
+!Config  Def  = y
+!Config  Help = Fonction  f(y) avec y = Sin(latit.) si = .true. 
+!Config         sinon y = latit.
+  ysinus = .TRUE.
+  CALL getin('ysinus',ysinus)
+
+!!
+!!
+  CALL DATE_AND_TIME(big_ben(1), big_ben(2), big_ben(3), date_time)
+  date_end = date_time(8) + 1000*date_time(7) + 60*1000*date_time(7)
+  WRITE(*,*) 'Time before dump in milli-sec :', date_end - date_start
+!!
+  CALL getin_dump()
+
+!!
+  CALL DATE_AND_TIME(big_ben(1), big_ben(2), big_ben(3), date_time)
+  date_end = date_time(8) + 1000*date_time(7) + 60*1000*date_time(7)
+  WRITE(*,*) 'Total time in milli-sec :', date_end - date_start
+
+END PROGRAM testconfig2
Index: /IOIPSL/tags/v2_2_5/example/testflio.f90
===================================================================
--- /IOIPSL/tags/v2_2_5/example/testflio.f90	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/example/testflio.f90	(revision 5058)
@@ -0,0 +1,341 @@
+PROGRAM testflio
+!-
+!$Id$
+!-
+! This software is governed by the CeCILL license
+! See IOIPSL/IOIPSL_License_CeCILL.txt
+!---------------------------------------------------------------------
+!- This program is an example of how to use "fliocom".
+!---------------------------------------------------------------------
+  USE ioipsl
+  USE defprec
+!-
+  IMPLICIT NONE
+!-
+  INTEGER,PARAMETER :: iimf=3, jjmf=2, llmf=3, itmf=5
+!-
+  REAL,DIMENSION(iimf,jjmf,llmf,itmf) :: forcing
+  REAL,DIMENSION(iimf,jjmf)           :: lonf,latf
+  REAL,DIMENSION(llmf)                :: levf
+  INTEGER,DIMENSION(itmf)             :: itauf
+  REAL :: parf=7.
+!-
+! REAL,DIMENSION(iimf,jjmf,llmf,itmf) :: forcingw
+  REAL,DIMENSION(iimf)                :: lonw
+  REAL,DIMENSION(jjmf)                :: latw
+  REAL,DIMENSION(llmf)                :: levw
+  INTEGER,DIMENSION(itmf)             :: itauw
+! REAL :: parw
+!-
+  CHARACTER(LEN=10),DIMENSION(flio_max_dims) :: f_n_d
+  INTEGER,DIMENSION(flio_max_dims) :: f_l_d,f_i_d
+  INTEGER :: i_t_w,n_d_w,n_a_w
+  INTEGER,DIMENSION(flio_max_var_dims) :: l_d_w,i_d_w
+  LOGICAL :: exv,exa
+  INTEGER :: iim,jjm,llm,ttm
+  INTEGER :: i,j,l,it,fid,id_dom
+  CHARACTER(LEN=25) :: f_n,v_n,c_n,c_tmp=' '
+  INTEGER :: year,month,day
+  REAL :: date,datew,dt,dtw,sec
+  REAL,DIMENSION(10) :: r_w
+  CHARACTER(LEN=30),DIMENSION(:),ALLOCATABLE :: cn_d,cn_v,cn_a
+  CHARACTER(LEN=30) :: cn_u,f_n_c
+!---------------------------------------------------------------------
+!-
+! Create a file for the lateral forcing
+!-
+  DO i=1,iimf
+    DO j=1,jjmf
+      lonf(i,j) = (REAL(i-1)/REAL(MAX(iimf-1,1)))*180.
+      latf(i,j) = (REAL(j-1)/REAL(MAX(jjmf-1,1)))*90.
+    ENDDO
+  ENDDO
+!-
+  levf(1:3) = (/ 0.7, 0.5, 0.3 /)
+!-
+  DO it = 1,itmf
+    itauf(it) = it+10
+    DO i=1,iimf
+      DO j=1,jjmf
+        DO l=1,llmf
+          forcing(i,j,l,it) = 10.*(10.*((10.*it)+l)+j)+i
+        ENDDO
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+  WRITE (*,'(" -----------------------------")')
+  WRITE (*,'(" ------- Using fliocom -------")')
+  WRITE (*,'(" -----------------------------")')
+  WRITE(*,'(/," ",A,3(1X,I5))') &
+ &  'flio_max_files,flio_max_dims,flio_max_var_dims : ', &
+ &   flio_max_files,flio_max_dims,flio_max_var_dims
+!-
+  year = 1997; month = 5; day = 10; sec = 0.;
+  CALL ymds2ju (year,month,day,sec,date)
+  dt = 86400./2.
+  WRITE (*,'(/," ----------------------------------")')
+  WRITE (*,'(/," t_init : ",F11.3)') date
+!-
+! Create a file and add variables and attributes
+!-
+  WRITE (*,'(/," ----------------------------------")')
+!-
+  f_n = 'testflio.nc'
+  f_l_d(1:5) = (/  iimf,  jjmf,  llmf,    -1,   10   /)
+  f_n_d(1:5) = (/ "lon ","lat ","lev ","time","ud1 " /)
+!-
+  CALL flio_dom_set &
+ & (1,1,(/1,2/),(/iimf,jjmf/),(/iimf,jjmf/), &
+ &  (/1,1/),(/iimf,jjmf/),(/0,0/),(/0,0/),"orange",id_dom)
+  WRITE (*,'(/," --> fliocrfd (",A,",...)")') TRIM(f_n)
+  CALL fliocrfd (TRIM(f_n),f_n_d(1:5),f_l_d(1:5),fid, &
+ &               id_dom=id_dom,c_f_n=f_n_c)
+  WRITE (*,'(/,"   created file name : ",A)') TRIM(f_n_c)
+  CALL flio_dom_unset (id_dom)
+!-
+  WRITE (*,'(/," --> fliopstc")')
+  CALL fliopstc (fid, &
+ &  x_axis_2d=lonf(:,:),y_axis_2d=latf(:,:),z_axis=levf(:), &
+ &  t_axis=itauf(:),t_init=date,t_step=dt,t_calendar="gregorian")
+!-
+  CALL fliodefv (fid,'forcing',(/ 1,2,3,4 /), &
+ &               v_t=flio_r4,units='1',long_name='Forcing field')
+  CALL flioputa (fid,'forcing','vecteur_3',(/3.,5.,7./))
+  CALL flioputa (fid,'forcing','text_n',"Text attribute")
+  CALL fliodefv (fid,'my_var_1',(/ 5 /), &
+ &               v_t=flio_r4,units='1',long_name='my_var_1', &
+ &               valid_min=-10.,valid_max=+20.,fillvalue=+50.)
+  CALL fliodefv (fid,'Var_vr4', &
+ &               v_t=flio_r4,units='1',long_name='Var_vr4')
+  CALL flioputa (fid,'Var_vr4','att_1',735)
+  CALL flioputa (fid,'?','Param_a4',REAL(parf,KIND=4))
+  CALL fliodefv (fid,'Var_vr8', &
+ &               v_t=flio_r8,units='1',long_name='Var_vr8')
+  CALL fliocpya (fid,'Var_vr4','att_1',fid,'Var_vr8')
+  CALL flioputa (fid,'?','Param_a8',REAL(parf,KIND=8))
+  CALL fliodefv (fid,'Var_vi2', &
+ &               v_t=flio_i2,units='1',long_name='Var_vi2')
+  CALL flioputv (fid,'forcing',forcing)
+  CALL flioputv (fid,'my_var_1',(/3.,4.,5./),start=(/3/))
+  CALL flioputv (fid,'Var_vr4',parf)
+  CALL flioputv (fid,'Var_vr8',parf)
+  CALL flioputv (fid,'Var_vi2',INT(parf,KIND=i_2))
+!-
+  WRITE (*,'(/," --> flioclo")')
+  CALL flioclo (fid)
+!-
+! Inspect the file
+!-
+  WRITE (*,'(/," ----------------------------------")')
+!-
+  WRITE (*,'(/," --> fliodmpf")')
+  CALL fliodmpf (TRIM(f_n_c))
+!-
+! Open the file and obtain information about the file
+!-
+  WRITE (*,'(/," ----------------------------------")')
+!-
+  WRITE (*,'(/," --> flioopfd")')
+  i = 0; j = 0; l = 0;
+  CALL flioopfd (TRIM(f_n_c),fid,nb_dim=i,nb_var=j,nb_gat=l)
+  WRITE (*,'(" Number of dimensions        in the file : ",I2)') i
+  WRITE (*,'(" Number of variables         in the file : ",I2)') j
+  WRITE (*,'(" Number of global attributes in the file : ",I2)') l
+!-
+  WRITE (*,'(/," --> flioinqf")')
+  CALL flioinqf &
+ & (fid,id_dim=f_i_d,ln_dim=f_l_d)
+  IF (i > 0) THEN
+    WRITE (*,'(" Identifiers of the dimensions :",/,(5(1X,I7),:))') &
+ &    f_i_d(1:MIN(i,SIZE(f_i_d)))
+    WRITE (*,'(" Dimensions :",/,(5(1X,I7),:))') &
+ &    f_l_d(1:MIN(i,SIZE(f_l_d)))
+  ENDIF
+!-
+  WRITE (*,'(/," --> flioinqn")')
+  ALLOCATE(cn_d(i),cn_v(j),cn_a(l))
+  CALL flioinqn (fid,cn_dim=cn_d,cn_var=cn_v,cn_gat=cn_a,cn_uld=cn_u)
+  WRITE (*,'(" Names of the dimensions in the file : ")')
+  DO it=1,i
+    WRITE (*,'("   """,A,"""")') TRIM(cn_d(it))
+  ENDDO
+  WRITE (*,'(" Names of the variables in the file : ")')
+  DO it=1,j
+    WRITE (*,'("   """,A,"""")') TRIM(cn_v(it))
+  ENDDO
+  WRITE (*,'(" Names of the global attributes in the file : ")')
+  DO it=1,l
+    WRITE (*,'("   """,A,"""")') TRIM(cn_a(it))
+  ENDDO
+  WRITE (*,'(" Name of the unlimited dimension : ")')
+  WRITE (*,'("   """,A,"""")') TRIM(cn_u)
+  DEALLOCATE(cn_d,cn_v,cn_a)
+!-
+  WRITE (*,'(/," --> flioqstc")')
+  c_n = "x"
+  CALL flioqstc (fid,TRIM(c_n),exv,v_n)
+  IF (exv) THEN
+    WRITE (*,'(" Name of the """,A,""" coordinate : ",A)') &
+ &   TRIM(c_n),TRIM(v_n)
+    CALL flioinqv &
+ &   (fid,TRIM(v_n),exv,nb_dims=n_d_w,len_dims=l_d_w,nb_atts=n_a_w) 
+    IF (n_d_w > 0) THEN
+      WRITE (*,'(" Number of dimensions : ",I2)') n_d_w
+      WRITE (*,'(" Dimensions :",/,(5(1X,I7),:))') &
+ &      l_d_w(1:MIN(n_d_w,SIZE(l_d_w)))
+      iim=l_d_w(1)
+    ENDIF
+    IF (n_a_w > 0) THEN
+      WRITE (*,'(" Number of attributes : ",I2)') n_a_w
+    ENDIF
+  ELSE
+    WRITE (*,'(" Coordinate """,A,""" not found")') TRIM(c_n)
+  ENDIF
+!-
+  WRITE (*,'(/," --> flioqstc")')
+  c_n = "y"
+  CALL flioqstc (fid,TRIM(c_n),exv,v_n)
+  IF (exv) THEN
+    WRITE (*,'(" Name of the """,A,""" coordinate : ",A)') &
+ &   TRIM(c_n),TRIM(v_n)
+    CALL flioinqv &
+ &   (fid,TRIM(v_n),exv,nb_dims=n_d_w,len_dims=l_d_w,nb_atts=n_a_w) 
+    IF (n_d_w > 0) THEN
+      WRITE (*,'(" Number of dimensions : ",I2)') n_d_w
+      WRITE (*,'(" Dimensions :",/,(5(1X,I7),:))') &
+ &      l_d_w(1:MIN(n_d_w,SIZE(l_d_w)))
+      jjm=l_d_w(n_d_w)
+    ENDIF
+    IF (n_a_w > 0) THEN
+      WRITE (*,'(" Number of attributes : ",I2)') n_a_w
+    ENDIF
+  ELSE
+    WRITE (*,'(" Coordinate """,A,""" not found")') TRIM(c_n)
+  ENDIF
+!-
+  WRITE (*,'(/," --> flioqstc")')
+  c_n = "z"
+  CALL flioqstc (fid,TRIM(c_n),exv,v_n)
+  IF (exv) THEN
+    WRITE (*,'(" Name of the """,A,""" coordinate : ",A)') &
+ &   TRIM(c_n),TRIM(v_n)
+    CALL flioinqv &
+ &   (fid,TRIM(v_n),exv,nb_dims=n_d_w,len_dims=l_d_w,nb_atts=n_a_w) 
+    IF (n_d_w > 0) THEN
+      WRITE (*,'(" Number of dimensions : ",I2)') n_d_w
+      WRITE (*,'(" Dimensions :",/,(5(1X,I7),:))') &
+ &      l_d_w(1:MIN(n_d_w,SIZE(l_d_w)))
+      llm=l_d_w(1)
+    ENDIF
+    IF (n_a_w > 0) THEN
+      WRITE (*,'(" Number of attributes : ",I2)') n_a_w
+    ENDIF
+  ELSE
+    WRITE (*,'(" Coordinate """,A,""" not found")') TRIM(c_n)
+  ENDIF
+!-
+  WRITE (*,'(/," --> flioqstc")')
+  c_n = "t"
+  CALL flioqstc (fid,TRIM(c_n),exv,v_n)
+  IF (exv) THEN
+    WRITE (*,'(" Name of the """,A,""" coordinate : ",A)') &
+ &   TRIM(c_n),TRIM(v_n)
+    CALL flioinqv &
+ &   (fid,TRIM(v_n),exv,nb_dims=n_d_w,len_dims=l_d_w,nb_atts=n_a_w) 
+    IF (n_d_w > 0) THEN
+      WRITE (*,'(" Number of dimensions : ",I2)') n_d_w
+      WRITE (*,'(" Dimensions :",/,(5(1X,I7),:))') &
+ &      l_d_w(1:MIN(n_d_w,SIZE(l_d_w)))
+      ttm=l_d_w(1)
+    ENDIF
+    IF (n_a_w > 0) THEN
+      WRITE (*,'(" Number of attributes : ",I2)') n_a_w
+    ENDIF
+  ELSE
+    WRITE (*,'(" Coordinate """,A,""" not found")') TRIM(c_n)
+  ENDIF
+!-
+  WRITE (*,'(/," --> fliogstc")')
+  CALL fliogstc (fid, &
+ &  x_axis=lonw,y_axis=latw,z_axis=levw, &
+ &  t_axis=itauw(:),t_init=datew,t_step=dtw,t_calendar=c_tmp, &
+ &  t_count=3)
+  WRITE (*,'(" x_axis :",/,(5(1X,1PE11.3),:))') lonw(1:iim)
+  WRITE (*,'(" y_axis :",/,(5(1X,1PE11.3),:))') latw(1:jjm)
+  WRITE (*,'(" z_axis :",/,(5(1X,1PE11.3),:))') levw(1:llm)
+  WRITE (*,'(" t_axis(1:3) :",/,(5(1X,I5),:))') itauw(1:3)
+  WRITE (*,'(" t_calendar  : ",A)') '"'//TRIM(c_tmp)//'"'
+  WRITE (*,'(" t_init,t_step :",(2(1X,1PE15.7),:))') datew,dtw
+!-
+  v_n = 'forcing'
+  WRITE (*,'(/," Variable : """,A,"""")') TRIM(v_n)
+  WRITE (*,'(" --> flioinqv(...,""",A,""",...)")') TRIM(v_n)
+  CALL flioinqv &
+ & (fid,TRIM(v_n),exv,v_t=i_t_w, &
+ &  nb_dims=n_d_w,len_dims=l_d_w,id_dims=i_d_w,nb_atts=n_a_w)
+  IF (exv) THEN
+    WRITE (*,'(" External type        : ",I2)') i_t_w
+    WRITE (*,'(" Number of dimensions : ",I2)') n_d_w
+    WRITE (*,'(" Dimensions :",/,5(1X,I7,:))') l_d_w(1:n_d_w)
+    WRITE (*,'(" Identifiers :",/,5(1X,I7,:))') i_d_w(1:n_d_w)
+    WRITE (*,'(" Number of attributes : ",I2)') n_a_w
+!---
+    IF (n_a_w > 0) THEN
+      ALLOCATE(cn_a(5))
+      CALL flioinqv (fid,TRIM(v_n),exv,cn_atts=cn_a)
+      WRITE (*,'(" Names of the attributes : ")')
+      DO it=1,n_a_w
+        WRITE (*,'("   """,A,"""")') TRIM(cn_a(it))
+      ENDDO
+      DEALLOCATE(cn_a)
+    ENDIF
+!---
+    WRITE (*, &
+ &   '(" --> flioinqa(...,""",A,""",""text_n"",...)")') TRIM(v_n)
+    CALL flioinqa (fid,TRIM(v_n),"text_n",exa,a_l=l)
+    IF (exa) THEN
+      WRITE (*,'("  len(text_n) : ",I3)') l
+!-----
+      WRITE (*,'(" --> fliogeta(...,""",A,""",""text_n"",...)")') &
+ &     TRIM(v_n)
+      CALL fliogeta (fid,TRIM(v_n),"text_n",c_tmp)
+      WRITE (*,'("  ""text_n"" : """,A,"""")') TRIM(c_tmp)
+    ELSE
+      WRITE (*,'(" Attribute not found")')
+    ENDIF
+  ELSE
+    WRITE (*,'(" Variable not found")')
+  ENDIF
+!-
+  v_n = 'my_var_1'
+  WRITE (*,'(/," Variable : """,A,"""")') TRIM(v_n)
+  WRITE (*,'(" --> flioinqv(...,""",A,""",...)")') TRIM(v_n)
+  CALL flioinqv &
+ & (fid,TRIM(v_n),exv, &
+ &  nb_dims=n_d_w,len_dims=l_d_w,id_dims=i_d_w,nb_atts=n_a_w)
+  IF (exv) THEN
+    WRITE (*,'(" Number of dimensions : ",I2)') n_d_w
+    WRITE (*,'(" Dimensions :",/,5(1X,I7,:))') l_d_w(1:n_d_w)
+    WRITE (*,'(" Identifiers :",/,5(1X,I7,:))') i_d_w(1:n_d_w)
+    WRITE (*,'(" Number of attributes : ",I2)') n_a_w
+    WRITE (*,'(" --> fliogetv(...,""",A,""",...)")') TRIM(v_n)
+    CALL fliogetv (fid,TRIM(v_n),r_w)
+    WRITE (*, &
+ &   '("  Values :",/,(5(1X,1PE11.3),:))') r_w
+    i = 2; j = 5;
+    WRITE (*,'(" --> fliogetv(...,",A, &
+ &             ",start=(/",I2,"/),count=(/",I2,"/))")') &
+ &   '"'//TRIM(v_n)//'"',i,j
+    CALL fliogetv (fid,TRIM(v_n),r_w,start=(/i/),count=(/j/))
+    WRITE (*, &
+ &   '("  Values(",I2,":",I2,") :",/,(5(1X,1PE11.3),:))') &
+ &   i,i+j-1,r_w(1:j)
+  ENDIF
+!-
+  WRITE (*,'(/," --> flioclo")')
+  CALL flioclo (fid)
+!-
+  WRITE (*,'(/," ----------------------------------")')
+!-------------------
+END PROGRAM testflio
Index: /IOIPSL/tags/v2_2_5/example/testhist1.f90
===================================================================
--- /IOIPSL/tags/v2_2_5/example/testhist1.f90	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/example/testhist1.f90	(revision 5058)
@@ -0,0 +1,134 @@
+PROGRAM testhist1
+!-
+!$Id$
+!-
+! This software is governed by the CeCILL license
+! See IOIPSL/IOIPSL_License_CeCILL.txt
+!---------------------------------------------------------------------
+!- This program provide a an example of the basic usage of HIST.
+!- No secial features are used but just the basics.
+!---------------------------------------------------------------------
+  USE ioipsl
+!
+  IMPLICIT NONE
+!
+  INTEGER,PARAMETER :: iim=96, jjm=96, llm=12, nbreg=200
+!
+  REAL :: champ1(iim,jjm), champ2(iim,jjm,llm), champ3(iim,jjm)
+  REAL :: lon(iim,jjm),lat(iim,jjm), lev(llm)
+  REAL :: lon_test(iim),lat_test(jjm)
+  REAL :: x
+!
+  INTEGER :: i, j, l, id, it, ij, sig_id, hori_id
+  INTEGER :: day=15, month=2, year=1997
+  INTEGER :: itau=0, index(nbreg), start
+!
+  REAL :: julday
+  REAL :: deltat=60., dt_wrt, dt_op
+  CHARACTER(LEN=20) :: histname
+!
+  REAL :: pi=3.1415
+!---------------------------------------------------------------------
+!-
+! 1.0 Define a few variables we will need. These are the coordinates
+!     the file name and the date.
+!-
+  DO i=1,iim
+    DO j=1,jjm
+      lon(i,j) = ((float(iim/2)+0.5)-float(i))*pi/float(iim/2) &
+ &              *(-1.)*180./pi
+      lat(i,j) = 180./pi*ASIN(((float(jjm/2)+0.5)-float(j)) &
+ &              /float(jjm/2))
+    ENDDO
+  ENDDO
+!-
+  lon_test(:) = lon(:,1)
+  lat_test(:) = lat(1,:)
+!-
+  DO l=1,llm
+    lev(l) = REAL(l)/llm
+  ENDDO
+!
+  histname = 'testhist1.nc'
+!-
+! 1.1 The chosen date is 15 Feb. 1997 as stated above.
+!     It has to be transformed into julian days using
+!     the calendar provided by IOIPSL.
+!-
+  CALL ymds2ju(year, month, day, 0.,julday)
+  dt_wrt = 3*deltat
+  dt_op = 3*deltat
+!-
+! 2.0 Do all the declarations for hist. That is define the file,
+!     the vertical coordinate and the variables in the file.
+!-
+  CALL ioconf_modname('testhist1 produced this file')
+!-
+  CALL histbeg (histname,iim,lon_test,jjm,lat_test, &
+ &              1,iim,1,jjm,itau,julday,deltat,hori_id,id)
+!-
+  CALL histvert (id,"sigma","Sigma levels"," ",llm,lev,sig_id,pdirect="up")
+!-
+  CALL histdef (id,"champ1","Some field","m", &
+ &       iim,jjm,hori_id,1,1,1,-99,32,"inst(scatter(x))", &
+ &       dt_op,dt_wrt,var_range=(/1.,-1./),standard_name='thickness')
+!-
+  CALL histdef (id,"champ2","Another field","m", &
+ &       iim,jjm,hori_id,llm,1,llm,sig_id,32,"t_max(max(x,1.0)*2)", &
+ &       deltat,dt_wrt,var_range=(/0.,90./),standard_name='thickness')
+!-
+  CALL histdef (id,"champ3","A field without time","m", &
+ &       iim,jjm,hori_id,1,1,1,-99, 32,"once", &
+ &       deltat,dt_wrt,standard_name='thickness')
+!-
+  CALL histend (id)
+!-
+! 2.1 The filed we are going to write are computes
+!-
+  x = 10.
+  CALL RANDOM_NUMBER(HARVEST=x)
+  CALL RANDOM_NUMBER(champ1)
+  champ3 = champ1
+  DO l=1,llm
+    champ2(:,:,l) = champ1*l
+  ENDDO
+!-
+! 3.0 Start the time steping and write the data as we go along.
+!-
+  start = 1
+!-
+  DO it=1,12
+!---
+!   3.1 In the 2D filed we will have a set of random numbers
+!       which move through the map.
+!---
+    ij = 0
+    DO j=1,nbreg/10
+      DO i=1,10
+        ij = ij+1
+        index(ij) = iim*(j+20)+(i+start)
+      ENDDO
+    ENDDO
+!---
+    IF (start < iim-10) THEN
+      start = start+10
+    ELSE
+      start = 1
+    ENDIF
+!---
+    itau = itau + 1
+!---
+!   3.2 Pass the data to HIST for operation and writing.
+!---
+    CALL histwrite (id,"champ1",itau,champ1,nbreg,index)
+    CALL histwrite (id,"champ2",itau,champ2,iim*jjm*llm,index)
+    CALL histwrite (id,"champ3",itau,champ3,iim*jjm,index)
+    champ1 = champ1+1
+    champ2 = champ2+2
+  ENDDO
+!-
+! 4.0 The HIST routines are ended and netCDF is closed
+!-
+  CALL histclo ()
+!--------------------
+END PROGRAM testhist1
Index: /IOIPSL/tags/v2_2_5/example/testhist2.f90
===================================================================
--- /IOIPSL/tags/v2_2_5/example/testhist2.f90	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/example/testhist2.f90	(revision 5058)
@@ -0,0 +1,135 @@
+PROGRAM testhist2
+!-
+!$Id$
+!-
+! This software is governed by the CeCILL license
+! See IOIPSL/IOIPSL_License_CeCILL.txt
+!---------------------------------------------------------------------
+!- This program provide a an example of the basic usage of HIST.
+!- Here the test the time sampling and averaging. Thus a long
+!- time-series is produced and sampled in different ways.
+!---------------------------------------------------------------------
+  USE ioipsl
+!
+  IMPLICIT NONE
+!
+  INTEGER,PARAMETER :: iim=12,jjm=10,llm=2
+!
+  REAL :: champ1(iim,jjm), champ(iim,jjm), champ2(iim,jjm)
+  REAL :: lon(iim,jjm),lat(iim,jjm), lev(llm)
+  REAL :: x
+!
+  INTEGER :: i, j, l, id, id2, sig_id, hori_id, it
+  INTEGER :: day=1, month=1, year=1997
+  INTEGER :: itau=0, start, index(1)
+!
+  REAL :: julday, un_mois, un_an
+  REAL :: deltat=86400, dt_wrt, dt_op, dt_wrt2, dt_op2
+  CHARACTER(LEN=20) :: histname
+!
+  REAL :: pi=3.1415
+!---------------------------------------------------------------------
+!-
+! 0.0 Choose a 360 days calendar
+!-
+  CALL ioconf_calendar('gregorian')
+!-
+! 1.0 Define a few variables we will need.
+!     These are the coordinates the file name and the date.
+!-
+  DO i=1,iim
+    DO j=1,jjm
+      lon(i,j) = ((float(iim/2)+0.5)-float(i))*pi/float(iim/2) &
+ &              *(-1.)*180./pi
+      lat(i,j) = 180./pi * ASIN(((float(jjm/2)+0.5)-float(j)) &
+ &              /float(jjm/2))
+    ENDDO
+  ENDDO
+!-
+  DO l=1,llm
+    lev(l) = float(l)/llm
+  ENDDO
+!-
+  histname = 'testhist2.nc'
+!-
+! 1.1 The chosen date is 15 Feb. 1997 as stated above.
+!     It has to be transformed into julian days using
+!     the calendar provided by IOIPSL.
+!-
+  CALL ymds2ju(year, month, day, 0.,julday)
+  CALL ioget_calendar(un_an)
+  un_mois = un_an/12.
+  dt_wrt = un_mois*deltat
+  dt_op = deltat
+  dt_wrt2 = -1.
+  dt_op2 = deltat
+!-
+! 2.0 Do all the declarations for hist. That is define the file,
+!     the vertical coordinate and the variables in the file.
+!     Monthly means are written to test this feature
+!-
+  CALL ioconf_modname ('testhist2 produced this file')
+!-
+  CALL histbeg (histname,iim,lon,jjm,lat, &
+ &       1,iim,1,jjm,itau,julday,deltat,hori_id,id)
+!-
+  CALL histvert (id,"sigma","Sigma levels"," ",llm,lev,sig_id,pdirect="up")
+!-
+  CALL histdef (id,"champ1","Some field","m",iim,jjm,hori_id, &
+ &  1,1,1,-99,32,"t_sum",dt_op,dt_wrt,standard_name='thickness')
+!-
+  CALL histdef (id,"champ2","summed field","m",iim,jjm,hori_id, &
+ &  1,1,1,-99,32,"t_sum",dt_op,dt_wrt,standard_name='thickness')
+!-
+  CALL histend (id)
+!-
+! Open a second file which will do monthly means using the -1 notation.
+!-
+  histname = 'testhist2_bis.nc'
+  CALL histbeg (histname,iim,lon,jjm,lat, &
+ &       1,iim,1,jjm,itau,julday,deltat,hori_id,id2)
+!-
+  CALL histvert (id2,"sigma","Sigma levels"," ",llm,lev,sig_id,pdirect="up")
+!-
+  CALL histdef (id2,"champ1","Some field","m",iim,jjm,hori_id, &
+ &  1,1,1,-99,32,"t_sum",dt_op2,dt_wrt2,standard_name='thickness')
+!-
+  CALL histdef (id2,"champ2","summed field","m",iim,jjm,hori_id, &
+ &  1,1,1,-99,32,"t_sum",dt_op2,dt_wrt2,standard_name='thickness')
+!-
+  CALL histend (id2)
+!-
+! 2.1 The filed we are going to write are computes
+!-
+  CALL RANDOM_NUMBER(HARVEST=x)
+  CALL RANDOM_NUMBER(champ)
+  champ = champ*2*pi
+  champ1 = sin(champ)
+  champ2(:,:) = 1.
+!-
+! 3.0 Start the time steping and write the data as we go along.
+!-
+  start = 1
+!-
+  DO it=1,730
+!---
+!   3.1 In the 2D filed we will have a set of random numbers
+!       which move through the map.
+!---
+    itau = itau+1
+!---
+!   3.2 Pass the data to HIST for operation and writing.
+!---
+    CALL histwrite (id, "champ1",itau,champ1,iim*jjm,index)
+    CALL histwrite (id2,"champ1",itau,champ1,iim*jjm,index)
+    CALL histwrite (id, "champ2",itau,champ2,iim*jjm,index)
+    CALL histwrite (id2,"champ2",itau,champ2,iim*jjm,index)
+!---
+    champ1 = sin((it+1)*champ)
+  ENDDO
+!-
+! 4.0 The HIST routines are ended and netCDF is closed
+!-
+  CALL histclo ()
+!--------------------
+END PROGRAM testhist2
Index: /IOIPSL/tags/v2_2_5/example/testopp.f90
===================================================================
--- /IOIPSL/tags/v2_2_5/example/testopp.f90	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/example/testopp.f90	(revision 5058)
@@ -0,0 +1,46 @@
+PROGRAM testopp
+!-
+!$Id$
+!-
+! This software is governed by the CeCILL license
+! See IOIPSL/IOIPSL_License_CeCILL.txt
+!---------------------------------------------------------------------
+!- This program allows to test the syntaxic analyzer.
+!---------------------------------------------------------------------
+   USE mathelp
+!-
+   IMPLICIT NONE
+!-
+   INTEGER,PARAMETER :: nbopp_max=10
+   REAL,PARAMETER :: missing_val=1.e20
+!- Please list here all the operation you wish to test.
+!- Do not forget to change the value of nbtest.
+   INTEGER,PARAMETER :: nbtest=3
+   CHARACTER(LEN=30),DIMENSION(nbtest) :: test_opp = &
+  &  (/ "t_max(gather(x*2))            ", &
+  &     "(inst(sqrt(max(X,0)*2.0)))    ", &
+  &     "(once)                        " /)
+!-
+   CHARACTER(LEN=80) :: opp
+   CHARACTER(LEN=50) :: ex_topps = 'ave, inst, t_min, t_max, once'
+   REAL,DIMENSION(nbopp_max) :: tmp_scal
+   CHARACTER(LEN=7),DIMENSION(nbopp_max) :: tmp_sopp
+   CHARACTER(LEN=7) :: tmp_topp
+   INTEGER :: nbopp,i,io
+!---------------------------------------------------------------------
+   DO io=1,nbtest
+     opp = test_opp(io)
+     WRITE(*,*) '-------------------------'
+     WRITE(*,*) ' '
+     WRITE(*,*) 'String to be analyzed : ',TRIM(opp)
+     CALL buildop (TRIM(opp),ex_topps,tmp_topp,missing_val, &
+ &                 tmp_sopp,tmp_scal,nbopp)
+!-
+     WRITE(*,*) 'Time operation   : ',TRIM(tmp_topp)
+     WRITE(*,*) 'Other operations : ',nbopp
+     DO i=1,nbopp
+       WRITE(*,*) ' ',i,' opp : ',tmp_sopp(i),' scalar : ',tmp_scal(i)
+     ENDDO
+   ENDDO
+!------------------
+END PROGRAM testopp
Index: /IOIPSL/tags/v2_2_5/example/testrest.f90
===================================================================
--- /IOIPSL/tags/v2_2_5/example/testrest.f90	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/example/testrest.f90	(revision 5058)
@@ -0,0 +1,129 @@
+PROGRAM testrest
+!-
+!$Id$
+!-
+! This software is governed by the CeCILL license
+! See IOIPSL/IOIPSL_License_CeCILL.txt
+!---------------------------------------------------------------------
+!- This program provide a an example of the basic usage of REST.
+!- Here the test the time sampling and averaging. Thus a long
+!- time-series is produced and sampled in different ways.
+!---------------------------------------------------------------------
+  USE ioipsl
+!
+  IMPLICIT NONE
+!
+  INTEGER :: iim,jjm,llm
+  PARAMETER (iim=12,jjm=10,llm=2)
+!
+  REAL :: champ1(iim,jjm,llm),champ2(iim,jjm,llm+1),champ3(iim,jjm,llm)
+  REAL :: champ4(iim,jjm)
+  REAL :: champ_read(iim,jjm,llm)
+  REAL :: lon(iim,jjm),lat(iim,jjm),lev(llm)
+!
+  INTEGER :: i,j,l,fid
+  INTEGER :: day=1,month=1,year=1997,itau=1
+!
+  REAL :: julday,un_mois,un_an
+  REAL :: deltat=86400,dt_wrt,dt_op,dt_wrt2,dt_op2
+  CHARACTER*20 :: fnamein,fnameout
+!
+  REAL :: pi=3.1415
+!---------------------------------------------------------------------
+!-
+! 0.0 Choose a  gregorian calendar
+!-
+  CALL ioconf_calendar ('gregorian')
+!-
+! 1.0 Define a few variables we will need.
+!     These are the coordinates the file name and the date.
+!-
+  DO i=1,iim
+    DO j=1,jjm
+      lon(i,j) = &
+ &     ((float(iim/2)+0.5)-float(i))*pi/float(iim/2)*(-1.)*180./pi
+      lat(i,j) = &
+ &     (180./pi)*ASIN(((float(jjm/2)+0.5)-float(j))/float(jjm/2))
+    ENDDO
+  ENDDO
+!-
+  DO l=1,llm
+    lev(l) = float(l)/llm
+  ENDDO
+!-
+! 1.1 The chosen date is 15 Feb. 1997 as stated above. It has to be 
+!     transformed into julian days using the calendar provided by 
+!     IOIPSL.
+!-
+  CALL ymds2ju (year,month,day,0.,julday)
+  CALL ioget_calendar (un_an)
+  un_mois = un_an/12.
+  dt_wrt = un_mois*deltat
+  dt_op = deltat
+  dt_wrt2 = -1.
+  dt_op2 = deltat
+!-
+  fnamein = 'NONE'
+  fnameout = 'restfile'
+!-
+! 2.0 Create a restart file from nothing !
+!-
+  CALL restini (fnamein,iim,jjm,lon,lat,llm,lev,fnameout, &
+ &              itau,julday,deltat,fid)
+!-
+  champ1(:,:,:) = ASIN(1.0)
+  champ2(:,:,:) = EXP(ASIN(1.0))
+!-
+  CALL ioconf_setatt ('units','1')
+  CALL ioconf_setatt ('long_name','Tests 1 for a real variable')
+  CALL restput (fid,'test1',iim,jjm,llm,itau,champ1)
+!-
+  CALL ioconf_setatt ('units','1')
+  CALL ioconf_setatt ('long_name','Tests 2 for a real variable')
+  CALL restput (fid,'test2',iim,jjm,llm+1,itau,champ2)
+!-
+  CALL restclo ()
+!-
+  WRITE(*,*) '============== FIRST FILE CLOSED =============='
+!-
+!  3.0 Reopen the restart file and check that the values read are correct
+!-
+  fnamein = 'restfile'
+  fnameout = 'restfilebis'
+!-
+  CALL restini (fnamein,iim,jjm,lon,lat,llm,lev,fnameout, &
+ &              itau,julday,deltat,fid)
+!-
+  CALL restget (fid,'test1',iim,jjm,llm,itau,.FALSE.,champ_read)
+!-
+  itau = itau+10
+  CALL restput (fid,'test1',iim,jjm,llm,itau,champ_read)
+  CALL restput (fid,'test2',iim,jjm,llm+1,itau,champ2)
+!-
+  itau = itau+10
+  champ3(:,:,:) = champ_read(:,:,:)+champ2(:,:,1:llm) 
+  CALL restput (fid,'test1',iim,jjm,llm,itau,champ3)
+!-
+  CALL restclo ()
+!-
+  WRITE(*,'(a25,e36.30)') 'The input data : ',champ1(1,1,1)
+  WRITE(*,'(a25,e36.30)') 'The restart data : ',champ_read(1,1,1)
+!-
+!  4.0 Reopen the restart file and add another time step
+!-
+  fnamein = 'restfilebis'
+  fnameout = 'restfilebis'
+!-
+  CALL restini (fnamein,iim,jjm,lon,lat,llm,lev,fnameout, &
+ &              itau,julday,deltat,fid)
+!-
+  itau = itau+10
+  CALL restput (fid,'test1',iim,jjm,llm,itau,champ1)
+  CALL ioconf_setatt ('units','1')
+  CALL ioconf_setatt ('long_name', &
+ &                    'Test a variable with another dimension')
+  CALL restput (fid,'test4',iim,jjm,0,itau,champ4)
+!-
+  CALL restclo ()
+!-------------------
+END PROGRAM testrest
Index: /IOIPSL/tags/v2_2_5/ins_m_prec
===================================================================
--- /IOIPSL/tags/v2_2_5/ins_m_prec	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/ins_m_prec	(revision 5058)
@@ -0,0 +1,93 @@
+#!/bin/ksh
+# $Id$
+#---------------------------------------------------------------------
+#- ins_m_prec - Installation of the precision module
+#---------------------------------------------------------------------
+function ins_m_prec_Usage
+{
+echo "
+ins_m_prec install the precision module in IOIPSL/src
+
+Usage :
+  ${b_n} [-h] | [-v] [-i i_prec] [-r r_prec]
+
+Options :
+  -h        : help
+  -v        : verbose mode
+  -i i_prec : integer precision 4/8
+  -r r_prec : real    precision 4/8
+
+Defaults :
+  -i 4 -r 8
+
+Example for default precision :
+  ins_m_prec
+
+Example for INTEGER 4 and REAL 4 precision :
+  ins_m_prec -i 4 -r 4
+"
+}
+#-    dirname     and      basename
+d_n=$(dirname ${0}); b_n=$(basename ${0});
+#- Retrieving and validation of the options
+x_v='silencious'; x_i=""; x_r="";
+while getopts :hvi:r: V
+  do
+    case ${V} in
+      (h)  ins_m_prec_Usage; exit 0;;
+      (v)  x_v='verbose';;
+      (i)  x_i=${OPTARG};;
+      (r)  x_r=${OPTARG};;
+      (:)  echo ${b_n}" : -"${OPTARG}" option : missing value" 1>&2;
+           exit 2;;
+      (\?) echo ${b_n}" : -"${OPTARG}" option : not supported" 1>&2;
+           exit 2;;
+    esac
+  done
+shift $(($OPTIND-1));
+#-
+# INTEGER and REAL precision
+#-
+case ${x_i} in
+  ( 4 | 8 ) ;;
+  ("") x_i='4';;
+  (*)
+    echo 'Invalid INTEGER precision requested' 1>&2;
+    echo ${x_i}' (must be 4 or 8)' 1>&2;
+    exit 1;;
+esac
+case ${x_r} in
+  ( 4 | 8 ) ;;
+  ("") x_r='8';;
+  (*)
+    echo 'Invalid REAL precision requested' 1>&2;
+    echo ${x_r}' (must be 4 or 8)' 1>&2;
+    exit 1;;
+esac
+#-
+[[ ${x_v} = 'verbose' ]] && \
+  { echo "\nInstallation of the precision module";
+    echo '--- Integer precision  : '${x_i};
+    echo '--- Real    precision  : '${x_r}; }
+#-
+dp_b='src/def.prec'
+[[ ! -f "${dp_b}" ]] && { echo "${dp_b} unreachable ..."; exit 3; }
+sed -e "s/i_std=i_?/i_std=i_${x_i}/" \
+    -e "s/r_std=r_?/r_std=r_${x_r}/" ${dp_b} > wmpf.$$;
+#-
+# Create the module "defprec.f90" or update it
+# if the default precision has been modified
+#-
+dp_p='src/defprec.f90';
+diff -q wmpf.$$ ${dp_p} > /dev/null 2>&1; r_c=${?};
+[[ ${r_c} != 0 ]] && cp -p wmpf.$$ ${dp_p};
+[[ ${x_v} = 'verbose' ]] && \
+ { echo " ";
+  { [[ ${r_c} = 0 ]] && echo ${dp_p} 'already up to date'; } || \
+  { [[ ${r_c} = 1 ]] && echo 'Update '${dp_p}; } || \
+  { [[ ${r_c} = 2 ]] && echo 'Create '${dp_p}; }
+  echo " "; }
+#-
+unset dp_b dp_p x_v x_i x_r; rm -f wmpf.$$;
+#-
+exit 0;
Index: /IOIPSL/tags/v2_2_5/makeioipsl_fcm
===================================================================
--- /IOIPSL/tags/v2_2_5/makeioipsl_fcm	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/makeioipsl_fcm	(revision 5058)
@@ -0,0 +1,148 @@
+#!/bin/bash
+export ROOT=$PWD
+export PATH=$ROOT/tools/FCM_V1.2/bin:${PATH}
+
+compil_mode="prod"
+
+job=1
+full_defined="FALSE"
+arch_defined="FALSE"
+arch_path="arch"
+arch_default_path="arch"
+arch_path_defined="FALSE"
+CPP_KEY="CPP_NONE" 
+
+while (($# > 0)) ; do
+  case $1 in
+      "-h") cat <<end_help
+########################################################################
+# Usage of the script makeioipsl_fcm
+#
+# makeioipsl_fcm compiles IOIPSL using the Fcm software developed
+# by the Hadley Centre. Fcm is stored in tools directory. 
+# Platform specific compile options are found in the arch directory. 
+########################################################################
+
+Usage:
+
+./makeioipsl_fcm -arch XXX [Options] 
+
+
+Mandatory argument:
+
+-arch XXX  : name of the archicture file containg platform dependent compile options. 
+             The files arch/arch-XXX.fcm and arch/arch-XXX.path must exist. If the 
+             file arch/arch-XXX.env exist, it'll be sourced before compilation starts.
+
+Main options:
+[-h]             : show this help
+[ -full ]        : activate full recompiling 
+[ -arch_path XXX ] : directory of the archicture file containg platform dependent compile options. 
+                     Default is ./arch directory. If the files are not found in the folder given by arch_path, 
+                     the default ./arch folder will be used. 
+
+Options for optimization, choose only one of the following
+[ -prod ]        : compilation for production (all optimization)
+[ -dev ]         : compilation for development (low optimization and -g)
+[ -debug ]       : compilation for debugging (no optmization and all debug options)
+
+[ -job x ]       : activate parallel compiling on x task, default is 1 task
+
+
+end_help
+	  exit;;
+      "-prod") 	      compil_mode="prod" ; shift ;;
+      "-dev") 	      compil_mode="dev" ; shift ;;
+      "-debug")       compil_mode="debug" ; shift ;;
+      "-arch") 	      arch=$2 ; arch_defined="TRUE"; shift ; shift ;;
+      "-arch_path")   arch_path=$2 ; arch_path_defined="TRUE"; shift ; shift ;;
+      "-job") 	      job=$2 ; shift ; shift;;
+      "-full") 	      full_defined="TRUE" ; shift ;;
+      *)
+	  code="$1" ; shift ;;
+  esac
+done
+
+# Create module defprec.90 using def.prec
+# Double precision is used (default mode)
+./ins_m_prec
+
+# Define arch files
+rm -f .void_file
+echo > .void_file
+rm -rf .void_dir
+mkdir .void_dir
+
+if [[ "$arch_defined" == "TRUE" ]] ; then
+  rm -f arch.path
+  rm -f arch.fcm
+  rm -f arch.env
+
+  if [ -f $arch_path/arch-${arch}.path ] ;   then
+    ln -s $arch_path/arch-${arch}.path arch.path
+  elif [ -f $arch_default_path/arch-${arch}.path ] ;  then
+    ln -s $arch_default_path/arch-${arch}.path arch.path
+  else
+    echo No arch.path is found. Exit now.
+    exit 1
+  fi
+        
+  if [ -f $arch_path/arch-${arch}.fcm ] ; then
+    ln -s $arch_path/arch-${arch}.fcm arch.fcm
+  elif [ -f $arch_default_path/arch-${arch}.fcm ] ; then
+    ln -s $arch_default_path/arch-${arch}.fcm arch.fcm
+  fi
+
+  if [ -f $arch_path/arch-${arch}.env ] ; then
+    ln -s $arch_path/arch-${arch}.env arch.env
+  elif [ -f $arch_default_path/arch-${arch}.env ] ; then
+    ln -s $arch_default_path/arch-${arch}.env arch.env
+  else
+    ln -s .void_file arch.env
+  fi
+  source arch.env
+  source arch.path
+else
+  echo "You must define the architecture files using makeioipsl_fcm -arch XXX"
+  echo "Show help using: ./makeioipsl_fcm -h"
+  exit 1
+fi
+LD_FLAGS="%BASE_LD"
+
+if [[ "$compil_mode" == "prod" ]] ; then
+  COMPIL_FFLAGS="%PROD_FFLAGS"
+elif [[ "$compil_mode" == "dev" ]] ; then
+  COMPIL_FFLAGS="%DEV_FFLAGS"
+elif [[ "$compil_mode" == "debug" ]] ; then
+  COMPIL_FFLAGS="%DEBUG_FFLAGS"
+fi
+
+
+IOIPSL_LIB="$NETCDF_LIBDIR $NETCDF_LIB $HDF5_LIBDIR $HDF5_LIB"
+
+rm -f config.fcm
+
+echo "%COMPIL_FFLAGS $COMPIL_FFLAGS $NETCDF_INCDIR" >> config.fcm
+echo "%LD_FLAGS $LD_FLAGS" >> config.fcm
+echo "%CPP_KEY $CPP_KEY" >> config.fcm
+echo "%LIB $IOIPSL_LIB">> config.fcm
+
+if [[ "$full_defined" == "TRUE" ]] ; then
+  ./build.sh --job $job --full
+else
+  ./build.sh --job $job
+fi
+err=$?
+
+# Check error message from fcm build
+if [ $err != 0 ] ; then
+  # Error found
+  echo makeioipsl_fcm: Error in compiling IOIPSL : $err  
+  exit 1
+fi
+
+## Link folders created during compilation to the base directory
+rm -rf bin lib inc
+ln -s build/lib lib
+ln -s build/inc inc
+mkdir bin ; ln -s ../build/bin/rebuild bin/rebuild ; ln -s ../build/bin/flio_rbld.exe bin/flio_rbld
Index: /IOIPSL/tags/v2_2_5/rebuild/AA_make
===================================================================
--- /IOIPSL/tags/v2_2_5/rebuild/AA_make	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/rebuild/AA_make	(revision 5058)
@@ -0,0 +1,35 @@
+#-
+#- $Id: AA_make 53 2007-06-06 15:27:26Z bellier $
+#-
+#-Q- sxdkrz F_O = $(F_D) $(F_P) -C vsafe -size_t64 -I $(MODDIR)
+#-Q- eshpux F_O = $(F_D) $(F_P) -C vsafe -I $(MODDIR)
+#---------------------------------------------------------------------
+#- Create "rebuild" related to IOIPSL
+#---------------------------------------------------------------------
+
+SXMODEL_LIB = $(MODEL_LIB)
+ifeq ($(L_X),1)
+SXMODEL_LIB = $(LIBDIR)/libsxioipsl.a
+endif
+
+rbld: libioipsl $(BINDIR)/rebuild $(BINDIR)/flio_rbld
+	@echo rebuild is OK
+
+$(BINDIR)/rebuild: ./rebuild
+	cp -p ./rebuild $(BINDIR)/rebuild
+	chmod a+rx $(BINDIR)/rebuild
+
+$(BINDIR)/flio_rbld: $(SXMODEL_LIB) flio_rbld.f90
+	$(F_C) $(F_O) -I$(NCDF_INC) flio_rbld.f90
+	$(F_L) flio_rbld.o -o $(BINDIR)/flio_rbld \
+               $(L_O) $(SXMODEL_LIB) $(NCDF_LIB)
+	chmod a+rx $(BINDIR)/flio_rbld
+	$(RM) flio_rbld.o
+
+libioipsl:
+	(cd ../src; $(M_K) -f Makefile)
+
+clean:
+	$(RM) *.*~ Makefile~ core *.o *.mod i.*.L *.L i.*.f90 *.M
+	$(RM) $(BINDIR)/rebuild $(BINDIR)/flio_rbld
+
Index: /IOIPSL/tags/v2_2_5/rebuild/AA_make.ldef
===================================================================
--- /IOIPSL/tags/v2_2_5/rebuild/AA_make.ldef	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/rebuild/AA_make.ldef	(revision 5058)
@@ -0,0 +1,14 @@
+#-
+#- $Id: AA_make.ldef 53 2007-06-06 15:27:26Z bellier $
+#-
+#---------------------------------------------------------------------
+#- Local parameters related to the "tools" of IOIPSL (rebuild, config)
+#---------------------------------------------------------------------
+SHELL = /bin/ksh
+#---------------------------------------------------------------------
+LIBDIR = ../../../lib
+BINDIR = ../../../bin
+MODDIR = $(LIBDIR)
+#---------------------------------------------------------------------
+P_P =
+MODEL_LIB = $(LIBDIR)/libioipsl.a
Index: /IOIPSL/tags/v2_2_5/rebuild/flio_rbld.f90
===================================================================
--- /IOIPSL/tags/v2_2_5/rebuild/flio_rbld.f90	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/rebuild/flio_rbld.f90	(revision 5058)
@@ -0,0 +1,1622 @@
+PROGRAM flio_rbld
+!
+!$Id: flio_rbld.f90 2310 2014-08-04 13:51:24Z mafoipsl $
+!-
+! This software is governed by the CeCILL license
+! See IOIPSL/IOIPSL_License_CeCILL.txt
+!!--------------------------------------------------------------------
+!! PROGRAM flio_rbld
+!!
+!! PURPOSE :
+!!   Recombine the files of MPI version of IOIPSL
+!!   along several dimensions.
+!!
+!! CALLING SEQUENCE :
+!!
+!!   "flio_rbld" is usually invoked by the script "rebuild"
+!!
+!!   rebuild -h
+!!
+!!   rebuild [-v lev] [-f] -o outfile infile[1] ... infile[n]
+!!
+!! INPUT for "rebuild" :
+!!
+!!   -h         : help
+!!   -v lev     : verbosity level
+!!   -f         : force executing mode
+!!   -o outfile : name of the recombined file.
+!!   infiles    : names of the files that must be recombined.
+!!
+!! INPUT for "flio_rbld" :
+!!
+!!  (I) i_v_lev  : verbosity level
+!!  (C) c_force  : executing mode (noforce/force)
+!!  (I) f_nb     : total number of files
+!!  (C) f_nm(:)  : names of the files (input_files output_file)
+!!
+!!
+!! ASSOCIATED MODULES :
+!!   IOIPSL(fliocom)
+!!
+!! RESTRICTIONS :
+!!
+!!   Cases for character are not coded.
+!!
+!!   Cases for netCDF variables such as array with more
+!!   than 5 dimensions are not coded.
+!!
+!!   Input files must have the following global attributes :
+!!
+!!     "DOMAIN_number_total"
+!!     "DOMAIN_number"
+!!     "DOMAIN_dimensions_ids"
+!!     "DOMAIN_size_global"
+!!     "DOMAIN_size_local"
+!!     "DOMAIN_position_first"
+!!     "DOMAIN_position_last"
+!!     "DOMAIN_halo_size_start"
+!!     "DOMAIN_halo_size_end"
+!!     "DOMAIN_type"
+!!
+!!   NetCDF files must be smaller than 2 Gb.
+!!
+!!   Character variables should have less than 257 letters
+!!
+!! EXAMPLE :
+!!
+!!   rebuild -v -o sst.nc sst_[0-9][0-9][0-9][0-9].nc
+!!
+!! MODIFICATION HISTORY :
+!!   Sebastien Masson   (smasson@jamstec.go.jp)   March 2004
+!!   Jacques   Bellier  (Jacques.Bellier@cea.fr)  June  2005
+!!--------------------------------------------------------------------
+  USE IOIPSL
+  USE defprec
+!-
+  IMPLICIT NONE
+!-
+! Character length
+  INTEGER,PARAMETER :: chlen=256
+!-
+! DO loops and test related variables
+  INTEGER :: i,ia,id,iv,iw,i_i,i_n
+  INTEGER :: ik,itmin,itmax,it1,it2,it
+  LOGICAL :: l_force,l_uld
+!-
+! Input arguments related variables
+  INTEGER :: i_v_lev
+  CHARACTER(LEN=15) :: c_force
+  INTEGER :: f_nb,f_nb_in
+  CHARACTER(LEN=chlen),DIMENSION(:),ALLOCATABLE :: f_nm
+!-
+! Domains related variables
+  INTEGER :: d_n_t,i_ntd
+  INTEGER,DIMENSION(:),ALLOCATABLE :: dom_att,d_d_i,d_s_g
+  INTEGER,DIMENSION(:,:),ALLOCATABLE :: d_s_l,d_p_f,d_p_l,d_h_s,d_h_e
+  LOGICAL :: l_cgd,l_cof,l_col,l_o_f,l_o_m,l_o_l
+  CHARACTER(LEN=chlen) :: c_d_n
+!-
+! Model files related variables
+  LOGICAL :: l_ocf
+  INTEGER,DIMENSION(:),ALLOCATABLE :: f_a_id
+  INTEGER :: f_id_i1,f_id_i,f_id_o
+  INTEGER :: f_d_nb,f_v_nb,f_a_nb,f_d_ul
+  INTEGER :: v_a_nb,a_type
+  CHARACTER(LEN=chlen),DIMENSION(:),ALLOCATABLE :: &
+&  f_d_nm,f_v_nm,f_a_nm,v_a_nm
+  CHARACTER(LEN=chlen) :: f_u_nm
+  INTEGER,DIMENSION(:),ALLOCATABLE :: v_d_nb,v_d_ul,v_type
+  INTEGER,DIMENSION(:,:),ALLOCATABLE :: v_d_i
+  INTEGER,DIMENSION(:),ALLOCATABLE :: f_d_i,f_d_l
+  INTEGER :: a_l
+  INTEGER,DIMENSION(flio_max_var_dims) :: d_i,ib,ie
+  INTEGER,DIMENSION(:),ALLOCATABLE :: &
+ &  io_i,io_n,ia_sf,io_sf,io_cf,ia_sm,io_sm,io_cm,ia_sl,io_sl,io_cl
+  LOGICAL :: l_ex
+  CHARACTER(LEN=chlen) :: c_wn1,c_wn2
+!-
+!?INTEGERS of KIND 1 are not supported on all computers
+!?INTEGER(KIND=i_1) :: i1_0d
+!?INTEGER(KIND=i_1),DIMENSION(:),ALLOCATABLE :: i1_1d
+!?INTEGER(KIND=i_1),DIMENSION(:,:),ALLOCATABLE :: i1_2d
+!?INTEGER(KIND=i_1),DIMENSION(:,:,:),ALLOCATABLE :: i1_3d
+!?INTEGER(KIND=i_1),DIMENSION(:,:,:,:),ALLOCATABLE :: i1_4d
+!?INTEGER(KIND=i_1),DIMENSION(:,:,:,:,:),ALLOCATABLE :: i1_5d
+  INTEGER(KIND=i_2) :: i2_0d
+  INTEGER(KIND=i_2),DIMENSION(:),ALLOCATABLE :: i2_1d
+  INTEGER(KIND=i_2),DIMENSION(:,:),ALLOCATABLE :: i2_2d
+  INTEGER(KIND=i_2),DIMENSION(:,:,:),ALLOCATABLE :: i2_3d
+  INTEGER(KIND=i_2),DIMENSION(:,:,:,:),ALLOCATABLE :: i2_4d
+  INTEGER(KIND=i_2),DIMENSION(:,:,:,:,:),ALLOCATABLE :: i2_5d
+  INTEGER(KIND=i_4) :: i4_0d
+  INTEGER(KIND=i_4),DIMENSION(:),ALLOCATABLE :: i4_1d
+  INTEGER(KIND=i_4),DIMENSION(:,:),ALLOCATABLE :: i4_2d
+  INTEGER(KIND=i_4),DIMENSION(:,:,:),ALLOCATABLE :: i4_3d
+  INTEGER(KIND=i_4),DIMENSION(:,:,:,:),ALLOCATABLE :: i4_4d
+  INTEGER(KIND=i_4),DIMENSION(:,:,:,:,:),ALLOCATABLE :: i4_5d
+  REAL(KIND=r_4) :: r4_0d
+  REAL(KIND=r_4),DIMENSION(:),ALLOCATABLE :: r4_1d
+  REAL(KIND=r_4),DIMENSION(:,:),ALLOCATABLE :: r4_2d
+  REAL(KIND=r_4),DIMENSION(:,:,:),ALLOCATABLE :: r4_3d
+  REAL(KIND=r_4),DIMENSION(:,:,:,:),ALLOCATABLE :: r4_4d
+  REAL(KIND=r_8),DIMENSION(:,:,:,:,:),ALLOCATABLE :: r4_5d
+  REAL(KIND=r_8) :: r8_0d
+  REAL(KIND=r_8),DIMENSION(:),ALLOCATABLE :: r8_1d
+  REAL(KIND=r_8),DIMENSION(:,:),ALLOCATABLE :: r8_2d
+  REAL(KIND=r_8),DIMENSION(:,:,:),ALLOCATABLE :: r8_3d
+  REAL(KIND=r_8),DIMENSION(:,:,:,:),ALLOCATABLE :: r8_4d
+  REAL(KIND=r_8),DIMENSION(:,:,:,:,:),ALLOCATABLE :: r8_5d
+!-
+! elapsed and cpu time computation variables
+  INTEGER :: nb_cc_ini,nb_cc_end,nb_cc_sec,nb_cc_max
+  REAL :: t_cpu_ini,t_cpu_end
+!---------------------------------------------------------------------
+!-
+!-------------------
+! INPUT arguments
+!-------------------
+!-
+! Retrieve the verbosity level
+  READ (UNIT=*,FMT=*) i_v_lev
+!-
+! Retrieve the executing mode
+  READ (UNIT=*,FMT='(A)') c_force
+  l_force = (TRIM(c_force)  == 'force')
+!-
+! Retrieve the number of arguments
+  READ (UNIT=*,FMT=*) f_nb
+  f_nb_in = f_nb-1
+!-
+! Retrieve the file names
+  ALLOCATE(f_nm(f_nb))
+  DO iw=1,f_nb
+    READ (UNIT=*,FMT='(A)') f_nm(iw)
+  ENDDO
+!-
+! Allocate and initialize the array of file access identifiers
+  ALLOCATE(f_a_id(f_nb_in)); f_a_id(:) = -1;
+!-
+  IF (i_v_lev >= 1) THEN
+    WRITE (UNIT=*,FMT='("")')
+    WRITE (UNIT=*,FMT='(" verbosity level : ",I4)') i_v_lev
+    WRITE (UNIT=*,FMT='(" executing  mode : ",A)') TRIM(c_force)
+    WRITE (UNIT=*,FMT='(" number of args  : ",I4)') f_nb
+    WRITE (UNIT=*,FMT='(" Input  files :")')
+    DO iw=1,f_nb_in
+      WRITE (*,'("   ",A)') TRIM(f_nm(iw))
+    ENDDO
+    WRITE (UNIT=*,FMT='(" Output file  :")')
+    WRITE (*,'("   ",A)') TRIM(f_nm(f_nb))
+!-- time initializations
+    CALL system_clock &
+ &   (count=nb_cc_ini,count_rate=nb_cc_sec,count_max=nb_cc_max)
+    CALL cpu_time (t_cpu_ini)
+  ENDIF
+!-
+!---------------------------------------------------
+! Retrieve basic informations from the first file
+!---------------------------------------------------
+!-
+! Open the first file
+  CALL flrb_of (1,f_id_i)
+!-
+! Get the attribute "DOMAIN_number_total"
+  CALL fliogeta (f_id_i,"?","DOMAIN_number_total",d_n_t)
+!-
+! Validate the number of input files :
+! should be equal to the total number
+! of domains used in the simulation
+  IF (d_n_t /= f_nb_in) THEN
+    IF (l_force) THEN
+      iw = 2
+    ELSE
+      iw = 3
+    ENDIF
+    CALL ipslerr (iw,"flio_rbld", &
+ &   "The number of input files", &
+ &   "is not equal to the number of DOMAINS"," ")
+  ENDIF
+!-
+! Retrieve the basic characteristics of the first input file
+  CALL flioinqf &
+ & (f_id_i,nb_dim=f_d_nb,nb_var=f_v_nb,nb_gat=f_a_nb,id_uld=f_d_ul)
+!-
+! Build the list of the names of the
+! dimensions/variables/global_attributes and retrieve
+! the unlimited_dimension name from the first input file
+  ALLOCATE(f_d_nm(f_d_nb),f_v_nm(f_v_nb),f_a_nm(f_a_nb))
+  CALL flioinqn (f_id_i,cn_dim=f_d_nm,cn_var=f_v_nm, &
+ &                      cn_gat=f_a_nm,cn_uld=f_u_nm)
+!-
+! Build the list of the dimensions identifiers and lengths
+  ALLOCATE(f_d_i(f_d_nb),f_d_l(f_d_nb))
+  CALL flioinqf (f_id_i,id_dim=f_d_i,ln_dim=f_d_l)
+!-
+! Close the file
+  CALL flrb_cf (1,.FALSE.)
+!-
+! Check if the number of needed files is greater than
+! the maximum number of simultaneously opened files.
+! In that case, open and close model files for each reading,
+! otherwise keep the "flio" identifiers of the opened files.
+  l_ocf = (f_nb > flio_max_files)
+!-
+!----------------------------------------------------
+! Retrieve domain informations for each input file
+!----------------------------------------------------
+!-
+  DO iw=1,f_nb_in
+!---
+    CALL flrb_of (iw,f_id_i)
+!---
+    IF (iw > 1) THEN
+      c_wn1 = "DOMAIN_number_total"
+      CALL flioinqa (f_id_i,"?",TRIM(c_wn1),l_ex,a_l=a_l)
+      IF (l_ex) THEN
+        CALL fliogeta (f_id_i,"?",TRIM(c_wn1),i_ntd)
+        IF (i_ntd /= d_n_t) THEN
+          CALL ipslerr (3,"flio_rbld", &
+ &        "File      : "//TRIM(f_nm(iw)), &
+ &        "Attribute : "//TRIM(c_wn1), &
+ &        "not equal to the one of the first file")
+        ENDIF
+      ELSE
+        CALL ipslerr (3,"flio_rbld", &
+ &       "File      : "//TRIM(f_nm(iw)), &
+ &       "Attribute : "//TRIM(c_wn1),"not found")
+      ENDIF
+    ENDIF
+!---
+    c_wn1 = "DOMAIN_dimensions_ids"
+    CALL flioinqa (f_id_i,"?",TRIM(c_wn1),l_ex,a_l=a_l)
+    IF (l_ex) THEN
+      ALLOCATE(dom_att(a_l))
+      CALL fliogeta (f_id_i,"?",TRIM(c_wn1),dom_att)
+      IF (iw == 1) THEN
+        IF (ANY(dom_att(:) == f_d_ul)) THEN
+          CALL ipslerr (3,"flio_rbld", &
+ &         "File      : "//TRIM(f_nm(iw)), &
+ &         "Attribute : "//TRIM(c_wn1), &
+ &         "contains the unlimited dimension")
+        ENDIF
+        ALLOCATE (d_d_i(a_l))
+        d_d_i(:) = dom_att(:)
+      ELSEIF (SIZE(dom_att) /= SIZE(d_d_i)) THEN
+        CALL ipslerr (3,"flio_rbld", &
+ &       "File      : "//TRIM(f_nm(iw)), &
+ &       "size of the attribute : "//TRIM(c_wn1), &
+ &       "not equal to the one of the first file")
+      ELSEIF (ANY(dom_att(:) /= d_d_i(:))) THEN
+        CALL ipslerr (3,"flio_rbld", &
+ &       "File      : "//TRIM(f_nm(iw)), &
+ &       "Attribute : "//TRIM(c_wn1), &
+ &       "not equal to the one of the first file")
+      ENDIF
+      DEALLOCATE(dom_att)
+    ELSE
+      CALL ipslerr (3,"flio_rbld", &
+ &     "File      : "//TRIM(f_nm(iw)), &
+ &     "Attribute : "//TRIM(c_wn1),"not found")
+    ENDIF
+!---
+    c_wn1 = "DOMAIN_size_global"
+    CALL flioinqa (f_id_i,"?",TRIM(c_wn1),l_ex,a_l=a_l)
+    IF (l_ex) THEN
+      IF (a_l /= SIZE(d_d_i)) THEN
+        CALL ipslerr (3,"flio_rbld", &
+ &       "File      : "//TRIM(f_nm(iw)), &
+ &       "size of the attribute : "//TRIM(c_wn1), &
+ &       "not equal to the size of DOMAIN_dimensions_ids")
+      ELSE
+        ALLOCATE(dom_att(a_l))
+        CALL fliogeta (f_id_i,"?",TRIM(c_wn1),dom_att)
+        IF (iw == 1) THEN
+          ALLOCATE (d_s_g(a_l))
+          d_s_g(:)=dom_att(:)
+        ELSEIF (ANY(dom_att(:) /= d_s_g(:))) THEN
+          CALL ipslerr (3,"flio_rbld", &
+ &         "File      : "//TRIM(f_nm(iw)), &
+ &         "Attribute : "//TRIM(c_wn1), &
+ &         "not equal to the one of the first file")
+        ENDIF
+        DEALLOCATE(dom_att)
+      ENDIF
+    ELSE
+      CALL ipslerr (3,"flio_rbld", &
+ &     "File      : "//TRIM(f_nm(iw)), &
+ &     "Attribute : "//TRIM(c_wn1),"not found")
+    ENDIF
+!---
+    c_wn1 = "DOMAIN_size_local"
+    CALL flioinqa (f_id_i,"?",TRIM(c_wn1),l_ex,a_l=a_l)
+    IF (l_ex) THEN
+      IF (a_l /= SIZE(d_d_i)) THEN
+        CALL ipslerr (3,"flio_rbld", &
+ &       "File      : "//TRIM(f_nm(iw)), &
+ &       "size of the attribute : "//TRIM(c_wn1), &
+ &       "not equal to the size of DOMAIN_dimensions_ids")
+      ELSE
+        ALLOCATE(dom_att(a_l))
+        CALL fliogeta (f_id_i,"?",TRIM(c_wn1),dom_att)
+        IF (iw == 1) THEN
+          ALLOCATE (d_s_l(a_l,f_nb_in))
+        ENDIF
+        d_s_l(:,iw)=dom_att(:)
+        DEALLOCATE(dom_att)
+      ENDIF
+    ELSE
+      CALL ipslerr (3,"flio_rbld", &
+ &     "File      : "//TRIM(f_nm(iw)), &
+ &     "Attribute : "//TRIM(c_wn1),"not found")
+    ENDIF
+!---
+    c_wn1 = "DOMAIN_position_first"
+    CALL flioinqa (f_id_i,"?",TRIM(c_wn1),l_ex,a_l=a_l)
+    IF (l_ex) THEN
+      IF (a_l /= SIZE(d_d_i)) THEN
+        CALL ipslerr (3,"flio_rbld", &
+ &       "File      : "//TRIM(f_nm(iw)), &
+ &       "size of the attribute : "//TRIM(c_wn1), &
+ &       "not equal to the size of DOMAIN_dimensions_ids")
+      ELSE
+        ALLOCATE(dom_att(a_l))
+        CALL fliogeta (f_id_i,"?",TRIM(c_wn1),dom_att)
+        IF (iw == 1) THEN
+          ALLOCATE (d_p_f(a_l,f_nb_in))
+        ENDIF
+        d_p_f(:,iw)=dom_att(:)
+        DEALLOCATE(dom_att)
+      ENDIF
+    ELSE
+      CALL ipslerr (3,"flio_rbld", &
+ &     "File      : "//TRIM(f_nm(iw)), &
+ &     "Attribute : "//TRIM(c_wn1),"not found")
+    ENDIF
+!---
+    c_wn1 = "DOMAIN_position_last"
+    CALL flioinqa (f_id_i,"?",TRIM(c_wn1),l_ex,a_l=a_l)
+    IF (l_ex) THEN
+      IF (a_l /= SIZE(d_d_i)) THEN
+        CALL ipslerr (3,"flio_rbld", &
+ &       "File      : "//TRIM(f_nm(iw)), &
+ &       "size of the attribute : "//TRIM(c_wn1), &
+ &       "not equal to the size of DOMAIN_dimensions_ids")
+      ELSE
+        ALLOCATE(dom_att(a_l))
+        CALL fliogeta (f_id_i,"?",TRIM(c_wn1),dom_att)
+        IF (iw == 1) THEN
+          ALLOCATE (d_p_l(a_l,f_nb_in))
+        ENDIF
+        d_p_l(:,iw)=dom_att(:)
+        DEALLOCATE(dom_att)
+      ENDIF
+    ELSE
+      CALL ipslerr (3,"flio_rbld", &
+ &     "File      : "//TRIM(f_nm(iw)), &
+ &     "Attribute : "//TRIM(c_wn1),"not found")
+    ENDIF
+!---
+    c_wn1 = "DOMAIN_halo_size_start"
+    CALL flioinqa (f_id_i,"?",TRIM(c_wn1),l_ex,a_l=a_l)
+    IF (l_ex) THEN
+      IF (a_l /= SIZE(d_d_i)) THEN
+        CALL ipslerr (3,"flio_rbld", &
+ &       "File      : "//TRIM(f_nm(iw)), &
+ &       "size of the attribute : "//TRIM(c_wn1), &
+ &       "not equal to the size of DOMAIN_dimensions_ids")
+      ELSE
+        ALLOCATE(dom_att(a_l))
+        CALL fliogeta (f_id_i,"?",TRIM(c_wn1),dom_att)
+        IF (iw == 1) THEN
+          ALLOCATE (d_h_s(a_l,f_nb_in))
+        ENDIF
+        d_h_s(:,iw)=dom_att(:)
+        DEALLOCATE(dom_att)
+      ENDIF
+    ELSE
+      CALL ipslerr (3,"flio_rbld", &
+ &     "File      : "//TRIM(f_nm(iw)), &
+ &     "Attribute : "//TRIM(c_wn1),"not found")
+    ENDIF
+!---
+    c_wn1 = "DOMAIN_halo_size_end"
+    CALL flioinqa (f_id_i,"?",TRIM(c_wn1),l_ex,a_l=a_l)
+    IF (l_ex) THEN
+      IF (a_l /= SIZE(d_d_i)) THEN
+        CALL ipslerr (3,"flio_rbld", &
+ &       "File      : "//TRIM(f_nm(iw)), &
+ &       "size of the attribute : "//TRIM(c_wn1), &
+ &       "not equal to the size of DOMAIN_dimensions_ids")
+      ELSE
+        ALLOCATE(dom_att(a_l))
+        CALL fliogeta (f_id_i,"?",TRIM(c_wn1),dom_att)
+        IF (iw == 1) THEN
+          ALLOCATE (d_h_e(a_l,f_nb_in))
+        ENDIF
+        d_h_e(:,iw)=dom_att(:)
+        DEALLOCATE(dom_att)
+      ENDIF
+    ELSE
+      CALL ipslerr (3,"flio_rbld", &
+ &     "File      : "//TRIM(f_nm(iw)), &
+ &     "Attribute : "//TRIM(c_wn1),"not found")
+    ENDIF
+!---
+    c_wn1 = "DOMAIN_type"
+    c_wn2 = " "
+    CALL flioinqa (f_id_i,"?",TRIM(c_wn1),l_ex,a_l=a_l)
+    IF (l_ex) THEN
+      CALL fliogeta (f_id_i,"?",TRIM(c_wn1),c_wn2)
+      CALL strlowercase (c_wn2)
+      IF (iw == 1) THEN
+        IF (    (TRIM(c_wn2) == "box") &
+ &          .OR.(TRIM(c_wn2) == "apple") ) THEN
+          c_d_n = c_wn2
+        ELSE
+          CALL ipslerr (3,"flio_rbld", &
+ &         "File      : "//TRIM(f_nm(iw)), &
+ &         "Attribute : "//TRIM(c_wn1), &
+ &         "type "//TRIM(c_wn2)//" not (yet) supported")
+        ENDIF
+      ELSEIF (TRIM(c_wn2) /= TRIM(c_d_n)) THEN
+        CALL ipslerr (3,"flio_rbld", &
+ &       "File      : "//TRIM(f_nm(iw)), &
+ &       "Attribute : "//TRIM(c_wn1), &
+ &       "not equal to the one of the first file")
+      ENDIF
+    ELSE
+      CALL ipslerr (3,"flio_rbld", &
+ &     "File      : "//TRIM(f_nm(iw)), &
+ &     "Attribute : "//TRIM(c_wn1),"not found")
+    ENDIF
+!---
+    CALL flrb_cf (iw,l_ocf)
+!---
+  ENDDO
+!-
+  IF (i_v_lev >= 2) THEN
+    WRITE (UNIT=*,FMT='("")')
+    WRITE (*,'(" From the first file : ")')
+    WRITE (*,'("   Number of dimensions : ",I2)') f_d_nb
+    WRITE (*,'("     Idents  : ",(10(1X,I4),:))') f_d_i(1:f_d_nb)
+    WRITE (*,'("     Lengths : ",(10(1X,I4),:))') f_d_l(1:f_d_nb)
+    WRITE (*,'("     Names: ")')
+    DO i=1,f_d_nb
+      WRITE (*,'("       """,A,"""")') TRIM(f_d_nm(i))
+    ENDDO
+    IF (f_d_ul > 0) THEN
+      WRITE (*,'("   Unlimited dimension id : ",I2)') f_d_i(f_d_ul)
+    ENDIF
+    WRITE (*,'("   Number of variables  : ",I2)') f_v_nb
+    WRITE (*,'("     Names: ")')
+    DO i=1,f_v_nb
+      WRITE (*,'("       """,A,"""")') TRIM(f_v_nm(i))
+    ENDDO
+    WRITE (*,'("   Number of global attributes : ",I2)') f_a_nb
+    WRITE (*,'("     Names: ")')
+    DO i=1,f_a_nb
+      WRITE (*,'("       """,A,"""")') TRIM(f_a_nm(i))
+    ENDDO
+  ENDIF
+  IF (i_v_lev >= 3) THEN
+    WRITE (UNIT=*,FMT='("")')
+    WRITE (*,'(" From input files : ")')
+    WRITE (*,'("   Total number of DOMAINS : ",I4)') d_n_t
+    WRITE (*,'("   DOMAIN_dimensions_ids :",(10(1X,I5),:))') d_d_i(:)
+    WRITE (*,'("   DOMAIN_size_global    :",(10(1X,I5),:))') d_s_g(:)
+    WRITE (*,'("   DOMAIN_type           : """,(A),"""")') TRIM(c_d_n)
+    DO iw=1,f_nb_in
+      WRITE (*,'("   File   : ",A)') TRIM(f_nm(iw))
+      WRITE (*,'("     d_s_l  :",(10(1X,I5),:))') d_s_l(:,iw)
+      WRITE (*,'("     d_p_f  :",(10(1X,I5),:))') d_p_f(:,iw)
+      WRITE (*,'("     d_p_l  :",(10(1X,I5),:))') d_p_l(:,iw)
+      WRITE (*,'("     d_h_s  :",(10(1X,I5),:))') d_h_s(:,iw)
+      IF (TRIM(c_d_n) == "apple") THEN
+        IF (COUNT(d_h_s(:,iw) /= 0) > 1) THEN
+          CALL ipslerr (3,"flio_rbld", &
+ &          "Beginning offset is not yet supported", &
+ &          "for more than one dimension"," ")
+        ENDIF
+      ENDIF
+      WRITE (*,'("     d_h_e  :",(10(1X,I5),:))') d_h_e(:,iw)
+      IF (TRIM(c_d_n) == "apple") THEN
+        IF (COUNT(d_h_e(:,iw) /= 0) > 1) THEN
+          CALL ipslerr (3,"flio_rbld", &
+ &          "Ending offset is not yet supported", &
+ &          "for more than one dimension"," ")
+        ENDIF
+      ENDIF
+    ENDDO
+  ENDIF
+!-
+!---------------------------------------
+! Create the dimensionned output file
+!---------------------------------------
+!-
+! Define the dimensions used in the output file
+  DO id=1,f_d_nb
+    DO i=1,SIZE(d_d_i)
+      IF (f_d_i(id) == d_d_i(i)) THEN
+        f_d_l(id) = d_s_g(i)
+      ENDIF
+    ENDDO
+  ENDDO
+!-
+  IF (f_d_ul > 0) THEN
+    i = f_d_l(f_d_ul); f_d_l(f_d_ul) = -1;
+  ENDIF
+!-
+! Create the output file
+  CALL fliocrfd (TRIM(f_nm(f_nb)),f_d_nm,f_d_l,f_id_o,c_f_n=c_wn1)
+!-
+  IF (f_d_ul > 0) THEN
+    f_d_l(f_d_ul) = i; itmin = 1; itmax = f_d_l(f_d_ul);
+  ELSE
+    itmin = 1; itmax = 1;
+  ENDIF
+!-
+! open the first input file used to build the output file
+!-
+  CALL flrb_of (1,f_id_i1)
+!-
+! define the global attributes in the output file
+! copy all global attributes except those beginning by "DOMAIN_"
+! eventually actualize the "file_name" attribute
+!-
+  DO ia=1,f_a_nb
+    IF (INDEX(TRIM(f_a_nm(ia)),"DOMAIN_") == 1)  CYCLE
+    IF (TRIM(f_a_nm(ia)) == "file_name") THEN
+      CALL flioputa (f_id_o,"?",TRIM(f_a_nm(ia)),TRIM(c_wn1))
+    ELSE
+      CALL fliocpya (f_id_i1,"?",TRIM(f_a_nm(ia)),f_id_o,"?")
+    ENDIF
+  ENDDO
+!-
+! define the variables in the output file
+!-
+  ALLOCATE(v_d_nb(f_v_nb)); v_d_nb(:) = 0;
+  ALLOCATE(v_d_ul(f_v_nb)); v_d_ul(:) = 0;
+  ALLOCATE(v_type(f_v_nb),v_d_i(flio_max_var_dims,f_v_nb));
+  DO iv=1,f_v_nb
+!-- get variable informations
+    CALL flioinqv &
+ &   (f_id_i1,TRIM(f_v_nm(iv)),l_ex,v_t=v_type(iv), &
+ &    nb_dims=v_d_nb(iv),id_dims=d_i,nb_atts=v_a_nb)
+!-- define the new variable
+    IF (v_d_nb(iv) == 0) THEN
+      CALL fliodefv &
+ &     (f_id_o,TRIM(f_v_nm(iv)),v_t=v_type(iv))
+    ELSE
+      CALL fliodefv &
+ &     (f_id_o,TRIM(f_v_nm(iv)),d_i(1:v_d_nb(iv)),v_t=v_type(iv))
+      DO iw=1,v_d_nb(iv)
+        IF (f_d_ul > 0) THEN
+          IF (d_i(iw) == f_d_ul) THEN
+            v_d_ul(iv) = iw
+          ENDIF
+        ENDIF
+      ENDDO
+      v_d_i(1:v_d_nb(iv),iv) = d_i(1:v_d_nb(iv))
+    ENDIF
+!-- copy all variable attributes
+    IF (v_a_nb > 0) THEN
+      ALLOCATE(v_a_nm(v_a_nb))
+      CALL flioinqv (f_id_i1,TRIM(f_v_nm(iv)),l_ex,cn_atts=v_a_nm)
+      DO ia=1,v_a_nb
+        CALL fliocpya &
+ &       (f_id_i1,TRIM(f_v_nm(iv)),TRIM(v_a_nm(ia)), &
+ &        f_id_o,TRIM(f_v_nm(iv)))
+      ENDDO
+      DEALLOCATE(v_a_nm)
+    ENDIF
+  ENDDO
+!-
+! update valid_min valid_max attributes values
+!-
+  CALL flrb_rg
+!-
+!------------------------
+! Fill the output file
+!------------------------
+!-
+  DO ik=1,2
+    l_uld = (ik /= 1)
+    IF (l_uld) THEN
+      it1=itmin; it2=itmax;
+    ELSE
+      it1=1; it2=1;
+    ENDIF
+    DO it=it1,it2
+      DO iv=1,f_v_nb
+        IF (    (.NOT.l_uld.AND.(v_d_ul(iv) > 0)) &
+ &          .OR.(l_uld.AND.(v_d_ul(iv) <= 0)) ) THEN
+          CYCLE
+        ENDIF
+        IF (i_v_lev >= 3) THEN
+          WRITE (UNIT=*,FMT='("")')
+          IF (l_uld) THEN
+            WRITE (UNIT=*,FMT=*) "time step     : ",it
+          ENDIF
+          WRITE (UNIT=*,FMT=*) "variable      : ",TRIM(f_v_nm(iv))
+          WRITE (UNIT=*,FMT=*) "var unlim dim : ",v_d_ul(iv)
+        ENDIF
+!------ do the variable contains dimensions to be recombined ?
+        l_cgd = .FALSE.
+        i_n = 1
+        DO i=1,SIZE(d_d_i)
+          l_cgd = ANY(v_d_i(1:v_d_nb(iv),iv) == d_d_i(i))
+          l_cgd = l_cgd.AND.ANY(d_s_l(i,1:f_nb_in) /= d_s_g(i))
+          IF (l_cgd) THEN
+            i_n = f_nb_in
+              EXIT
+          ENDIF
+        ENDDO
+        IF (v_d_nb(iv) > 0) THEN
+!-------- Allocate io_i,io_n,ia_sm,io_sm,io_cm
+          i = v_d_nb(iv)
+          ALLOCATE(io_i(i),io_n(i),ia_sm(i),io_sm(i),io_cm(i))
+!-------- Default definition of io_i,io_n,io_sm,io_cm
+          io_i(:) = 1; io_n(:) = f_d_l(v_d_i(1:v_d_nb(iv),iv));
+          ia_sm(:) = 1; io_sm(:) = 1;
+          IF (v_d_ul(iv) > 0) THEN
+            io_i(v_d_ul(iv))=it
+            io_n(v_d_ul(iv))=1
+            io_sm(v_d_ul(iv))=it
+          ENDIF
+          io_cm(:) = io_n(:);
+!-------- If needed, allocate offset
+          l_o_f = .FALSE.; l_o_m = .TRUE.; l_o_l = .FALSE.;
+          IF (TRIM(c_d_n) == "apple") THEN
+            ALLOCATE(ia_sf(i),io_sf(i),io_cf(i))
+            ALLOCATE(ia_sl(i),io_sl(i),io_cl(i))
+            ia_sf(:) = 1; io_sf(:) = 1; io_cf(:) = io_n(:);
+            ia_sl(:) = 1; io_sl(:) = 1; io_cl(:) = io_n(:);
+            IF (v_d_ul(iv) > 0) THEN
+              io_sf(v_d_ul(iv))=it
+              io_sl(v_d_ul(iv))=it
+            ENDIF
+          ENDIF
+        ENDIF
+!------
+        DO i_i=1,i_n
+          IF (l_cgd) THEN
+!---------- the variable contains dimensions to be recombined
+!-----------
+!---------- open each file containing a small piece of data
+            CALL flrb_of (i_i,f_id_i)
+!-----------
+!---------- do the variable has offset at first/last block ?
+            l_cof = .FALSE.; l_col = .FALSE.;
+            IF (TRIM(c_d_n) == "apple") THEN
+              L_BF: DO id=1,v_d_nb(iv)
+                DO i=1,SIZE(d_d_i)
+                  IF (v_d_i(id,iv) == d_d_i(i)) THEN
+                    l_cof = (d_h_s(i,i_i) /= 0)
+                    IF (l_cof)  EXIT L_BF
+                  ENDIF
+                ENDDO
+              ENDDO L_BF
+              L_BL: DO id=1,v_d_nb(iv)
+                DO i=1,SIZE(d_d_i)
+                  IF (v_d_i(id,iv) == d_d_i(i)) THEN
+                    l_col = (d_h_e(i,i_i) /= 0)
+                    IF (l_col)  EXIT L_BL
+                  ENDIF
+                ENDDO
+              ENDDO L_BL
+            ENDIF
+!---------- if needed, redefine start and count for dimensions
+            l_o_f = .FALSE.; l_o_m = .TRUE.; l_o_l = .FALSE.;
+            DO id=1,v_d_nb(iv)
+              DO i=1,SIZE(d_d_i)
+                IF (v_d_i(id,iv) == d_d_i(i)) THEN
+                  io_n(id) = d_p_l(i,i_i)-d_p_f(i,i_i)+1
+                  ia_sm(id) = 1
+                  io_sm(id) = d_p_f(i,i_i)
+                  io_cm(id) = io_n(id)
+                  IF     (TRIM(c_d_n) == "box") THEN
+                    ia_sm(id) = ia_sm(id)+d_h_s(i,i_i)
+                    io_sm(id) = io_sm(id)+d_h_s(i,i_i)
+                    io_cm(id) = io_cm(id)-d_h_s(i,i_i)-d_h_e(i,i_i)
+                  ELSEIF (TRIM(c_d_n) == "apple") THEN
+                    IF (l_cof) THEN
+                      IF (d_h_s(i,i_i) /= 0) THEN
+                        ia_sf(id) = 1+d_h_s(i,i_i)
+                        io_sf(id) = d_p_f(i,i_i)+d_h_s(i,i_i)
+                        io_cf(id) = io_n(id)-d_h_s(i,i_i)
+                      ELSE
+                        io_sf(id) = d_p_f(i,i_i)
+                        io_cf(id) = 1
+                        ia_sm(id) = ia_sm(id)+1
+                        io_sm(id) = io_sm(id)+1
+                        io_cm(id) = io_cm(id)-1
+                        l_o_f = .TRUE.
+                      ENDIF
+                    ENDIF
+                    IF (l_col) THEN
+                      IF (d_h_e(i,i_i) /= 0) THEN
+                        ia_sl(id) = 1
+                        io_sl(id) = d_p_f(i,i_i)
+                        io_cl(id) = io_n(id)-d_h_e(i,i_i)
+                      ELSE
+                        io_cm(id) = io_cm(id)-1
+                        ia_sl(id) = 1+io_n(id)-1
+                        io_sl(id) = d_p_f(i,i_i)+io_n(id)-1
+                        io_cl(id) = 1
+                        l_o_l = .TRUE.
+                      ENDIF
+                    ENDIF
+                  ENDIF
+                ENDIF
+              ENDDO
+            ENDDO
+            l_o_m = ALL(io_cm > 0)
+          ELSE
+!---------- the data can be read/write in one piece
+            f_id_i = f_id_i1
+          ENDIF
+!---------
+          IF (i_v_lev >= 3) THEN
+            WRITE (UNIT=*,FMT=*) &
+ &            TRIM(f_nm(i_i))//" - "//TRIM(f_v_nm(iv))
+            WRITE (UNIT=*,FMT=*) "io_i  : ",io_i(:)
+            WRITE (UNIT=*,FMT=*) "io_n  : ",io_n(:)
+            WRITE (UNIT=*,FMT=*) "l_o_f : ",l_o_f
+            IF (l_o_f) THEN
+              WRITE (UNIT=*,FMT=*) "ia_sf : ",ia_sf(:)
+              WRITE (UNIT=*,FMT=*) "io_sf : ",io_sf(:)
+              WRITE (UNIT=*,FMT=*) "io_cf : ",io_cf(:)
+            ENDIF
+            WRITE (UNIT=*,FMT=*) "l_o_m : ",l_o_m
+            IF (l_o_m) THEN
+              WRITE (UNIT=*,FMT=*) "ia_sm : ",ia_sm(:)
+              WRITE (UNIT=*,FMT=*) "io_sm : ",io_sm(:)
+              WRITE (UNIT=*,FMT=*) "io_cm : ",io_cm(:)
+            ENDIF
+            WRITE (UNIT=*,FMT=*) "l_o_l : ",l_o_l
+            IF (l_o_l) THEN
+              WRITE (UNIT=*,FMT=*) "ia_sl : ",ia_sl(:)
+              WRITE (UNIT=*,FMT=*) "io_sl : ",io_sl(:)
+              WRITE (UNIT=*,FMT=*) "io_cl : ",io_cl(:)
+            ENDIF
+          ENDIF
+!---------
+!-------- Cases according to the type, shape and offsets of the data
+!---------
+          SELECT CASE (v_type(iv))
+!?INTEGERS of KIND 1 are not supported on all computers
+!?        CASE (flio_i1) !--- INTEGER 1
+!?          SELECT CASE (v_d_nb(iv))
+!?          CASE (0) !--- Scalar
+!?            CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),i1_0d)
+!?            CALL flioputv (f_id_o,TRIM(f_v_nm(iv)),i1_0d)
+!?          CASE (1) !--- 1d array
+!?            ALLOCATE(i1_1d(io_n(1)))
+!?            CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),i1_1d, &
+!? &            start=io_i(:),count=io_n(:))
+!?            IF (l_o_f) THEN
+!?              ib(1:1) = ia_sf(1:1); ie(1:1) = ib(1:1)+io_cf(1:1)-1;
+!?              CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+!? &              i1_1d(ib(1):ie(1)), &
+!? &              start=io_sf(:),count=io_cf(:))
+!?            ENDIF
+!?            IF (l_o_m) THEN
+!?              ib(1:1) = ia_sm(1:1); ie(1:1) = ib(1:1)+io_cm(1:1)-1;
+!?              CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+!? &              i1_1d(ib(1):ie(1)), &
+!? &              start=io_sm(:),count=io_cm(:))
+!?            ENDIF
+!?            IF (l_o_l) THEN
+!?              ib(1:1) = ia_sl(1:1); ie(1:1) = ib(1:1)+io_cl(1:1)-1;
+!?              CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+!? &              i1_1d(ib(1):ie(1)), &
+!? &              start=io_sl(:),count=io_cl(:))
+!?            ENDIF
+!?            DEALLOCATE(i1_1d)
+!?          CASE (2) !--- 2d array
+!?            ALLOCATE(i1_2d(io_n(1),io_n(2)))
+!?            CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),i1_2d, &
+!? &            start=io_i(:),count=io_n(:))
+!?            IF (l_o_f) THEN
+!?              ib(1:2) = ia_sf(1:2); ie(1:2) = ib(1:2)+io_cf(1:2)-1;
+!?              CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+!? &              i1_2d(ib(1):ie(1),ib(2):ie(2)), &
+!? &              start=io_sf(:),count=io_cf(:))
+!?            ENDIF
+!?            IF (l_o_m) THEN
+!?              ib(1:2) = ia_sm(1:2); ie(1:2) = ib(1:2)+io_cm(1:2)-1;
+!?              CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+!? &              i1_2d(ib(1):ie(1),ib(2):ie(2)), &
+!? &              start=io_sm(:),count=io_cm(:))
+!?            ENDIF
+!?            IF (l_o_l) THEN
+!?              ib(1:2) = ia_sl(1:2); ie(1:2) = ib(1:2)+io_cl(1:2)-1;
+!?              CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+!? &              i1_2d(ib(1):ie(1),ib(2):ie(2)), &
+!? &              start=io_sl(:),count=io_cl(:))
+!?            ENDIF
+!?            DEALLOCATE(i1_2d)
+!?          CASE (3) !--- 3d array
+!?            ALLOCATE(i1_3d(io_n(1),io_n(2),io_n(3)))
+!?            CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),i1_3d, &
+!? &            start=io_i(:),count=io_n(:))
+!?            IF (l_o_f) THEN
+!?              ib(1:3) = ia_sf(1:3); ie(1:3) = ib(1:3)+io_cf(1:3)-1;
+!?              CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+!? &              i1_3d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3)), &
+!? &              start=io_sf(:),count=io_cf(:))
+!?            ENDIF
+!?            IF (l_o_m) THEN
+!?              ib(1:3) = ia_sm(1:3); ie(1:3) = ib(1:3)+io_cm(1:3)-1;
+!?              CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+!? &              i1_3d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3)), &
+!? &              start=io_sm(:),count=io_cm(:))
+!?            ENDIF
+!?            IF (l_o_l) THEN
+!?              ib(1:3) = ia_sl(1:3); ie(1:3) = ib(1:3)+io_cl(1:3)-1;
+!?              CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+!? &              i1_3d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3)), &
+!? &              start=io_sl(:),count=io_cl(:))
+!?            ENDIF
+!?            DEALLOCATE(i1_3d)
+!?          CASE (4) !--- 4d array
+!?            ALLOCATE(i1_4d(io_n(1),io_n(2),io_n(3),io_n(4)))
+!?            CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),i1_4d, &
+!? &            start=io_i(:),count=io_n(:))
+!?            IF (l_o_f) THEN
+!?              ib(1:4) = ia_sf(1:4); ie(1:4) = ib(1:4)+io_cf(1:4)-1;
+!?              CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+!? &              i1_4d(ib(1):ie(1),ib(2):ie(2), &
+!? &                    ib(3):ie(3),ib(4):ie(4)), &
+!? &              start=io_sf(:),count=io_cf(:))
+!?            ENDIF
+!?            IF (l_o_m) THEN
+!?              ib(1:4) = ia_sm(1:4); ie(1:4) = ib(1:4)+io_cm(1:4)-1;
+!?              CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+!? &              i1_4d(ib(1):ie(1),ib(2):ie(2), &
+!? &                    ib(3):ie(3),ib(4):ie(4)), &
+!? &              start=io_sm(:),count=io_cm(:))
+!?            ENDIF
+!?            IF (l_o_l) THEN
+!?              ib(1:4) = ia_sl(1:4); ie(1:4) = ib(1:4)+io_cl(1:4)-1;
+!?              CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+!? &              i1_4d(ib(1):ie(1),ib(2):ie(2), &
+!? &                    ib(3):ie(3),ib(4):ie(4)), &
+!? &              start=io_sl(:),count=io_cl(:))
+!?            ENDIF
+!?            DEALLOCATE(i1_4d)
+!?          CASE (5) !--- 5d array
+!?            ALLOCATE(i1_5d(io_n(1),io_n(2),io_n(3),io_n(4),io_n(5)))
+!?            CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),i1_5d, &
+!? &            start=io_i(:),count=io_n(:))
+!?            IF (l_o_f) THEN
+!?              ib(1:5) = ia_sf(1:5); ie(1:5) = ib(1:5)+io_cf(1:5)-1;
+!?              CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+!? &              i1_5d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3), &
+!? &                    ib(4):ie(4),ib(5):ie(5)), &
+!? &              start=io_sf(:),count=io_cf(:))
+!?            ENDIF
+!?            IF (l_o_m) THEN
+!?              ib(1:5) = ia_sm(1:5); ie(1:5) = ib(1:5)+io_cm(1:5)-1;
+!?              CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+!? &              i1_5d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3), &
+!? &                    ib(4):ie(4),ib(5):ie(5)), &
+!? &              start=io_sm(:),count=io_cm(:))
+!?            ENDIF
+!?            IF (l_o_l) THEN
+!?              ib(1:5) = ia_sl(1:5); ie(1:5) = ib(1:5)+io_cl(1:5)-1;
+!?              CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+!? &              i1_5d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3), &
+!? &                    ib(4):ie(4),ib(5):ie(5)), &
+!? &              start=io_sl(:),count=io_cl(:))
+!?            ENDIF
+!?            DEALLOCATE(i1_5d)
+!?          END SELECT
+!?        CASE (flio_i2) !--- INTEGER 2
+          CASE (flio_i1,flio_i2) !--- INTEGER 1/INTEGER 2
+            SELECT CASE (v_d_nb(iv))
+            CASE (0) !--- Scalar
+              CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),i2_0d)
+              CALL flioputv (f_id_o,TRIM(f_v_nm(iv)),i2_0d)
+            CASE (1) !--- 1d array
+              ALLOCATE(i2_1d(io_n(1)))
+              CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),i2_1d, &
+ &              start=io_i(:),count=io_n(:))
+              IF (l_o_f) THEN
+                ib(1:1) = ia_sf(1:1); ie(1:1) = ib(1:1)+io_cf(1:1)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i2_1d(ib(1):ie(1)), &
+ &                start=io_sf(:),count=io_cf(:))
+              ENDIF
+              IF (l_o_m) THEN
+                ib(1:1) = ia_sm(1:1); ie(1:1) = ib(1:1)+io_cm(1:1)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i2_1d(ib(1):ie(1)), &
+ &                start=io_sm(:),count=io_cm(:))
+              ENDIF
+              IF (l_o_l) THEN
+                ib(1:1) = ia_sl(1:1); ie(1:1) = ib(1:1)+io_cl(1:1)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i2_1d(ib(1):ie(1)), &
+ &                start=io_sl(:),count=io_cl(:))
+              ENDIF
+              DEALLOCATE(i2_1d)
+            CASE (2) !--- 2d array
+              ALLOCATE(i2_2d(io_n(1),io_n(2)))
+              CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),i2_2d, &
+ &              start=io_i(:),count=io_n(:))
+              IF (l_o_f) THEN
+                ib(1:2) = ia_sf(1:2); ie(1:2) = ib(1:2)+io_cf(1:2)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i2_2d(ib(1):ie(1),ib(2):ie(2)), &
+ &                start=io_sf(:),count=io_cf(:))
+              ENDIF
+              IF (l_o_m) THEN
+                ib(1:2) = ia_sm(1:2); ie(1:2) = ib(1:2)+io_cm(1:2)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i2_2d(ib(1):ie(1),ib(2):ie(2)), &
+ &                start=io_sm(:),count=io_cm(:))
+              ENDIF
+              IF (l_o_l) THEN
+                ib(1:2) = ia_sl(1:2); ie(1:2) = ib(1:2)+io_cl(1:2)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i2_2d(ib(1):ie(1),ib(2):ie(2)), &
+ &                start=io_sl(:),count=io_cl(:))
+              ENDIF
+              DEALLOCATE(i2_2d)
+            CASE (3) !--- 3d array
+              ALLOCATE(i2_3d(io_n(1),io_n(2),io_n(3)))
+              CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),i2_3d, &
+ &              start=io_i(:),count=io_n(:))
+              IF (l_o_f) THEN
+                ib(1:3) = ia_sf(1:3); ie(1:3) = ib(1:3)+io_cf(1:3)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i2_3d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3)), &
+ &                start=io_sf(:),count=io_cf(:))
+              ENDIF
+              IF (l_o_m) THEN
+                ib(1:3) = ia_sm(1:3); ie(1:3) = ib(1:3)+io_cm(1:3)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i2_3d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3)), &
+ &                start=io_sm(:),count=io_cm(:))
+              ENDIF
+              IF (l_o_l) THEN
+                ib(1:3) = ia_sl(1:3); ie(1:3) = ib(1:3)+io_cl(1:3)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i2_3d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3)), &
+ &                start=io_sl(:),count=io_cl(:))
+              ENDIF
+              DEALLOCATE(i2_3d)
+            CASE (4) !--- 4d array
+              ALLOCATE(i2_4d(io_n(1),io_n(2),io_n(3),io_n(4)))
+              CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),i2_4d, &
+ &              start=io_i(:),count=io_n(:))
+              IF (l_o_f) THEN
+                ib(1:4) = ia_sf(1:4); ie(1:4) = ib(1:4)+io_cf(1:4)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i2_4d(ib(1):ie(1),ib(2):ie(2), &
+ &                      ib(3):ie(3),ib(4):ie(4)), &
+ &                start=io_sf(:),count=io_cf(:))
+              ENDIF
+              IF (l_o_m) THEN
+                ib(1:4) = ia_sm(1:4); ie(1:4) = ib(1:4)+io_cm(1:4)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i2_4d(ib(1):ie(1),ib(2):ie(2), &
+ &                      ib(3):ie(3),ib(4):ie(4)), &
+ &                start=io_sm(:),count=io_cm(:))
+              ENDIF
+              IF (l_o_l) THEN
+                ib(1:4) = ia_sl(1:4); ie(1:4) = ib(1:4)+io_cl(1:4)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i2_4d(ib(1):ie(1),ib(2):ie(2), &
+ &                      ib(3):ie(3),ib(4):ie(4)), &
+ &                start=io_sl(:),count=io_cl(:))
+              ENDIF
+              DEALLOCATE(i2_4d)
+            CASE (5) !--- 5d array
+              ALLOCATE(i2_5d(io_n(1),io_n(2),io_n(3),io_n(4),io_n(5)))
+              CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),i2_5d, &
+ &              start=io_i(:),count=io_n(:))
+              IF (l_o_f) THEN
+                ib(1:5) = ia_sf(1:5); ie(1:5) = ib(1:5)+io_cf(1:5)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i2_5d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3), &
+ &                      ib(4):ie(4),ib(5):ie(5)), &
+ &                start=io_sf(:),count=io_cf(:))
+              ENDIF
+              IF (l_o_m) THEN
+                ib(1:5) = ia_sm(1:5); ie(1:5) = ib(1:5)+io_cm(1:5)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i2_5d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3), &
+ &                      ib(4):ie(4),ib(5):ie(5)), &
+ &                start=io_sm(:),count=io_cm(:))
+              ENDIF
+              IF (l_o_l) THEN
+                ib(1:5) = ia_sl(1:5); ie(1:5) = ib(1:5)+io_cl(1:5)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i2_5d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3), &
+ &                      ib(4):ie(4),ib(5):ie(5)), &
+ &                start=io_sl(:),count=io_cl(:))
+              ENDIF
+              DEALLOCATE(i2_5d)
+            END SELECT
+          CASE (flio_i4) !--- INTEGER 4
+            SELECT CASE (v_d_nb(iv))
+            CASE (0) !--- Scalar
+              CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),i4_0d)
+              CALL flioputv (f_id_o,TRIM(f_v_nm(iv)),i4_0d)
+            CASE (1) !--- 1d array
+              ALLOCATE(i4_1d(io_n(1)))
+              CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),i4_1d, &
+ &              start=io_i(:),count=io_n(:))
+              IF (l_o_f) THEN
+                ib(1:1) = ia_sf(1:1); ie(1:1) = ib(1:1)+io_cf(1:1)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i4_1d(ib(1):ie(1)), &
+ &                start=io_sf(:),count=io_cf(:))
+              ENDIF
+              IF (l_o_m) THEN
+                ib(1:1) = ia_sm(1:1); ie(1:1) = ib(1:1)+io_cm(1:1)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i4_1d(ib(1):ie(1)), &
+ &                start=io_sm(:),count=io_cm(:))
+              ENDIF
+              IF (l_o_l) THEN
+                ib(1:1) = ia_sl(1:1); ie(1:1) = ib(1:1)+io_cl(1:1)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i4_1d(ib(1):ie(1)), &
+ &                start=io_sl(:),count=io_cl(:))
+              ENDIF
+              DEALLOCATE(i4_1d)
+            CASE (2) !--- 2d array
+              ALLOCATE(i4_2d(io_n(1),io_n(2)))
+              CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),i4_2d, &
+ &              start=io_i(:),count=io_n(:))
+              IF (l_o_f) THEN
+                ib(1:2) = ia_sf(1:2); ie(1:2) = ib(1:2)+io_cf(1:2)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i4_2d(ib(1):ie(1),ib(2):ie(2)), &
+ &                start=io_sf(:),count=io_cf(:))
+              ENDIF
+              IF (l_o_m) THEN
+                ib(1:2) = ia_sm(1:2); ie(1:2) = ib(1:2)+io_cm(1:2)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i4_2d(ib(1):ie(1),ib(2):ie(2)), &
+ &                start=io_sm(:),count=io_cm(:))
+              ENDIF
+              IF (l_o_l) THEN
+                ib(1:2) = ia_sl(1:2); ie(1:2) = ib(1:2)+io_cl(1:2)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i4_2d(ib(1):ie(1),ib(2):ie(2)), &
+ &                start=io_sl(:),count=io_cl(:))
+              ENDIF
+              DEALLOCATE(i4_2d)
+            CASE (3) !--- 3d array
+              ALLOCATE(i4_3d(io_n(1),io_n(2),io_n(3)))
+              CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),i4_3d, &
+ &              start=io_i(:),count=io_n(:))
+              IF (l_o_f) THEN
+                ib(1:3) = ia_sf(1:3); ie(1:3) = ib(1:3)+io_cf(1:3)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i4_3d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3)), &
+ &                start=io_sf(:),count=io_cf(:))
+              ENDIF
+              IF (l_o_m) THEN
+                ib(1:3) = ia_sm(1:3); ie(1:3) = ib(1:3)+io_cm(1:3)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i4_3d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3)), &
+ &                start=io_sm(:),count=io_cm(:))
+              ENDIF
+              IF (l_o_l) THEN
+                ib(1:3) = ia_sl(1:3); ie(1:3) = ib(1:3)+io_cl(1:3)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i4_3d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3)), &
+ &                start=io_sl(:),count=io_cl(:))
+              ENDIF
+              DEALLOCATE(i4_3d)
+            CASE (4) !--- 4d array
+              ALLOCATE(i4_4d(io_n(1),io_n(2),io_n(3),io_n(4)))
+              CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),i4_4d, &
+ &              start=io_i(:),count=io_n(:))
+              IF (l_o_f) THEN
+                ib(1:4) = ia_sf(1:4); ie(1:4) = ib(1:4)+io_cf(1:4)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i4_4d(ib(1):ie(1),ib(2):ie(2), &
+ &                      ib(3):ie(3),ib(4):ie(4)), &
+ &                start=io_sf(:),count=io_cf(:))
+              ENDIF
+              IF (l_o_m) THEN
+                ib(1:4) = ia_sm(1:4); ie(1:4) = ib(1:4)+io_cm(1:4)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i4_4d(ib(1):ie(1),ib(2):ie(2), &
+ &                      ib(3):ie(3),ib(4):ie(4)), &
+ &                start=io_sm(:),count=io_cm(:))
+              ENDIF
+              IF (l_o_l) THEN
+                ib(1:4) = ia_sl(1:4); ie(1:4) = ib(1:4)+io_cl(1:4)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i4_4d(ib(1):ie(1),ib(2):ie(2), &
+ &                      ib(3):ie(3),ib(4):ie(4)), &
+ &                start=io_sl(:),count=io_cl(:))
+              ENDIF
+              DEALLOCATE(i4_4d)
+            CASE (5) !--- 5d array
+              ALLOCATE(i4_5d(io_n(1),io_n(2),io_n(3),io_n(4),io_n(5)))
+              CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),i4_5d, &
+ &              start=io_i(:),count=io_n(:))
+              IF (l_o_f) THEN
+                ib(1:5) = ia_sf(1:5); ie(1:5) = ib(1:5)+io_cf(1:5)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i4_5d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3), &
+ &                      ib(4):ie(4),ib(5):ie(5)), &
+ &                start=io_sf(:),count=io_cf(:))
+              ENDIF
+              IF (l_o_m) THEN
+                ib(1:5) = ia_sm(1:5); ie(1:5) = ib(1:5)+io_cm(1:5)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i4_5d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3), &
+ &                      ib(4):ie(4),ib(5):ie(5)), &
+ &                start=io_sm(:),count=io_cm(:))
+              ENDIF
+              IF (l_o_l) THEN
+                ib(1:5) = ia_sl(1:5); ie(1:5) = ib(1:5)+io_cl(1:5)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                i4_5d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3), &
+ &                      ib(4):ie(4),ib(5):ie(5)), &
+ &                start=io_sl(:),count=io_cl(:))
+              ENDIF
+              DEALLOCATE(i4_5d)
+            END SELECT
+          CASE (flio_r4) !--- REAL 4
+            SELECT CASE (v_d_nb(iv))
+            CASE (0) !--- Scalar
+              CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),r4_0d)
+              CALL flioputv (f_id_o,TRIM(f_v_nm(iv)),r4_0d)
+            CASE (1) !--- 1d array
+              ALLOCATE(r4_1d(io_n(1)))
+              CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),r4_1d, &
+ &              start=io_i(:),count=io_n(:))
+              IF (l_o_f) THEN
+                ib(1:1) = ia_sf(1:1); ie(1:1) = ib(1:1)+io_cf(1:1)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r4_1d(ib(1):ie(1)), &
+ &                start=io_sf(:),count=io_cf(:))
+              ENDIF
+              IF (l_o_m) THEN
+                ib(1:1) = ia_sm(1:1); ie(1:1) = ib(1:1)+io_cm(1:1)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r4_1d(ib(1):ie(1)), &
+ &                start=io_sm(:),count=io_cm(:))
+              ENDIF
+              IF (l_o_l) THEN
+                ib(1:1) = ia_sl(1:1); ie(1:1) = ib(1:1)+io_cl(1:1)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r4_1d(ib(1):ie(1)), &
+ &                start=io_sl(:),count=io_cl(:))
+              ENDIF
+              DEALLOCATE(r4_1d)
+            CASE (2) !--- 2d array
+              ALLOCATE(r4_2d(io_n(1),io_n(2)))
+              CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),r4_2d, &
+ &              start=io_i(:),count=io_n(:))
+              IF (l_o_f) THEN
+                ib(1:2) = ia_sf(1:2); ie(1:2) = ib(1:2)+io_cf(1:2)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r4_2d(ib(1):ie(1),ib(2):ie(2)), &
+ &                start=io_sf(:),count=io_cf(:))
+              ENDIF
+              IF (l_o_m) THEN
+                ib(1:2) = ia_sm(1:2); ie(1:2) = ib(1:2)+io_cm(1:2)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r4_2d(ib(1):ie(1),ib(2):ie(2)), &
+ &                start=io_sm(:),count=io_cm(:))
+              ENDIF
+              IF (l_o_l) THEN
+                ib(1:2) = ia_sl(1:2); ie(1:2) = ib(1:2)+io_cl(1:2)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r4_2d(ib(1):ie(1),ib(2):ie(2)), &
+ &                start=io_sl(:),count=io_cl(:))
+              ENDIF
+              DEALLOCATE(r4_2d)
+            CASE (3) !--- 3d array
+              ALLOCATE(r4_3d(io_n(1),io_n(2),io_n(3)))
+              CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),r4_3d, &
+ &              start=io_i(:),count=io_n(:))
+              IF (l_o_f) THEN
+                ib(1:3) = ia_sf(1:3); ie(1:3) = ib(1:3)+io_cf(1:3)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r4_3d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3)), &
+ &                start=io_sf(:),count=io_cf(:))
+              ENDIF
+              IF (l_o_m) THEN
+                ib(1:3) = ia_sm(1:3); ie(1:3) = ib(1:3)+io_cm(1:3)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r4_3d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3)), &
+ &                start=io_sm(:),count=io_cm(:))
+              ENDIF
+              IF (l_o_l) THEN
+                ib(1:3) = ia_sl(1:3); ie(1:3) = ib(1:3)+io_cl(1:3)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r4_3d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3)), &
+ &                start=io_sl(:),count=io_cl(:))
+              ENDIF
+              DEALLOCATE(r4_3d)
+            CASE (4) !--- 4d array
+              ALLOCATE(r4_4d(io_n(1),io_n(2),io_n(3),io_n(4)))
+              CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),r4_4d, &
+ &              start=io_i(:),count=io_n(:))
+              IF (l_o_f) THEN
+                ib(1:4) = ia_sf(1:4); ie(1:4) = ib(1:4)+io_cf(1:4)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r4_4d(ib(1):ie(1),ib(2):ie(2), &
+ &                      ib(3):ie(3),ib(4):ie(4)), &
+ &                start=io_sf(:),count=io_cf(:))
+              ENDIF
+              IF (l_o_m) THEN
+                ib(1:4) = ia_sm(1:4); ie(1:4) = ib(1:4)+io_cm(1:4)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r4_4d(ib(1):ie(1),ib(2):ie(2), &
+ &                      ib(3):ie(3),ib(4):ie(4)), &
+ &                start=io_sm(:),count=io_cm(:))
+              ENDIF
+              IF (l_o_l) THEN
+                ib(1:4) = ia_sl(1:4); ie(1:4) = ib(1:4)+io_cl(1:4)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r4_4d(ib(1):ie(1),ib(2):ie(2), &
+ &                      ib(3):ie(3),ib(4):ie(4)), &
+ &                start=io_sl(:),count=io_cl(:))
+              ENDIF
+              DEALLOCATE(r4_4d)
+            CASE (5) !--- 5d array
+              ALLOCATE(r4_5d(io_n(1),io_n(2),io_n(3),io_n(4),io_n(5)))
+              CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),r4_5d, &
+ &              start=io_i(:),count=io_n(:))
+              IF (l_o_f) THEN
+                ib(1:5) = ia_sf(1:5); ie(1:5) = ib(1:5)+io_cf(1:5)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r4_5d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3), &
+ &                      ib(4):ie(4),ib(5):ie(5)), &
+ &                start=io_sf(:),count=io_cf(:))
+              ENDIF
+              IF (l_o_m) THEN
+                ib(1:5) = ia_sm(1:5); ie(1:5) = ib(1:5)+io_cm(1:5)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r4_5d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3), &
+ &                      ib(4):ie(4),ib(5):ie(5)), &
+ &                start=io_sm(:),count=io_cm(:))
+              ENDIF
+              IF (l_o_l) THEN
+                ib(1:5) = ia_sl(1:5); ie(1:5) = ib(1:5)+io_cl(1:5)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r4_5d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3), &
+ &                      ib(4):ie(4),ib(5):ie(5)), &
+ &                start=io_sl(:),count=io_cl(:))
+              ENDIF
+              DEALLOCATE(r4_5d)
+            END SELECT
+          CASE (flio_r8) !--- REAL 8
+            SELECT CASE (v_d_nb(iv))
+            CASE (0) !--- Scalar
+              CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),r8_0d)
+              CALL flioputv (f_id_o,TRIM(f_v_nm(iv)),r8_0d)
+            CASE (1) !--- 1d array
+              ALLOCATE(r8_1d(io_n(1)))
+              CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),r8_1d, &
+ &              start=io_i(:),count=io_n(:))
+              IF (l_o_f) THEN
+                ib(1:1) = ia_sf(1:1); ie(1:1) = ib(1:1)+io_cf(1:1)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r8_1d(ib(1):ie(1)), &
+ &                start=io_sf(:),count=io_cf(:))
+              ENDIF
+              IF (l_o_m) THEN
+                ib(1:1) = ia_sm(1:1); ie(1:1) = ib(1:1)+io_cm(1:1)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r8_1d(ib(1):ie(1)), &
+ &                start=io_sm(:),count=io_cm(:))
+              ENDIF
+              IF (l_o_l) THEN
+                ib(1:1) = ia_sl(1:1); ie(1:1) = ib(1:1)+io_cl(1:1)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r8_1d(ib(1):ie(1)), &
+ &                start=io_sl(:),count=io_cl(:))
+              ENDIF
+              DEALLOCATE(r8_1d)
+            CASE (2) !--- 2d array
+              ALLOCATE(r8_2d(io_n(1),io_n(2)))
+              CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),r8_2d, &
+ &              start=io_i(:),count=io_n(:))
+              IF (l_o_f) THEN
+                ib(1:2) = ia_sf(1:2); ie(1:2) = ib(1:2)+io_cf(1:2)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r8_2d(ib(1):ie(1),ib(2):ie(2)), &
+ &                start=io_sf(:),count=io_cf(:))
+              ENDIF
+              IF (l_o_m) THEN
+                ib(1:2) = ia_sm(1:2); ie(1:2) = ib(1:2)+io_cm(1:2)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r8_2d(ib(1):ie(1),ib(2):ie(2)), &
+ &                start=io_sm(:),count=io_cm(:))
+              ENDIF
+              IF (l_o_l) THEN
+                ib(1:2) = ia_sl(1:2); ie(1:2) = ib(1:2)+io_cl(1:2)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r8_2d(ib(1):ie(1),ib(2):ie(2)), &
+ &                start=io_sl(:),count=io_cl(:))
+              ENDIF
+              DEALLOCATE(r8_2d)
+            CASE (3) !--- 3d array
+              ALLOCATE(r8_3d(io_n(1),io_n(2),io_n(3)))
+              CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),r8_3d, &
+ &              start=io_i(:),count=io_n(:))
+              IF (l_o_f) THEN
+                ib(1:3) = ia_sf(1:3); ie(1:3) = ib(1:3)+io_cf(1:3)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r8_3d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3)), &
+ &                start=io_sf(:),count=io_cf(:))
+              ENDIF
+              IF (l_o_m) THEN
+                ib(1:3) = ia_sm(1:3); ie(1:3) = ib(1:3)+io_cm(1:3)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r8_3d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3)), &
+ &                start=io_sm(:),count=io_cm(:))
+              ENDIF
+              IF (l_o_l) THEN
+                ib(1:3) = ia_sl(1:3); ie(1:3) = ib(1:3)+io_cl(1:3)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r8_3d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3)), &
+ &                start=io_sl(:),count=io_cl(:))
+              ENDIF
+              DEALLOCATE(r8_3d)
+            CASE (4) !--- 4d array
+              ALLOCATE(r8_4d(io_n(1),io_n(2),io_n(3),io_n(4)))
+              CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),r8_4d, &
+ &              start=io_i(:),count=io_n(:))
+              IF (l_o_f) THEN
+                ib(1:4) = ia_sf(1:4); ie(1:4) = ib(1:4)+io_cf(1:4)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r8_4d(ib(1):ie(1),ib(2):ie(2), &
+ &                      ib(3):ie(3),ib(4):ie(4)), &
+ &                start=io_sf(:),count=io_cf(:))
+              ENDIF
+              IF (l_o_m) THEN
+                ib(1:4) = ia_sm(1:4); ie(1:4) = ib(1:4)+io_cm(1:4)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r8_4d(ib(1):ie(1),ib(2):ie(2), &
+ &                      ib(3):ie(3),ib(4):ie(4)), &
+ &                start=io_sm(:),count=io_cm(:))
+              ENDIF
+              IF (l_o_l) THEN
+                ib(1:4) = ia_sl(1:4); ie(1:4) = ib(1:4)+io_cl(1:4)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r8_4d(ib(1):ie(1),ib(2):ie(2), &
+ &                      ib(3):ie(3),ib(4):ie(4)), &
+ &                start=io_sl(:),count=io_cl(:))
+              ENDIF
+              DEALLOCATE(r8_4d)
+            CASE (5) !--- 5d array
+              ALLOCATE(r8_5d(io_n(1),io_n(2),io_n(3),io_n(4),io_n(5)))
+              CALL fliogetv (f_id_i,TRIM(f_v_nm(iv)),r8_5d, &
+ &              start=io_i(:),count=io_n(:))
+              IF (l_o_f) THEN
+                ib(1:5) = ia_sf(1:5); ie(1:5) = ib(1:5)+io_cf(1:5)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r8_5d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3), &
+ &                      ib(4):ie(4),ib(5):ie(5)), &
+ &                start=io_sf(:),count=io_cf(:))
+              ENDIF
+              IF (l_o_m) THEN
+                ib(1:5) = ia_sm(1:5); ie(1:5) = ib(1:5)+io_cm(1:5)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r8_5d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3), &
+ &                      ib(4):ie(4),ib(5):ie(5)), &
+ &                start=io_sm(:),count=io_cm(:))
+              ENDIF
+              IF (l_o_l) THEN
+                ib(1:5) = ia_sl(1:5); ie(1:5) = ib(1:5)+io_cl(1:5)-1;
+                CALL flioputv (f_id_o,TRIM(f_v_nm(iv)), &
+ &                r8_5d(ib(1):ie(1),ib(2):ie(2),ib(3):ie(3), &
+ &                      ib(4):ie(4),ib(5):ie(5)), &
+ &                start=io_sl(:),count=io_cl(:))
+              ENDIF
+              DEALLOCATE(r8_5d)
+            END SELECT
+          END SELECT
+!-------- eventually close each file containing a small piece of data
+          CALL flrb_cf (i_i,l_ocf.AND.l_cgd.AND.(i_i /= 1))
+        ENDDO
+!------ If needed, deallocate io_* arrays
+        IF (v_d_nb(iv) > 0) THEN
+          DEALLOCATE(io_i,io_n,ia_sm,io_sm,io_cm)
+          IF (TRIM(c_d_n) == "apple") THEN
+            DEALLOCATE(ia_sf,io_sf,io_cf)
+            DEALLOCATE(ia_sl,io_sl,io_cl)
+          ENDIF
+        ENDIF
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+!-------------------
+! Ending the work
+!-------------------
+!-
+! Close files
+  CALL flrb_cf (0,.TRUE.)
+!-
+! Deallocate
+  DEALLOCATE(f_nm,f_a_id)
+  DEALLOCATE(f_d_nm,f_v_nm,f_a_nm)
+  DEALLOCATE(f_d_i,f_d_l)
+  DEALLOCATE(v_d_nb,v_d_ul,v_type,v_d_i)
+  DEALLOCATE(d_d_i,d_s_g)
+  DEALLOCATE(d_s_l,d_p_f,d_p_l,d_h_s,d_h_e)
+!-
+  IF (i_v_lev >= 1) THEN
+!-- elapsed and cpu time computation
+    CALL cpu_time (t_cpu_end)
+    CALL system_clock(count=nb_cc_end)
+    WRITE (UNIT=*,FMT='("")')
+    WRITE (UNIT=*,fmt='(" elapsed time (s) : ",1PE11.4)') &
+ &   REAL(nb_cc_end-nb_cc_ini)/REAL(nb_cc_sec)
+    WRITE (UNIT=*,fmt='(" CPU time (s) : ",1PE11.4)') &
+ &   t_cpu_end-t_cpu_ini
+  ENDIF
+!=======
+CONTAINS
+!=======
+SUBROUTINE flrb_of (i_f_n,i_f_i)
+!---------------------------------------------------------------------
+! Open the file of number "i_f_n" if necessary,
+! and returns its identifier in "i_f_i".
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN)  :: i_f_n
+  INTEGER,INTENT(OUT) :: i_f_i
+!---------------------------------------------------------------------
+  IF (f_a_id(i_f_n) < 0) THEN
+    CALL flioopfd (TRIM(f_nm(i_f_n)),i_f_i)
+    f_a_id(i_f_n) = i_f_i
+  ELSE
+    i_f_i = f_a_id(i_f_n)
+  ENDIF
+!---------------------
+END SUBROUTINE flrb_of
+!===
+SUBROUTINE flrb_cf (i_f_n,l_cf)
+!---------------------------------------------------------------------
+! Close the file of number "i_f_n" if "l_cf" is TRUE.
+! Close all files if "i_f_n <= 0".
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: i_f_n
+  LOGICAL,INTENT(IN) :: l_cf
+!---------------------------------------------------------------------
+  IF (i_f_n <= 0) THEN
+    CALL flioclo ()
+    f_a_id(:) = -1
+  ELSE
+    IF (l_cf) THEN
+      IF (f_a_id(i_f_n) < 0) THEN
+        CALL ipslerr (2,"flio_rbld", &
+ &       "The file",TRIM(f_nm(i_f_n)),"is already closed")
+      ELSE
+        CALL flioclo (f_a_id(i_f_n))
+        f_a_id(i_f_n) = -1
+      ENDIF
+    ENDIF
+  ENDIF
+!---------------------
+END SUBROUTINE flrb_cf
+!===
+SUBROUTINE flrb_rg
+!---------------------------------------------------------------------
+! Update valid_min valid_max attributes values
+!---------------------------------------------------------------------
+  INTEGER :: k,j
+  LOGICAL :: l_vmin,l_vmax
+  INTEGER(KIND=i_4) :: i4_vmin,i4_vmax
+  REAL(KIND=r_4) :: r4_vmin,r4_vmax
+  REAL(KIND=r_8) :: r8_vmin,r8_vmax
+!---------------------------------------------------------------------
+  DO k=1,f_v_nb
+!-- get attribute informations
+    CALL flioinqa &
+ &    (f_id_i1,TRIM(f_v_nm(k)),'valid_min',l_vmin,a_t=a_type)
+    CALL flioinqa &
+ &    (f_id_i1,TRIM(f_v_nm(k)),'valid_max',l_vmax,a_t=a_type)
+!---
+    IF (l_vmin.OR.l_vmax) THEN
+!---- get values of min/max
+      SELECT CASE (a_type)
+      CASE (flio_i1,flio_i2,flio_i4) !--- INTEGER 1/2/4
+        DO j=1,f_nb_in
+          CALL flrb_of (j,f_id_i)
+          IF (l_vmin) THEN
+            CALL fliogeta(f_id_i,TRIM(f_v_nm(k)),"valid_min",i4_0d)
+            IF (j == 1) THEN
+              i4_vmin = i4_0d
+            ELSE
+              i4_vmin = MIN(i4_vmin,i4_0d)
+            ENDIF
+          ENDIF
+          IF (l_vmax) THEN
+            CALL fliogeta(f_id_i,TRIM(f_v_nm(k)),"valid_max",i4_0d)
+            IF (j == 1) THEN
+              i4_vmax = i4_0d
+            ELSE
+              i4_vmax = MAX(i4_vmax,i4_0d)
+            ENDIF
+          ENDIF
+          CALL flrb_cf (j,l_ocf.AND.(f_id_i /= f_id_i1))
+        ENDDO
+        IF (l_vmin) THEN
+          CALL flioputa (f_id_o,TRIM(f_v_nm(k)),"valid_min",i4_vmin)
+        ENDIF
+        IF (l_vmax) THEN
+          CALL flioputa (f_id_o,TRIM(f_v_nm(k)),"valid_max",i4_vmax)
+        ENDIF
+      CASE (flio_r4) !--- REAL 4
+        DO j=1,f_nb_in
+          CALL flrb_of (j,f_id_i)
+          IF (l_vmin) THEN
+            CALL fliogeta(f_id_i,TRIM(f_v_nm(k)),"valid_min",r4_0d)
+            IF (j == 1) THEN
+              r4_vmin = r4_0d
+            ELSE
+              r4_vmin = MIN(r4_vmin,r4_0d)
+            ENDIF
+          ENDIF
+          IF (l_vmax) THEN
+            CALL fliogeta(f_id_i,TRIM(f_v_nm(k)),"valid_max",r4_0d)
+            IF (j == 1) THEN
+              r4_vmax = r4_0d
+            ELSE
+              r4_vmax = MAX(r4_vmax,r4_0d)
+            ENDIF
+          ENDIF
+          CALL flrb_cf (j,l_ocf.AND.(f_id_i /= f_id_i1))
+        ENDDO
+        IF (l_vmin) THEN
+          CALL flioputa (f_id_o,TRIM(f_v_nm(k)),"valid_min",r4_vmin)
+        ENDIF
+        IF (l_vmax) THEN
+          CALL flioputa (f_id_o,TRIM(f_v_nm(k)),"valid_max",r4_vmax)
+        ENDIF
+      CASE (flio_r8) !--- REAL 8
+        DO j=1,f_nb_in
+          CALL flrb_of (j,f_id_i)
+          IF (l_vmin) THEN
+            CALL fliogeta(f_id_i,TRIM(f_v_nm(k)),"valid_min",r8_0d)
+            IF (j == 1) THEN
+              r8_vmin = r8_0d
+            ELSE
+              r8_vmin = MIN(r8_vmin,r8_0d)
+            ENDIF
+          ENDIF
+          IF (l_vmax) THEN
+            CALL fliogeta(f_id_i,TRIM(f_v_nm(k)),"valid_max",r8_0d)
+            IF (j == 1) THEN
+              r8_vmax = r8_0d
+            ELSE
+              r8_vmax = MAX(r8_vmax,r8_0d)
+            ENDIF
+          ENDIF
+          CALL flrb_cf (j,l_ocf.AND.(f_id_i /= f_id_i1))
+        ENDDO
+        IF (l_vmin) THEN
+          CALL flioputa (f_id_o,TRIM(f_v_nm(k)),"valid_min",r8_vmin)
+        ENDIF
+        IF (l_vmax) THEN
+          CALL flioputa (f_id_o,TRIM(f_v_nm(k)),"valid_max",r8_vmax)
+        ENDIF
+      END SELECT
+    ENDIF
+  ENDDO
+!---------------------
+END SUBROUTINE flrb_rg
+!===
+!--------------------
+END PROGRAM flio_rbld
Index: /IOIPSL/tags/v2_2_5/rebuild/rebuild
===================================================================
--- /IOIPSL/tags/v2_2_5/rebuild/rebuild	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/rebuild/rebuild	(revision 5058)
@@ -0,0 +1,110 @@
+#!/bin/ksh
+#
+#$Id: rebuild 761 2009-10-26 16:30:14Z bellier $
+#
+# This software is governed by the CeCILL license
+# See IOIPSL/IOIPSL_License_CeCILL.txt
+#---------------------------------------------------------------------
+# @(#)Rebuild IOIPSL domains
+#---------------------------------------------------------------------
+function rebuild_Usage
+{
+print - "
+\"${b_n}\"
+  rebuild a model_file from several input files.
+Each input file contains the model_data for a domain.
+
+Usage :
+  ${b_n} [-h]
+  ${b_n} [-v level] [-f] -o output_file_name input_file_names
+
+Options :
+  -h         : help
+  -v O/1/2/3 : verbose mode (verbosity increasing with level)
+  -f         : executing mode
+               (execute the program even if the number of input files
+                is not equal to the total number of domains)
+"
+}
+#-
+#set -xv
+#-
+# Extract the calling sequence of the script (d_n/b_n)
+#-
+d_n=${0%/*}; b_n=${0##*/};
+#-
+# Retrieving the options
+#-
+r_v='0'; r_f='noforce'; r_o="";
+while getopts :hv:fo: V
+ do
+  case $V in
+   (h) rebuild_Usage; exit 0;;
+   (v) r_v=${OPTARG};;
+   (f) r_f='force';;
+   (o) r_o=${OPTARG};;
+   (:) print -u2 "${b_n} : missing value for option $OPTARG"; exit 2;;
+   (\?) print -u2 "${b_n} : option $OPTARG not supported"; exit 2;;
+  esac
+ done
+shift $(($OPTIND-1));
+#-
+# Validate the -v option
+#-
+case ${r_v} in
+  ( 0 | 1 | 2 | 3 );;
+  ("") r_v='0';;
+  (*)
+    print -u2 "${b_n} :";
+    print -u2 "Invalid verbosity level requested : ${r_v}";
+    print -u2 "(must be 0, 1, 2 or 3)";
+    exit 1;;
+esac
+#-
+# Validate the number of arguments
+#-
+[[ ${#} < 1 ]] && \
+ {
+  print -u2 "${b_n} : Too few arguments have been specified. (Use -h)";
+  exit 3;
+ }
+#-
+# Check for the output file name
+#-
+[[ -z ${r_o} ]] && \
+ {
+  r_o='rebuilt_file.nc';
+  print -u2 - "
+   ${b_n} : output_file_name not specified. (Use -h)
+            rebuilt_file.nc should be created."
+ }
+#-
+# Validate the names of the input files
+#-
+for i in $*;
+ do
+  [[ ! -f ${i} ]] && { echo "${i} unreachable ..."; exit 3;}
+ done
+#-
+# Create the information file for the program
+#-
+echo ${r_v} > tmp.$$;
+echo ${r_f} >> tmp.$$;
+echo $((${#}+1)) >> tmp.$$;
+for i in $*;
+ do echo ${i} >> tmp.$$;
+ done
+echo ${r_o} >> tmp.$$;
+#-
+# Create the output file
+#-
+${d_n}/flio_rbld < tmp.$$
+r_c=$?
+#-
+# Clear
+#-
+rm -f tmp.$$
+#-
+# End
+#-
+exit ${r_c};
Index: /IOIPSL/tags/v2_2_5/src/AA_make
===================================================================
--- /IOIPSL/tags/v2_2_5/src/AA_make	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/src/AA_make	(revision 5058)
@@ -0,0 +1,113 @@
+#-
+#- $Id$
+#-
+MAKE_NAM = $(MAKE)
+ifneq ($(MAKE_NAM),$(M_K))
+ @$(error You must invoke this Makefile with the $(M_K) command)
+endif
+USER_DIR = $(shell /bin/pwd)
+MAKE_DIR = '??'
+ifneq ($(USER_DIR),$(MAKE_DIR))
+ @$(error You must invoke this Makefile from its directory)
+endif
+#-
+#-Q- sxdkrz F_O = $(F_D) $(F_P) -C vsafe -size_t64 -I $(MODDIR)
+#-Q- eshpux F_O = $(F_D) $(F_P) -C vsafe -I $(MODDIR)
+#---------------------------------------------------------------------
+#- Create "libioipsl"
+#---------------------------------------------------------------------
+
+.PRECIOUS : $(MODEL_LIB)
+SXMODEL_LIB = $(MODEL_LIB)
+ifeq ($(L_X),1)
+SXMODEL_LIB = $(LIBDIR)/libsxioipsl.a
+.PRECIOUS : $(SXMODEL_LIB)
+endif
+
+MODS1 = defprec.f90   \
+        errioipsl.f90 \
+        stringop.f90  \
+        mathelp.f90   \
+        getincom.f90  \
+        calendar.f90  \
+        fliocom.f90   \
+        flincom.f90   \
+        histcom.f90   \
+        restcom.f90   \
+        ioipsl.f90
+OBJSMODS1 = $(MODS1:.f90=.o)
+
+all: $(MODEL_LIB)($(OBJSMODS1))
+	@echo IOIPSL is OK
+
+$(MODEL_LIB)(%.o): %.f90
+	$(F_C) $(F_O) -I$(NCDF_INC) $*.f90
+	$(A_C) $(MODEL_LIB) $*.o
+ifeq ($(L_X),1)
+	$(A_X) $(SXMODEL_LIB) $*.o
+endif
+#-Q- sgi6 	mv $(shell echo $* | tr '[:lower:]' '[:upper:]').mod $(MODDIR)
+ifeq ($(M_M),1)
+	mv $*.mod $(MODDIR)
+endif
+	$(RM) $*.o
+
+clean:
+	$(RM) $(MODEL_LIB)
+ifeq ($(L_X),1)
+	$(RM) $(SXMODEL_LIB)
+endif
+	$(RM) *.*~ Makefile~ core *.o *.mod i.*.L *.L i.*.f90
+	$(RM) $(MODDIR)/*.mod $(MODDIR)/*.M *.M
+
+#- Specific dependencies
+
+$(MODEL_LIB)(errioipsl.o):   \
+  $(MODEL_LIB)(defprec.o)
+
+$(MODEL_LIB)(stringop.o):    \
+  $(MODEL_LIB)(defprec.o)
+
+$(MODEL_LIB)(mathelp.o):     \
+  $(MODEL_LIB)(errioipsl.o)  \
+  $(MODEL_LIB)(stringop.o)
+
+$(MODEL_LIB)(getincom.o):    \
+  $(MODEL_LIB)(stringop.o)
+
+$(MODEL_LIB)(calendar.o):    \
+  $(MODEL_LIB)(errioipsl.o)  \
+  $(MODEL_LIB)(stringop.o)
+
+$(MODEL_LIB)(fliocom.o):     \
+  $(MODEL_LIB)(defprec.o)    \
+  $(MODEL_LIB)(errioipsl.o)  \
+  $(MODEL_LIB)(stringop.o)   \
+  $(MODEL_LIB)(calendar.o)
+
+$(MODEL_LIB)(flincom.o):     \
+  $(MODEL_LIB)(errioipsl.o)  \
+  $(MODEL_LIB)(stringop.o)   \
+  $(MODEL_LIB)(calendar.o)
+
+$(MODEL_LIB)(histcom.o):     \
+  $(MODEL_LIB)(errioipsl.o)  \
+  $(MODEL_LIB)(stringop.o)   \
+  $(MODEL_LIB)(mathelp.o)    \
+  $(MODEL_LIB)(calendar.o)   \
+  $(MODEL_LIB)(fliocom.o)
+
+$(MODEL_LIB)(restcom.o):     \
+  $(MODEL_LIB)(errioipsl.o)  \
+  $(MODEL_LIB)(stringop.o)   \
+  $(MODEL_LIB)(mathelp.o)    \
+  $(MODEL_LIB)(calendar.o)   \
+  $(MODEL_LIB)(fliocom.o)
+
+$(MODEL_LIB)(ioipsl.o):      \
+  $(MODEL_LIB)(getincom.o)   \
+  $(MODEL_LIB)(fliocom.o)    \
+  $(MODEL_LIB)(flincom.o)    \
+  $(MODEL_LIB)(histcom.o)    \
+  $(MODEL_LIB)(restcom.o)
+
Index: /IOIPSL/tags/v2_2_5/src/AA_make.ldef
===================================================================
--- /IOIPSL/tags/v2_2_5/src/AA_make.ldef	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/src/AA_make.ldef	(revision 5058)
@@ -0,0 +1,14 @@
+#-
+#- $Id$
+#-
+#---------------------------------------------------------------------
+#- Local parameters for "libioipsl"
+#---------------------------------------------------------------------
+SHELL = /bin/ksh
+#---------------------------------------------------------------------
+LIBDIR = ../../../lib
+BINDIR = ../../../bin
+MODDIR = $(LIBDIR)
+#---------------------------------------------------------------------
+P_P =
+MODEL_LIB = $(LIBDIR)/libioipsl.a
Index: /IOIPSL/tags/v2_2_5/src/calendar.f90
===================================================================
--- /IOIPSL/tags/v2_2_5/src/calendar.f90	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/src/calendar.f90	(revision 5058)
@@ -0,0 +1,1046 @@
+MODULE calendar
+!-
+!$Id$
+!-
+! This software is governed by the CeCILL license
+! See IOIPSL/IOIPSL_License_CeCILL.txt
+!---------------------------------------------------------------------
+!- This is the calendar which going to be used to do all
+!- calculations on time. Three types of calendars are possible :
+!-
+!-  - gregorian :
+!-      The normal calendar. The time origin for the
+!-      julian day in this case is 24 Nov -4713
+!-      (other names : 'standard','proleptic_gregorian')
+!-  - noleap :
+!-      A 365 day year without leap years.
+!-      The origin for the julian days is in this case 1 Jan 0
+!-      (other names : '365_day','365d')
+!-  - all_leap :
+!-      A 366 day year with leap years.
+!-      The origin for the julian days is in this case ????
+!-      (other names : '366_day','366d'
+!-  - julian :
+!-      same as gregorian, but with all leap century years
+!-  - xxxd :
+!-      Year of xxx days with month of equal length.
+!-      The origin for the julian days is then also 1 Jan 0
+!-
+!- As one can see it is difficult to go from one calendar to the other.
+!- All operations involving julian days will be wrong.
+!- This calendar will lock as soon as possible
+!- the length of the year and forbid any further modification.
+!-
+!- For the non leap-year calendar the method is still brute force.
+!- We need to find an Integer series which takes care of the length
+!- of the various month. (Jan)
+!-
+!-   one_day  : one day in seconds
+!-   one_year : one year in days
+!---------------------------------------------------------------------
+  USE stringop,ONLY  : strlowercase
+  USE errioipsl,ONLY : ipslerr, ipsldbg, ipslout
+!-
+  PRIVATE
+  PUBLIC :: ymds2ju,ju2ymds,tlen2itau,isittime,ioconf_calendar, &
+ &          ioget_calendar,ioget_mon_len,ioget_year_len,itau2date, &
+ &          ioget_timestamp,ioconf_startdate,itau2ymds, &
+ &          time_diff,time_add,lock_calendar
+!-
+  INTERFACE ioget_calendar
+    MODULE PROCEDURE &
+ &    ioget_calendar_real1,ioget_calendar_real2,ioget_calendar_str
+  END INTERFACE
+!-
+  INTERFACE ioconf_startdate
+     MODULE PROCEDURE &
+ &    ioconf_startdate_simple,ioconf_startdate_internal, &
+ &    ioconf_startdate_ymds
+  END INTERFACE
+!-
+  REAL,PARAMETER :: one_day = 86400.0
+  LOGICAL,SAVE :: lock_startdate = .FALSE.
+!-
+  CHARACTER(LEN=30),SAVE :: time_stamp='XXXXXXXXXXXXXXXX'
+!-
+!- Description of calendar
+!-
+  CHARACTER(LEN=20),SAVE :: calendar_used="gregorian"
+  LOGICAL,SAVE :: lock_one_year = .FALSE.
+  REAL,SAVE :: one_year = 365.2425
+  INTEGER,SAVE :: mon_len(12)=(/31,28,31,30,31,30,31,31,30,31,30,31/)
+!-
+  CHARACTER(LEN=3),PARAMETER :: &
+ &  cal(12) = (/'JAN','FEB','MAR','APR','MAY','JUN', &
+ &              'JUL','AUG','SEP','OCT','NOV','DEC'/)
+!-
+  REAL,SAVE :: start_day,start_sec
+!-
+CONTAINS
+!-
+!===
+!-
+SUBROUTINE lock_calendar (new_status,old_status)
+!!--------------------------------------------------------------------
+!! The "lock_calendar" routine
+!! allows to lock or unlock the calendar,
+!! and to know the current status of the calendar.
+!! Be careful !
+!!
+!! SUBROUTINE lock_calendar (new_status,old_status)
+!!
+!! Optional INPUT argument
+!!
+!! (L) new_status : new status of the calendar
+!!
+!! Optional OUTPUT argument
+!!
+!! (L) old_status : current status of the calendar
+!!--------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  LOGICAL,OPTIONAL,INTENT(IN)  :: new_status
+  LOGICAL,OPTIONAL,INTENT(OUT) :: old_status
+!---------------------------------------------------------------------
+  IF (PRESENT(old_status)) THEN
+    old_status = lock_one_year
+  ENDIF
+  IF (PRESENT(new_status)) THEN
+    lock_one_year = new_status
+  ENDIF
+!---------------------------
+END SUBROUTINE lock_calendar
+!-
+!===
+!-
+SUBROUTINE ymds2ju (year,month,day,sec,julian)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: year,month,day
+  REAL,INTENT(IN)    :: sec
+!-
+  REAL,INTENT(OUT) :: julian
+!-
+  INTEGER :: julian_day
+  REAL    :: julian_sec
+!---------------------------------------------------------------------
+  CALL ymds2ju_internal (year,month,day,sec,julian_day,julian_sec)
+!-
+  julian = julian_day+julian_sec/one_day
+!---------------------
+END SUBROUTINE ymds2ju
+!-
+!===
+!-
+SUBROUTINE ymds2ju_internal (year,month,day,sec,julian_day,julian_sec)
+!---------------------------------------------------------------------
+!- Converts year, month, day and seconds into a julian day
+!-
+!- In 1968 in a letter to the editor of Communications of the ACM
+!- (CACM, volume 11, number 10, October 1968, p.657) Henry F. Fliegel
+!- and Thomas C. Van Flandern presented such an algorithm.
+!-
+!- See also : http://www.magnet.ch/serendipity/hermetic/cal_stud/jdn.htm
+!-
+!- In the case of the Gregorian calendar we have chosen to use
+!- the Lilian day numbers. This is the day counter which starts
+!- on the 15th October 1582.
+!- This is the day at which Pope Gregory XIII introduced the
+!- Gregorian calendar.
+!- Compared to the true Julian calendar, which starts some
+!- 7980 years ago, the Lilian days are smaler and are dealt with
+!- easily on 32 bit machines. With the true Julian days you can only
+!- the fraction of the day in the real part to a precision of
+!- a 1/4 of a day with 32 bits.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: year,month,day
+  REAL,INTENT(IN)    :: sec
+!-
+  INTEGER,INTENT(OUT) :: julian_day
+  REAL,INTENT(OUT)    :: julian_sec
+!-
+  INTEGER :: jd,m,y,d,ml
+!---------------------------------------------------------------------
+  lock_one_year = .TRUE.
+!-
+  m = month
+  y = year
+  d = day
+!-
+!- We deduce the calendar from the length of the year as it
+!- is faster than an INDEX on the calendar variable.
+!-
+  IF ( (one_year > 365.0).AND.(one_year < 366.0) ) THEN
+!-- "Gregorian"
+    jd = (1461*(y+4800+INT((m-14)/12)))/4 &
+ &      +(367*(m-2-12*(INT((m-14)/12))))/12 &
+ &      -(3*((y+4900+INT((m-14)/12))/100))/4 &
+ &      +d-32075
+    jd = jd-2299160
+  ELSE IF (    (ABS(one_year-365.0) <= EPSILON(one_year))  &
+ &         .OR.(ABS(one_year-366.0) <= EPSILON(one_year)) ) THEN
+!-- "No leap" or "All leap"
+    ml = SUM(mon_len(1:m-1))
+    jd = y*NINT(one_year)+ml+(d-1)
+  ELSE
+!-- Calendar with regular month
+    ml = NINT(one_year/12.)
+    jd = y*NINT(one_year)+(m-1)*ml+(d-1)
+  ENDIF
+!-
+  julian_day = jd
+  julian_sec = sec
+!------------------------------
+END SUBROUTINE ymds2ju_internal
+!-
+!===
+!-
+SUBROUTINE ju2ymds (julian,year,month,day,sec)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  REAL,INTENT(IN) :: julian
+!-
+  INTEGER,INTENT(OUT) :: year,month,day
+  REAL,INTENT(OUT)    :: sec
+!-
+  INTEGER :: julian_day
+  REAL    :: julian_sec
+!---------------------------------------------------------------------
+  julian_day = INT(julian)
+  julian_sec = (julian-julian_day)*one_day
+!-
+  CALL ju2ymds_internal(julian_day,julian_sec,year,month,day,sec)
+!---------------------
+END SUBROUTINE ju2ymds
+!-
+!===
+!-
+SUBROUTINE ju2ymds_internal (julian_day,julian_sec,year,month,day,sec)
+!---------------------------------------------------------------------
+!- This subroutine computes from the julian day the year,
+!- month, day and seconds
+!-
+!- In 1968 in a letter to the editor of Communications of the ACM
+!- (CACM, volume 11, number 10, October 1968, p.657) Henry F. Fliegel
+!- and Thomas C. Van Flandern presented such an algorithm.
+!-
+!- See also : http://www.magnet.ch/serendipity/hermetic/cal_stud/jdn.htm
+!-
+!- In the case of the Gregorian calendar we have chosen to use
+!- the Lilian day numbers. This is the day counter which starts
+!- on the 15th October 1582. This is the day at which Pope
+!- Gregory XIII introduced the Gregorian calendar.
+!- Compared to the true Julian calendar, which starts some 7980
+!- years ago, the Lilian days are smaler and are dealt with easily
+!- on 32 bit machines. With the true Julian days you can only the
+!- fraction of the day in the real part to a precision of a 1/4 of
+!- a day with 32 bits.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: julian_day
+  REAL,INTENT(IN)    :: julian_sec
+!-
+  INTEGER,INTENT(OUT) :: year,month,day
+  REAL,INTENT(OUT)    :: sec
+!-
+  INTEGER :: l,n,i,jd,j,d,m,y,ml
+  INTEGER :: add_day
+  REAL :: eps_day
+!---------------------------------------------------------------------
+  eps_day = SPACING(one_day)
+  lock_one_year = .TRUE.
+!-
+  jd = julian_day
+  sec = julian_sec
+  IF (sec > (one_day-eps_day)) THEN
+    add_day = INT(sec/one_day)
+    sec = sec-add_day*one_day
+    jd = jd+add_day
+  ENDIF
+  IF (sec < -eps_day) THEN
+    sec = sec+one_day
+    jd = jd-1
+  ENDIF
+!-
+  IF ( (one_year > 365.0).AND.(one_year < 366.0) ) THEN
+!-- Gregorian
+    jd = jd+2299160
+!-
+    l = jd+68569
+    n = (4*l)/146097
+    l = l-(146097*n+3)/4
+    i = (4000*(l+1))/1461001
+    l = l-(1461*i)/4+31
+    j = (80*l)/2447
+    d = l-(2447*j)/80
+    l = j/11
+    m = j+2-(12*l)
+    y = 100*(n-49)+i+l
+  ELSE IF (    (ABS(one_year-365.0) <= EPSILON(one_year)) &
+ &         .OR.(ABS(one_year-366.0) <= EPSILON(one_year)) ) THEN
+!-- No leap or All leap
+    y = jd/NINT(one_year)
+    l = jd-y*NINT(one_year)
+    m = 1
+    ml = 0
+    DO WHILE (ml+mon_len(m) <= l)
+      ml = ml+mon_len(m)
+      m = m+1
+    ENDDO
+    d = l-ml+1
+  ELSE
+!-- others
+    ml = NINT(one_year/12.)
+    y = jd/NINT(one_year)
+    l = jd-y*NINT(one_year)
+    m = (l/ml)+1
+    d = l-(m-1)*ml+1
+  ENDIF
+!-
+  day = d
+  month = m
+  year = y
+!------------------------------
+END SUBROUTINE ju2ymds_internal
+!-
+!===
+!-
+SUBROUTINE tlen2itau (input_str,dt,date,itau)
+!---------------------------------------------------------------------
+!- This subroutine transforms a string containing a time length
+!- into a number of time steps.
+!- To do this operation the date (in julian days is needed as the
+!- length of the month varies.
+!- The following convention is used :
+!-   n   : n time steps
+!-   nS  : n seconds is transformed into itaus
+!-   nH  : n hours
+!-   nD  : n days
+!-   nM  : n month
+!-   nY  : n years
+!- Combinations are also possible
+!-   nYmD : nyears plus m days !
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*),INTENT(IN) :: input_str
+  REAL,INTENT(IN)             :: dt,date
+!-
+  INTEGER,INTENT(OUT)         :: itau
+!-
+  INTEGER           :: y_pos,m_pos,d_pos,h_pos,s_pos
+  INTEGER           :: read_time
+  CHARACTER(LEN=13) :: fmt
+  CHARACTER(LEN=80) :: tmp_str
+!-
+  INTEGER :: year,month,day
+  REAL    :: sec,date_new,dd,ss
+!---------------------------------------------------------------------
+  itau = 0
+  CALL ju2ymds (date,year,month,day,sec)
+!-
+  y_pos = MAX(INDEX(input_str,'y'),INDEX(input_str,'Y'))
+  m_pos = MAX(INDEX(input_str,'m'),INDEX(input_str,'M'))
+  d_pos = MAX(INDEX(input_str,'d'),INDEX(input_str,'D'))
+  h_pos = MAX(INDEX(input_str,'h'),INDEX(input_str,'H'))
+  s_pos = MAX(INDEX(input_str,'s'),INDEX(input_str,'S'))
+!-
+  IF (MAX(y_pos,m_pos,d_pos,s_pos) > 0) THEN
+    tmp_str = input_str
+    DO WHILE ( MAX(y_pos,m_pos,d_pos,s_pos) > 0)
+!---- WRITE(ipslout,*) tmp_str
+!---- WRITE(ipslout,*) y_pos,m_pos,d_pos,s_pos
+      IF (y_pos > 0) THEN
+        WRITE(fmt,'("(I",I10.10,")")') y_pos-1
+        READ(tmp_str(1:y_pos-1),fmt) read_time
+        CALL ymds2ju (year+read_time,month,day,sec,date_new)
+        dd = date_new-date
+        ss = INT(dd)*one_day+dd-INT(dd)
+        itau = itau+NINT(ss/dt)
+        tmp_str = tmp_str(y_pos+1:LEN_TRIM(tmp_str))
+      ELSE IF (m_pos > 0) THEN
+        WRITE(fmt,'("(I",I10.10,")")') m_pos-1
+        READ(tmp_str(1:m_pos-1),fmt) read_time
+        CALL ymds2ju (year,month+read_time,day,sec,date_new)
+        dd = date_new-date
+        ss = INT(dd)*one_day+dd-INT(dd)
+        itau = itau+NINT(ss/dt)
+        tmp_str = tmp_str(m_pos+1:LEN_TRIM(tmp_str))
+      ELSE IF (d_pos > 0) THEN
+        WRITE(fmt,'("(I",I10.10,")")') d_pos-1
+        READ(tmp_str(1:d_pos-1),fmt) read_time
+        itau = itau+NINT(read_time*one_day/dt)
+        tmp_str = tmp_str(d_pos+1:LEN_TRIM(tmp_str))
+      ELSE IF (h_pos > 0) THEN
+        WRITE(fmt,'("(I",I10.10,")")') h_pos-1
+        READ(tmp_str(1:h_pos-1),fmt) read_time
+        itau = itau+NINT(read_time*60.*60./dt)
+        tmp_str = tmp_str(d_pos+1:LEN_TRIM(tmp_str))
+      ELSE IF  (s_pos > 0) THEN
+        WRITE(fmt,'("(I",I10.10,")")') s_pos-1
+        READ(tmp_str(1:s_pos-1),fmt) read_time
+        itau = itau+NINT(read_time/dt)
+        tmp_str = tmp_str(s_pos+1:LEN_TRIM(tmp_str))
+      ENDIF
+!-
+      y_pos = MAX(INDEX(tmp_str,'y'),INDEX(tmp_str,'Y'))
+      m_pos = MAX(INDEX(tmp_str,'m'),INDEX(tmp_str,'M'))
+      d_pos = MAX(INDEX(tmp_str,'d'),INDEX(tmp_str,'D'))
+      h_pos = MAX(INDEX(tmp_str,'h'),INDEX(tmp_str,'H'))
+      s_pos = MAX(INDEX(tmp_str,'s'),INDEX(tmp_str,'S'))
+    ENDDO
+  ELSE
+    WRITE(fmt,'("(I",I10.10,")")') LEN_TRIM(input_str)
+    READ(input_str(1:LEN_TRIM(input_str)),fmt) itau
+  ENDIF
+!-----------------------
+END SUBROUTINE tlen2itau
+!-
+!===
+!-
+REAL FUNCTION itau2date (itau,date0,deltat)
+!---------------------------------------------------------------------
+!- This function transforms itau into a date. The date with which
+!- the time axis is going to be labeled
+!-
+!- INPUT
+!-   itau   : current time step
+!-   date0  : Date at which itau was equal to 0
+!-   deltat : time step between itau s
+!-
+!- OUTPUT
+!-   itau2date : Date for the given itau
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER  :: itau
+  REAL     :: date0,deltat
+!---------------------------------------------------------------------
+  itau2date = REAL(itau)*deltat/one_day+date0
+!---------------------
+END FUNCTION itau2date
+!-
+!===
+!-
+SUBROUTINE itau2ymds (itau,deltat,year,month,day,sec)
+!---------------------------------------------------------------------
+!- This subroutine transforms itau into a date. The date with which
+!- the time axis is going to be labeled
+!-
+!- INPUT
+!-   itau   : current time step
+!-   deltat : time step between itau s
+!-
+!- OUTPUT
+!-   year  : year
+!-   month : month
+!-   day   : day
+!-   sec   : seconds since midnight
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: itau
+  REAL,INTENT(IN)    :: deltat
+!-
+  INTEGER,INTENT(OUT) :: year,month,day
+  REAL,INTENT(OUT)    :: sec
+!-
+  INTEGER :: julian_day
+  REAL    :: julian_sec
+!---------------------------------------------------------------------
+  IF (.NOT.lock_startdate) THEN
+    CALL ipslerr (2,'itau2ymds', &
+ &   'You try to call this function, itau2ymds, but you didn''t', &
+ &   ' call ioconf_startdate to initialize date0 in calendar.', &
+ &   ' Please call ioconf_startdate before itau2ymds.')
+  ENDIF
+  julian_day = start_day
+  julian_sec = start_sec+REAL(itau)*deltat
+  CALL ju2ymds_internal (julian_day,julian_sec,year,month,day,sec)
+!-----------------------
+END SUBROUTINE itau2ymds
+!-
+!===
+!-
+REAL FUNCTION dtchdate (itau,date0,old_dt,new_dt)
+!---------------------------------------------------------------------
+!- This function changes the date so that the simulation can
+!- continue with the same itau but a different dt.
+!-
+!- INPUT
+!-   itau   : current time step
+!-   date0  : Date at which itau was equal to 0
+!-   old_dt : Old time step between itaus
+!-   new_dt : New time step between itaus
+!-
+!- OUTPUT
+!-   dtchdate : Date for the given itau
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: itau
+  REAL,INTENT(IN)    :: date0,old_dt,new_dt
+!-
+  REAL :: rtime
+!---------------------------------------------------------------------
+  rtime = itau2date (itau,date0,old_dt)
+  dtchdate = rtime-REAL(itau)*new_dt/one_day
+!--------------------
+END FUNCTION dtchdate
+!-
+!===
+!-
+SUBROUTINE isittime &
+ &  (itau,date0,dt,freq,last_action,last_check,do_action)
+!---------------------------------------------------------------------
+!- This subroutine checks the time as come for a given action.
+!- This is computed from the current time-step(itau).
+!- Thus we need to have the time delta (dt), the frequency
+!- of the action (freq) and the last time it was done
+!- (last_action in units of itau).
+!- In order to extrapolate when will be the next check we need
+!- the time step of the last call (last_check).
+!-
+!- The test is done on the following condition :
+!- the distance from the current time to the time for the next
+!- action is smaller than the one from the next expected
+!- check to the next action.
+!- When the test is done on the time steps simplifications make
+!- it more difficult to read in the code.
+!- For the real time case it is easier to understand !
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: itau
+  REAL,INTENT(IN)    :: dt,freq
+  INTEGER,INTENT(IN) :: last_action,last_check
+  REAL,INTENT(IN)    :: date0
+!-
+  LOGICAL,INTENT(OUT)  :: do_action
+!-
+  REAL :: dt_action,dt_check
+  REAL :: date_last_act,date_next_check,date_next_act, &
+ &        date_now,date_mp1,date_mpf
+  INTEGER :: year,month,monthp1,day,next_check_itau,next_act_itau
+  INTEGER :: yearp,dayp
+  REAL :: sec,secp
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!---------------------------------------------------------------------
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) &
+ &    "isittime 1.0 ",itau,date0,dt,freq,last_action,last_check
+  ENDIF
+!-
+  IF (last_check >= 0) THEN
+    dt_action = (itau-last_action)*dt
+    dt_check = (itau-last_check)*dt
+    next_check_itau = itau+(itau-last_check)
+!-
+!-- We are dealing with frequencies in seconds and thus operation
+!-- can be done on the time steps.
+!-
+    IF (freq > 0) THEN
+      IF (ABS(dt_action-freq) <= ABS(dt_action+dt_check-freq)) THEN
+        do_action = .TRUE.
+      ELSE
+        do_action = .FALSE.
+      ENDIF
+!-
+!---- Here we deal with frequencies in month and work on julian days.
+!-
+    ELSE
+      date_now = itau2date (itau,date0,dt)
+      date_last_act = itau2date (last_action,date0,dt)
+      CALL ju2ymds (date_last_act,year,month,day,sec)
+      monthp1 = month-freq
+      yearp = year
+!-
+!---- Here we compute what logically should be the next month
+!-
+      IF (month >= 13) THEN
+        yearp = year+1
+        monthp1 = monthp1-12
+      ENDIF
+      CALL ymds2ju (year,monthp1,day,sec,date_mpf)
+!-
+!---- But it could be that because of a shorter month or a bad
+!---- starting date that we end up further than we should be.
+!---- Thus we compute the first day of the next month.
+!---- We can not be beyond this date and if we are close
+!---- then we will take it as it is better.
+!-
+      monthp1 = month+ABS(freq)
+      yearp=year
+      IF (monthp1 >= 13) THEN
+        yearp = year+1
+        monthp1 = monthp1 -12
+      ENDIF
+      dayp = 1
+      secp = 0.0
+      CALL ymds2ju (yearp,monthp1,dayp,secp,date_mp1)
+!-
+!---- If date_mp1 is smaller than date_mpf or only less than 4 days
+!---- larger then we take it. This needed to ensure that short month
+!---- like February do not mess up the thing !
+!-
+      IF (date_mp1-date_mpf < 4.) THEN
+        date_next_act = date_mp1
+      ELSE
+        date_next_act = date_mpf
+      ENDIF
+      date_next_check = itau2date (next_check_itau,date0,dt)
+!-
+!---- Transform the dates into time-steps for the needed precisions.
+!-
+      next_act_itau = &
+ &      last_action+INT((date_next_act-date_last_act)*(one_day/dt))
+!-----
+      IF (   ABS(itau-next_act_itau) &
+ &        <= ABS( next_check_itau-next_act_itau)) THEN
+        do_action = .TRUE.
+        IF (l_dbg) THEN
+          WRITE(ipslout,*) &
+ &         'ACT-TIME : itau, next_act_itau, next_check_itau : ', &
+ &         itau,next_act_itau,next_check_itau
+          CALL ju2ymds (date_now,year,month,day,sec)
+          WRITE(ipslout,*) 'ACT-TIME : y, m, d, s : ',year,month,day,sec
+          WRITE(ipslout,*) &
+ &         'ACT-TIME : date_mp1, date_mpf : ',date_mp1,date_mpf
+        ENDIF
+      ELSE
+        do_action = .FALSE.
+      ENDIF
+    ENDIF
+!-
+    IF (l_dbg) THEN
+      WRITE(ipslout,*) "isittime 2.0 ", &
+ &     date_next_check,date_next_act,ABS(dt_action-freq), &
+ &     ABS(dt_action+dt_check-freq),dt_action,dt_check, &
+ &     next_check_itau,do_action
+    ENDIF
+  ELSE
+    do_action=.FALSE.
+  ENDIF
+!----------------------
+END SUBROUTINE isittime
+!-
+!===
+!-
+SUBROUTINE ioconf_calendar (str)
+!---------------------------------------------------------------------
+!- This routine allows to configure the calendar to be used.
+!- This operation is only allowed once and the first call to
+!- ymds2ju or ju2ymsd will lock the current configuration.
+!- the argument to ioconf_calendar can be any of the following :
+!-  - gregorian : This is the gregorian calendar (default here)
+!-  - noleap    : A calendar without leap years = 365 days
+!-  - xxxd      : A calendar of xxx days (has to be a modulo of 12)
+!-                with 12 month of equal length
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*),INTENT(IN) :: str
+!-
+  INTEGER :: leng,ipos
+  CHARACTER(LEN=20) :: str_w
+!---------------------------------------------------------------------
+!-
+! Clean up the string !
+!-
+  str_w = str
+  CALL strlowercase (str_w)
+!-
+  IF (.NOT.lock_one_year) THEN
+!---
+    lock_one_year=.TRUE.
+!---
+    SELECT CASE(TRIM(str_w))
+    CASE('gregorian','standard','proleptic_gregorian')
+      calendar_used = 'gregorian'
+      one_year = 365.2425
+      mon_len(:)=(/31,28,31,30,31,30,31,31,30,31,30,31/)
+    CASE('noleap','365_day','365d')
+      calendar_used = 'noleap'
+      one_year = 365.0
+      mon_len(:)=(/31,28,31,30,31,30,31,31,30,31,30,31/)
+    CASE('all_leap','366_day','366d')
+      calendar_used = 'all_leap'
+      one_year = 366.0
+      mon_len(:)=(/31,29,31,30,31,30,31,31,30,31,30,31/)
+    CASE('360_day','360d')
+      calendar_used = '360d'
+      one_year = 360.0
+      mon_len(:)=(/30,30,30,30,30,30,30,30,30,30,30,30/)
+    CASE('julian')
+      calendar_used = 'julian'
+      one_year = 365.25
+      mon_len(:)=(/31,28,31,30,31,30,31,31,30,31,30,31/)
+    CASE DEFAULT
+      ipos = INDEX(str_w,'d')
+      IF (ipos == 4) THEN
+        READ(str_w(1:3),'(I3)') leng
+        IF ( (MOD(leng,12) == 0).AND.(leng > 1) ) THEN
+          calendar_used = str_w
+          one_year = leng
+          mon_len(:) = leng/12
+        ELSE
+          CALL ipslerr (3,'ioconf_calendar', &
+ &         'The length of the year as to be a modulo of 12', &
+ &         'so that it can be divided into 12 month of equal length', &
+ &         TRIM(str_w))
+        ENDIF
+      ELSE
+        CALL ipslerr (3,'ioconf_calendar', &
+ &       'Unrecognized input, please check the man pages.', &
+ &       TRIM(str_w),' ')
+      ENDIF
+    END SELECT
+  ELSE IF (TRIM(str_w) /= TRIM(calendar_used)) THEN
+    WRITE(str_w,'(f10.4)') one_year
+    CALL ipslerr (2,'ioconf_calendar', &
+ &   'The calendar was already used or configured to : '// &
+ &    TRIM(calendar_used)//'.', &
+ &   'You are not allowed to change it to : '//TRIM(str)//'.', &
+ &   'The following length of year is used : '//TRIM(ADJUSTL(str_w)))
+  ENDIF
+!-----------------------------
+END SUBROUTINE ioconf_calendar
+!-
+!===
+!-
+SUBROUTINE ioconf_startdate_simple (julian)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  REAL,INTENT(IN) :: julian
+!-
+  INTEGER :: julian_day
+  REAL    :: julian_sec
+!---------------------------------------------------------------------
+  julian_day = INT(julian)
+  julian_sec = (julian-julian_day)*one_day
+!-
+  CALL ioconf_startdate_internal (julian_day,julian_sec)
+!-------------------------------------
+END SUBROUTINE ioconf_startdate_simple
+!-
+!===
+!-
+SUBROUTINE ioconf_startdate_ymds (year,month,day,sec)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: year,month,day
+  REAL,INTENT(IN)    :: sec
+!-
+  INTEGER :: julian_day
+  REAL    :: julian_sec
+!---------------------------------------------------------------------
+  CALL ymds2ju_internal (year,month,day,sec,julian_day,julian_sec)
+!-
+  CALL ioconf_startdate_internal (julian_day,julian_sec)
+!-----------------------------------
+END SUBROUTINE ioconf_startdate_ymds
+!-
+!===
+!-
+SUBROUTINE ioconf_startdate_internal (julian_day,julian_sec)
+!---------------------------------------------------------------------
+! This subroutine allows to set the startdate for later
+! use. It allows the applications to access the date directly from
+! the timestep. In order to avoid any problems the start date will
+! be locked and can not be changed once set.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN)  :: julian_day
+  REAL,INTENT(IN)     :: julian_sec
+!-
+  CHARACTER(len=70) :: str70a,str70b
+!---------------------------------------------------------------------
+  IF (.NOT.lock_startdate) THEN
+    start_day = julian_day
+    start_sec = julian_sec
+    lock_startdate = .TRUE.
+  ELSE
+    WRITE(str70a,'("The date you tried to set : ",f10.4)') &
+ &   julian_day,julian_sec/one_day
+    WRITE(str70b, &
+ &   '("The date which was already set in the calendar : ",f10.4)') &
+ &   start_day+start_sec/one_day
+    CALL ipslerr (2,'ioconf_startdate', &
+ &   'The start date has already been set and you tried to change it', &
+ &   str70a,str70b)
+  ENDIF
+!---------------------------------------
+END SUBROUTINE ioconf_startdate_internal
+!-
+!===
+!-
+SUBROUTINE ioget_calendar_str (str)
+!---------------------------------------------------------------------
+!- This subroutine returns the name of the calendar used here.
+!- Three options exist :
+!-  - gregorian : This is the gregorian calendar (default here)
+!-  - noleap    : A calendar without leap years = 365 days
+!-  - xxxd      : A calendar of xxx days (has to be a modulo of 12)
+!-                with 12 month of equal length
+!-
+!- This routine will lock the calendar.
+!- You do not want it to change after your inquiry.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*),INTENT(OUT) :: str
+!---------------------------------------------------------------------
+  lock_one_year = .TRUE.
+!-
+  str = calendar_used
+!--------------------------------
+END SUBROUTINE ioget_calendar_str
+!-
+!===
+!-
+SUBROUTINE ioget_calendar_real1 (long_year)
+!---------------------------------------------------------------------
+!- This subroutine returns the name of the calendar used here.
+!- Three options exist :
+!-  - gregorian : This is the gregorian calendar (default here)
+!-  - noleap    : A calendar without leap years = 365 days
+!-  - xxxd      : A calendar of xxx days (has to be a modulo of 12)
+!-                with 12 month of equal length
+!-
+!- This routine will lock the calendar.
+!- You do not want it to change after your inquiry.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  REAL,INTENT(OUT) :: long_year
+!---------------------------------------------------------------------
+  long_year = one_year
+  lock_one_year = .TRUE.
+!----------------------------------
+END SUBROUTINE ioget_calendar_real1
+!-
+!===
+!-
+SUBROUTINE ioget_calendar_real2 (long_year,long_day)
+!---------------------------------------------------------------------
+!- This subroutine returns the name of the calendar used here.
+!- Three options exist :
+!-  - gregorian : This is the gregorian calendar (default here)
+!-  - noleap    : A calendar without leap years = 365 days
+!-  - xxxd      : A calendar of xxx days (has to be a modulo of 12)
+!-                with 12 month of equal length
+!-
+!- This routine will lock the calendar.
+!- You do not want it to change after your inquiry.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  REAL,INTENT(OUT) :: long_year,long_day
+!---------------------------------------------------------------------
+  long_year = one_year
+  long_day  = one_day
+  lock_one_year = .TRUE.
+!----------------------------------
+END SUBROUTINE ioget_calendar_real2
+!-
+!===
+!-
+INTEGER FUNCTION ioget_mon_len (year,month)
+!!--------------------------------------------------------------------
+!! The "ioget_mon_len" function returns
+!! the number of days in a "month" of a "year",
+!! in the current calendar.
+!!
+!! INTEGER FUNCTION ioget_mon_len (year,month)
+!!
+!! INPUT
+!!
+!! (I) year  : year
+!! (I) month : month in the year (1 --> 12)
+!!
+!! OUTPUT
+!!
+!! (I) ioget_mon_len : number of days in the month
+!!--------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: year,month
+!-
+  INTEGER :: ml
+!---------------------------------------------------------------------
+  IF ( (month >= 1).AND.(month <= 12) ) THEN
+    IF ( (one_year > 365.0).AND.(one_year < 366.0) ) THEN
+!---- "Gregorian" or "Julian"
+      ml = mon_len(month)
+      IF (month == 2) THEN
+        IF (ABS(one_year-365.2425) <= EPSILON(one_year) ) THEN
+!-------- "Gregorian"
+          IF (    ((MOD(year,4) == 0).AND.(MOD(year,100) /= 0)) &
+              .OR.(MOD(year,400) == 0) ) THEN
+            ml = ml+1
+          ENDIF
+        ELSE
+!-------- "Julian"
+          IF (MOD(year,4) == 0) THEN
+            ml = ml+1
+          ENDIF
+        ENDIF
+      ENDIF
+      ioget_mon_len = ml
+    ELSE
+!---- "No leap" or "All leap" or "Calendar with regular month"
+      ioget_mon_len = mon_len(month)
+    ENDIF
+  ELSE
+    CALL ipslerr (3,'ioget_mon_len', &
+ &    'The number of the month','must be between','1 and 12')
+  ENDIF
+!-------------------------
+END FUNCTION ioget_mon_len
+!-
+!===
+!-
+INTEGER FUNCTION ioget_year_len (year)
+!!--------------------------------------------------------------------
+!! The "ioget_year_len" function returns
+!! the number of days in "year", in the current calendar.
+!!
+!! INTEGER FUNCTION ioget_year_len (year)
+!!
+!! INPUT
+!!
+!! (I) year  : year
+!!
+!! OUTPUT
+!!
+!! (I) ioget_year_len : number of days in the year
+!!--------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: year
+!-
+  INTEGER :: yl
+!---------------------------------------------------------------------
+  SELECT CASE(TRIM(calendar_used))
+  CASE('gregorian')
+    yl = 365
+    IF (    ((MOD(year,4) == 0).AND.(MOD(year,100) /= 0)) &
+        .OR.(MOD(year,400) == 0) ) THEN
+      yl = yl+1
+    ENDIF
+  CASE('julian')
+    yl = 365
+    IF (MOD(year,4) == 0) THEN
+      yl = yl+1
+    ENDIF
+  CASE DEFAULT
+    yl = NINT(one_year)
+  END SELECT
+  ioget_year_len = yl
+!--------------------------
+END FUNCTION ioget_year_len
+!-
+!===
+!-
+SUBROUTINE ioget_timestamp (string)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=30),INTENT(OUT) :: string
+!-
+  INTEGER :: date_time(8)
+  CHARACTER(LEN=10) :: bigben(3)
+!---------------------------------------------------------------------
+  IF (INDEX(time_stamp,'XXXXXX') > 0) THEN
+    CALL DATE_AND_TIME (bigben(1),bigben(2),bigben(3),date_time)
+!---
+    WRITE(time_stamp, &
+ &   "(I4.4,'-',A3,'-',I2.2,' ',I2.2,':',I2.2,':',I2.2,' GMT',a5)") &
+ &   date_time(1),cal(date_time(2)),date_time(3),date_time(5), &
+ &   date_time(6),date_time(7),bigben(3)
+  ENDIF
+!-
+  string = time_stamp
+!-----------------------------
+END SUBROUTINE ioget_timestamp
+!-
+!===
+!-
+SUBROUTINE time_add &
+ &  (year_s,month_s,day_s,sec_s,sec_increment, &
+ &   year_e,month_e,day_e,sec_e)
+!---------------------------------------------------------------------
+!- This subroutine allows to increment a date by a number of seconds.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: year_s,month_s,day_s
+  REAL,INTENT(IN)    :: sec_s
+!-
+! Time in seconds to be added to the date
+!-
+  REAL,INTENT(IN)    :: sec_increment
+!-
+  INTEGER,INTENT(OUT) :: year_e,month_e,day_e
+  REAL,INTENT(OUT)    :: sec_e
+!-
+  INTEGER :: julian_day
+  REAL    :: julian_sec
+!---------------------------------------------------------------------
+  CALL ymds2ju_internal &
+ &  (year_s,month_s,day_s,sec_s,julian_day,julian_sec)
+!-
+  julian_sec = julian_sec+sec_increment
+!-
+  CALL ju2ymds_internal &
+ &  (julian_day,julian_sec,year_e,month_e,day_e,sec_e)
+!----------------------
+END SUBROUTINE time_add
+!-
+!===
+!-
+SUBROUTINE time_diff &
+ &  (year_s,month_s,day_s,sec_s,year_e,month_e,day_e,sec_e,sec_diff)
+!---------------------------------------------------------------------
+!- This subroutine allows to determine the number of seconds
+!- between two dates.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: year_s,month_s,day_s
+  REAL,INTENT(IN)    :: sec_s
+  INTEGER,INTENT(IN) :: year_e,month_e,day_e
+  REAL,INTENT(IN)    :: sec_e
+!-
+! Time in seconds between the two dates
+!-
+  REAL,INTENT(OUT)    :: sec_diff
+!-
+  INTEGER :: julian_day_s,julian_day_e,day_diff
+  REAL    :: julian_sec_s,julian_sec_e
+!---------------------------------------------------------------------
+  CALL ymds2ju_internal &
+ &  (year_s,month_s,day_s,sec_s,julian_day_s,julian_sec_s)
+  CALL ymds2ju_internal &
+ &  (year_e,month_e,day_e,sec_e,julian_day_e,julian_sec_e)
+!-
+  day_diff = julian_day_e-julian_day_s
+  sec_diff = julian_sec_e-julian_sec_s
+!-
+  sec_diff = sec_diff+day_diff*one_day
+!-----------------------
+END SUBROUTINE time_diff
+!-
+!===
+!-
+END MODULE calendar
Index: /IOIPSL/tags/v2_2_5/src/def.prec
===================================================================
--- /IOIPSL/tags/v2_2_5/src/def.prec	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/src/def.prec	(revision 5058)
@@ -0,0 +1,22 @@
+MODULE defprec 
+!-
+! $Id$
+!-
+! This software is governed by the CeCILL license
+! See IOIPSL/IOIPSL_License_CeCILL.txt
+!!--------------------------------------------------------------------
+!! The module "defprec" set default precision for computation
+!!
+!! This module should be used by every modules
+!! to keep the right precision for every variable
+!!--------------------------------------------------------------------
+!?INTEGERS of KIND 1 are not supported on all computers
+!?INTEGER,PARAMETER :: i_1=SELECTED_INT_KIND(2)
+  INTEGER,PARAMETER :: i_2=SELECTED_INT_KIND(4)
+  INTEGER,PARAMETER :: i_4=SELECTED_INT_KIND(9)
+  INTEGER,PARAMETER :: i_8=SELECTED_INT_KIND(13)
+  INTEGER,PARAMETER :: r_4=SELECTED_REAL_KIND(6,37)
+  INTEGER,PARAMETER :: r_8=SELECTED_REAL_KIND(15,307)
+  INTEGER,PARAMETER :: i_std=i_?, r_std=r_?
+!-----------------
+END MODULE defprec
Index: /IOIPSL/tags/v2_2_5/src/errioipsl.f90
===================================================================
--- /IOIPSL/tags/v2_2_5/src/errioipsl.f90	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/src/errioipsl.f90	(revision 5058)
@@ -0,0 +1,216 @@
+MODULE errioipsl
+!-
+!$Id$
+!-
+! This software is governed by the CeCILL license
+! See IOIPSL/IOIPSL_License_CeCILL.txt
+!---------------------------------------------------------------------
+IMPLICIT NONE
+!-
+PRIVATE
+!-
+PUBLIC :: ipslnlf, ipslerr, ipslerr_act, ipslerr_inq, histerr, ipsldbg, ipslout
+!-
+  INTEGER :: ipslout=6, ilv_cur=0, ilv_max=0
+  LOGICAL :: ioipsl_debug=.FALSE., lact_mode=.TRUE.
+!-
+!===
+CONTAINS
+!===
+SUBROUTINE ipslnlf (new_number,old_number)
+!!--------------------------------------------------------------------
+!! The "ipslnlf" routine allows to know and modify
+!! the current logical number for the messages.
+!!
+!! SUBROUTINE ipslnlf (new_number,old_number)
+!!
+!! Optional INPUT argument
+!!
+!! (I) new_number : new logical number of the file
+!!
+!! Optional OUTPUT argument
+!!
+!! (I) old_number : current logical number of the file
+!!--------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,OPTIONAL,INTENT(IN)  :: new_number
+  INTEGER,OPTIONAL,INTENT(OUT) :: old_number
+!---------------------------------------------------------------------
+  IF (PRESENT(old_number)) THEN
+    old_number = ipslout
+  ENDIF
+  IF (PRESENT(new_number)) THEN
+    ipslout = new_number
+  ENDIF
+!---------------------
+END SUBROUTINE ipslnlf
+!===
+SUBROUTINE ipslerr (plev,pcname,pstr1,pstr2,pstr3)
+!---------------------------------------------------------------------
+!! The "ipslerr" routine
+!! allows to handle the messages to the user.
+!!
+!! INPUT
+!!
+!! plev   : Category of message to be reported to the user
+!!          1 = Note to the user
+!!          2 = Warning to the user
+!!          3 = Fatal error
+!! pcname : Name of subroutine which has called ipslerr
+!! pstr1   
+!! pstr2  : Strings containing the explanations to the user
+!! pstr3
+!---------------------------------------------------------------------
+   IMPLICIT NONE
+!-
+   INTEGER :: plev
+   CHARACTER(LEN=*) :: pcname,pstr1,pstr2,pstr3
+!-
+   CHARACTER(LEN=30),DIMENSION(3) :: pemsg = &
+  &  (/ "NOTE TO THE USER FROM ROUTINE ", &
+  &     "WARNING FROM ROUTINE          ", &
+  &     "FATAL ERROR FROM ROUTINE      " /)
+!---------------------------------------------------------------------
+   IF ( (plev >= 1).AND.(plev <= 3) ) THEN
+     ilv_cur = plev
+     ilv_max = MAX(ilv_max,plev)
+     WRITE(ipslout,'(/,A," ",A)') TRIM(pemsg(plev)),TRIM(pcname)
+     WRITE(ipslout,'(3(" --> ",A,/))') TRIM(pstr1),TRIM(pstr2),TRIM(pstr3)
+   ENDIF
+   IF ( (plev == 3).AND.lact_mode) THEN
+     WRITE(ipslout,'("Fatal error from IOIPSL. STOP in ipslerr with code")')
+     STOP 1
+   ENDIF
+!---------------------
+END SUBROUTINE ipslerr
+!===
+SUBROUTINE ipslerr_act (new_mode,old_mode)
+!!--------------------------------------------------------------------
+!! The "ipslerr_act" routine allows to know and modify
+!! the current "action mode" for the error messages,
+!! and reinitialize the error level values.
+!!
+!! SUBROUTINE ipslerr_act (new_mode,old_mode)
+!!
+!! Optional INPUT argument
+!!
+!! (I) new_mode : new error action mode
+!!                .TRUE.  -> STOP     in case of fatal error
+!!                .FALSE. -> CONTINUE in case of fatal error
+!!
+!! Optional OUTPUT argument
+!!
+!! (I) old_mode : current error action mode
+!!--------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  LOGICAL,OPTIONAL,INTENT(IN)  :: new_mode
+  LOGICAL,OPTIONAL,INTENT(OUT) :: old_mode
+!---------------------------------------------------------------------
+  IF (PRESENT(old_mode)) THEN
+    old_mode = lact_mode
+  ENDIF
+  IF (PRESENT(new_mode)) THEN
+    lact_mode = new_mode
+  ENDIF
+  ilv_cur = 0
+  ilv_max = 0
+!-------------------------
+END SUBROUTINE ipslerr_act
+!===
+SUBROUTINE ipslerr_inq (current_level,maximum_level)
+!!--------------------------------------------------------------------
+!! The "ipslerr_inq" routine allows to know
+!! the current level of the error messages
+!! and the maximum level encountered since the
+!! last call to "ipslerr_act".
+!!
+!! SUBROUTINE ipslerr_inq (current_level,maximum_level)
+!!
+!! Optional OUTPUT argument
+!!
+!! (I) current_level : current error level
+!! (I) maximum_level : maximum error level
+!!--------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,OPTIONAL,INTENT(OUT) :: current_level,maximum_level
+!---------------------------------------------------------------------
+  IF (PRESENT(current_level)) THEN
+    current_level = ilv_cur
+  ENDIF
+  IF (PRESENT(maximum_level)) THEN
+    maximum_level = ilv_max
+  ENDIF
+!-------------------------
+END SUBROUTINE ipslerr_inq
+!===
+SUBROUTINE histerr (plev,pcname,pstr1,pstr2,pstr3)
+!---------------------------------------------------------------------
+!- INPUT
+!- plev   : Category of message to be reported to the user
+!-          1 = Note to the user
+!-          2 = Warning to the user
+!-          3 = Fatal error
+!- pcname : Name of subroutine which has called histerr
+!- pstr1   
+!- pstr2  : String containing the explanations to the user
+!- pstr3
+!---------------------------------------------------------------------
+   IMPLICIT NONE
+!-
+   INTEGER :: plev
+   CHARACTER(LEN=*) :: pcname,pstr1,pstr2,pstr3
+!-
+   CHARACTER(LEN=30),DIMENSION(3) :: pemsg = &
+  &  (/ "NOTE TO THE USER FROM ROUTINE ", &
+  &     "WARNING FROM ROUTINE          ", &
+  &     "FATAL ERROR FROM ROUTINE      " /)
+!---------------------------------------------------------------------
+   IF ( (plev >= 1).AND.(plev <= 3) ) THEN
+     WRITE(*,'("     ")')
+     WRITE(*,'(A," ",A)') TRIM(pemsg(plev)),TRIM(pcname)
+     WRITE(*,'(" --> ",A)') pstr1
+     WRITE(*,'(" --> ",A)') pstr2
+     WRITE(*,'(" --> ",A)') pstr3
+   ENDIF
+   IF (plev == 3) THEN
+     WRITE(ipslout,'("Fatal error from IOIPSL. See stdout for more details")')
+     STOP 1 
+   ENDIF
+!---------------------
+END SUBROUTINE histerr
+!===
+SUBROUTINE ipsldbg (new_status,old_status)
+!!--------------------------------------------------------------------
+!! The "ipsldbg" routine
+!! allows to activate or deactivate the debug,
+!! and to know the current status of the debug.
+!!
+!! SUBROUTINE ipsldbg (new_status,old_status)
+!!
+!! Optional INPUT argument
+!!
+!! (L) new_status : new status of the debug
+!!
+!! Optional OUTPUT argument
+!!
+!! (L) old_status : current status of the debug
+!!--------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  LOGICAL,OPTIONAL,INTENT(IN)  :: new_status
+  LOGICAL,OPTIONAL,INTENT(OUT) :: old_status
+!---------------------------------------------------------------------
+  IF (PRESENT(old_status)) THEN
+    old_status = ioipsl_debug
+  ENDIF
+  IF (PRESENT(new_status)) THEN
+    ioipsl_debug = new_status
+  ENDIF
+!---------------------
+END SUBROUTINE ipsldbg
+!===
+!-------------------
+END MODULE errioipsl
Index: /IOIPSL/tags/v2_2_5/src/flincom.f90
===================================================================
--- /IOIPSL/tags/v2_2_5/src/flincom.f90	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/src/flincom.f90	(revision 5058)
@@ -0,0 +1,2049 @@
+MODULE flincom
+!-
+!$Id$
+!-
+! This software is governed by the CeCILL license
+! See IOIPSL/IOIPSL_License_CeCILL.txt
+!---------------------------------------------------------------------
+  USE netcdf
+!-
+  USE calendar,  ONLY : ju2ymds, ymds2ju, ioconf_calendar
+  USE errioipsl, ONLY : histerr, ipslout,ipslerr,ipsldbg
+  USE stringop,  ONLY : strlowercase
+!-
+  IMPLICIT NONE
+!-
+  PRIVATE
+  PUBLIC :: flinput, flincre, flinget, flinclo, &
+            flinopen, flininfo, flininspect, flinquery_var
+!-
+  INTERFACE flinopen
+!---------------------------------------------------------------------
+!- The "flinopen" routines will open an input file
+!-
+!- INPUT
+!-
+!- filename  : Name of the netCDF file to be opened
+!-
+!- iideb     : index i for zoom     !
+!- iilen     : length  of  zoom     !   for
+!- jjdeb     : index j for zoom     !   zoom
+!- jjlen     : length  of  zoom     !
+!-
+!- do_test   : A flag that enables the testing of the content
+!-             of the file against the input from the model
+!-
+!- INPUT if do_test=TRUE      OUTPUT else
+!-
+!- iim       : size in the x direction in the file (longitude)
+!- jjm       : size in the y direction
+!- llm       : number of levels
+!-             (llm = 0 means no axis to be expected)
+!- lon       : array of (iilen,jjlen) (zoom), or (iim,jjm) (no zoom),
+!-             that contains the longitude of each point
+!- lat       : same for latitude
+!- lev       : An array of llm for the latitude
+!-
+!- WARNING :
+!- In the case of do_test=FALSE it is for the user to check
+!- that the dimensions of lon lat and lev are correct when passed to
+!- flinopen. This can be done after the call when iim and jjm have
+!- been retrieved from the netCDF file. In F90 this problem will
+!- be solved with an internal assign
+!- IF iim, jjm, llm or ttm are parameters in the calling program and
+!- you use the option do_test=FALSE it will create a segmentation fault
+!-
+!-  OUTPUT
+!-
+!-  ttm       : size of time axis
+!-  itaus     : Time steps within this file
+!-  date0     : Julian date at which itau = 0
+!-  dt        : length of the time steps of the data
+!-  fid       : returned file ID which is later used to read the data
+!---------------------------------------------------------------------
+    MODULE PROCEDURE flinopen_zoom2d, flinopen_nozoom
+  END INTERFACE
+!-
+  INTERFACE flinput
+!---------------------------------------------------------------------
+!- The "flinput" routines will put a variable
+!- on the netCDF file created by flincre.
+!- If the sizes of the axis do not match the one of the IDs
+!- then a new axis is created.
+!- That is we loose the possibility of writting hyperslabs of data.
+!-
+!- Again here if iim = jjm = llm = ttm = 0
+!- then a global attribute is added to the file.
+!-
+!- INPUT
+!-
+!- fid      : Identification of the file in which we will write
+!- varname  : Name of variable to be written
+!- iim      : size in x of variable
+!- nlonid   : ID of x axis which could fit for this axis
+!- jjm      : size in y of variable
+!- nlatid   : ID of y axis which could fit for this axis
+!- llm      : size in z of variable
+!- zdimid   : ID of z axis which could fit for this axis
+!- ttm      : size in t of variable
+!- tdimid   : ID of t axis which could fit for this axis
+!-
+!- OUTPUT
+!-
+!- NONE
+!---------------------------------------------------------------------
+    MODULE PROCEDURE flinput_r4d, flinput_r3d, flinput_r2d, &
+                     flinput_r1d, flinput_scal
+  END INTERFACE
+!-
+  INTERFACE flinget
+    MODULE PROCEDURE flinget_r4d, flinget_r3d, flinget_r2d, &
+                     flinget_r1d, flinget_scal, &
+                     flinget_r4d_zoom2d, flinget_r3d_zoom2d, &
+                     flinget_r2d_zoom2d
+  END INTERFACE
+!-
+! This is the data we keep on each file we open
+!-
+  INTEGER, PARAMETER :: nbfile_max = 200
+  INTEGER, SAVE :: nbfiles = 0
+  INTEGER, SAVE :: ncids(nbfile_max), ncnbd(nbfile_max), &
+                   ncfunli(nbfile_max), ncnba(nbfile_max)
+  INTEGER, SAVE :: ncnbva(nbfile_max), ncdims(nbfile_max,4)
+  LOGICAL, SAVE :: ncfileopen(nbfile_max)=.FALSE.
+!-
+  INTEGER, SAVE :: cind_vid, cind_fid, cind_len
+  INTEGER,DIMENSION(:),ALLOCATABLE,SAVE :: cindex
+!-
+  INTEGER,DIMENSION(4) :: w_sta, w_len, w_dim
+!-
+CONTAINS
+!-
+!===
+!-
+SUBROUTINE flincre &
+  (filename, iim1, jjm1, lon1, lat1, llm1, lev1, ttm1, itaus, &
+   time0, dt, fid_out, nlonid1, nlatid1, zdimid1, tdimid1)
+!---------------------------------------------------------------------
+!- This is a "low level" subroutine for opening netCDF files wich
+!- contain the major coordinate system of the model.
+!- Other coordinates needed for other variables
+!- will be added as they are needed.
+!-
+!- INPUT
+!-
+!- filename    : Name of the file to be created
+!- iim1, jjm1  : Horizontal size of the grid
+!-               which will be stored in the file
+!- lon1, lat1  : Horizontal grids
+!- llm1        : Size of the vertical grid
+!- lev1        : Vertical grid
+!- ttm1        : Size of time axis
+!- itaus       : time steps on the time axis
+!- time0       : Time in julian days at which itau = 0
+!- dt          : time step in seconds between itaus
+!-               (one step of itau)
+!-
+!- OUTPUT
+!-
+!- fid         : File identification
+!- nlonid1     : Identification of longitudinal axis
+!- nlatid1     : Identification of latitudinal axis
+!- zdimid1     : ID of vertical axis
+!- tdimid1     : ID of time axis
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+! ARGUMENTS
+!-
+  CHARACTER(LEN=*) :: filename
+  INTEGER :: iim1, jjm1, llm1, ttm1
+  REAL :: lon1(iim1,jjm1)
+  REAL :: lat1(iim1,jjm1)
+  REAL :: lev1(llm1)
+  INTEGER :: itaus(ttm1)
+  REAL :: time0
+  REAL :: dt
+  INTEGER :: fid_out, zdimid1, nlonid1, nlatid1, tdimid1
+!-
+! LOCAL
+!-
+  INTEGER :: iret, lll, fid
+  INTEGER :: lonid, latid, levid, timeid
+  INTEGER :: year, month, day
+  REAL :: sec
+  CHARACTER(LEN=250):: name
+!-
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+
+  lll = LEN_TRIM(filename)
+  IF (filename(lll-2:lll) /= '.nc') THEN
+    name=filename(1:lll)//'.nc'
+  ELSE
+    name=filename(1:lll)
+  ENDIF
+!-
+  iret = NF90_CREATE (name, NF90_CLOBBER, fid)
+!-
+  iret = NF90_DEF_DIM (fid, 'x',     iim1, nlonid1)
+  iret = NF90_DEF_DIM (fid, 'y',     jjm1, nlatid1)
+  iret = NF90_DEF_DIM (fid, 'lev',   llm1, zdimid1)
+  iret = NF90_DEF_DIM (fid, 'tstep', ttm1, tdimid1)
+!-
+! Vertical axis
+!-
+  IF (l_dbg) WRITE(ipslout,*) 'flincre Vertical axis'
+!-
+  iret = NF90_DEF_VAR (fid, 'lev', NF90_FLOAT, zdimid1, levid)
+  iret = NF90_PUT_ATT (fid, levid, 'units',     '-')
+  iret = NF90_PUT_ATT (fid, levid, 'title',     'levels')
+  iret = NF90_PUT_ATT (fid, levid, 'long_name', 'Sigma Levels')
+!-
+! Time axis
+!-
+  IF (l_dbg) WRITE(ipslout,*) 'flincre time axis'
+!-
+  iret = NF90_DEF_VAR (fid, 'tstep', NF90_FLOAT, tdimid1, timeid)
+  iret = NF90_PUT_ATT (fid, timeid, 'units',     '-')
+  iret = NF90_PUT_ATT (fid, timeid, 'title',     'time')
+  iret = NF90_PUT_ATT (fid, timeid, 'long_name', 'time steps')
+!-
+! The longitude
+!-
+  IF (l_dbg) WRITE(ipslout,*) 'flincre Longitude axis'
+!-
+  iret = NF90_DEF_VAR (fid, "nav_lon", NF90_FLOAT, &
+                       (/ nlonid1, nlatid1 /), lonid)
+  iret = NF90_PUT_ATT (fid, lonid, 'units', "degrees_east")
+  iret = NF90_PUT_ATT (fid, lonid, 'title', "Longitude")
+  iret = NF90_PUT_ATT (fid, lonid, 'nav_model', &
+                       "Lambert projection of PROMES")
+  iret = NF90_PUT_ATT (fid, lonid, 'valid_min', &
+                       REAL(MINVAL(lon1),KIND=4))
+  iret = NF90_PUT_ATT (fid, lonid, 'valid_max', &
+                       REAL(MAXVAL(lon1),KIND=4))
+!-
+! The Latitude
+!-
+  IF (l_dbg) WRITE(ipslout,*) 'flincre Latitude axis'
+!-
+  iret = NF90_DEF_VAR (fid, "nav_lat", NF90_FLOAT, &
+                     (/ nlonid1, nlatid1 /), latid)
+  iret = NF90_PUT_ATT (fid, latid, 'units', "degrees_north")
+  iret = NF90_PUT_ATT (fid, latid, 'title', "Latitude")
+  iret = NF90_PUT_ATT (fid, latid, 'nav_model', &
+                       "Lambert projection of PROMES")
+  iret = NF90_PUT_ATT (fid, latid, 'valid_min', &
+                       REAL(MINVAL(lat1),KIND=4))
+  iret = NF90_PUT_ATT (fid, latid, 'valid_max', &
+                       REAL(MAXVAL(lat1),KIND=4))
+!-
+! The time coordinates
+!-
+  iret = NF90_PUT_ATT (fid, NF90_GLOBAL, 'delta_tstep_sec', &
+                       REAL(dt,KIND=4))
+!-
+  CALL ju2ymds (time0, year, month, day, sec)
+!-
+  iret = NF90_PUT_ATT (fid, NF90_GLOBAL, 'year0',  REAL(year,KIND=4))
+  iret = NF90_PUT_ATT (fid, NF90_GLOBAL, 'month0', REAL(month,KIND=4))
+  iret = NF90_PUT_ATT (fid, NF90_GLOBAL, 'day0',   REAL(day,KIND=4))
+  iret = NF90_PUT_ATT (fid, NF90_GLOBAL, 'sec0',   REAL(sec,KIND=4))
+!-
+  iret = NF90_ENDDEF (fid)
+!-
+  IF (l_dbg) WRITE(ipslout,*) 'flincre Variable'
+!-
+  iret = NF90_PUT_VAR (fid, levid, lev1(1:llm1))
+!-
+  IF (l_dbg) WRITE(ipslout,*) 'flincre Time Variable'
+!-
+  iret = NF90_PUT_VAR (fid, timeid, REAL(itaus(1:ttm1)))
+!-
+  IF (l_dbg) WRITE(ipslout,*) 'flincre Longitude'
+!-
+  iret = NF90_PUT_VAR (fid, lonid, lon1(1:iim1,1:jjm1))
+!-
+  IF (l_dbg) WRITE(ipslout,*) 'flincre Latitude'
+!-
+  iret = NF90_PUT_VAR (fid, latid, lat1(1:iim1,1:jjm1))
+!-
+! Keep all this information
+!-
+  nbfiles = nbfiles+1
+!-
+  IF (nbfiles > nbfile_max) THEN
+    CALL histerr (3,'flincre', &
+     'Too many files. Please increase nbfil_max', &
+     'in program flincom.F90.',' ')
+  ENDIF
+!-
+  ncids(nbfiles) = fid
+  ncnbd(nbfiles) = 4
+!-
+  ncdims(nbfiles,1:4) = (/ iim1, jjm1, llm1, ttm1 /)
+!-
+  ncfunli(nbfiles) = -1
+  ncnba(nbfiles)   =  4
+  ncnbva(nbfiles)  =  0
+  ncfileopen(nbfiles) = .TRUE.
+!-
+  fid_out = nbfiles
+!---------------------
+END SUBROUTINE flincre
+!-
+!===
+!-
+SUBROUTINE flinopen_zoom2d &
+  (filename, iideb, iilen, jjdeb, jjlen, do_test, &
+  iim, jjm, llm, lon, lat, lev, ttm, itaus, date0, dt, fid_out)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+! ARGUMENTS
+!-
+  CHARACTER(LEN=*) :: filename
+  LOGICAL :: do_test
+  INTEGER :: iim, jjm, llm, ttm, iideb, iilen, jjdeb, jjlen
+  REAL :: lon(iilen,jjlen), lat(iilen,jjlen), lev(llm)
+  INTEGER :: itaus(ttm)
+  REAL :: date0, dt
+  INTEGER :: fid_out
+!-
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+
+  IF (l_dbg) WRITE (*,*) ' iideb, iilen, jjdeb, jjlen, iim, jjm ', &
+                           iideb, iilen, jjdeb, jjlen, iim, jjm
+  IF (l_dbg) WRITE (*,*) ' lon ', lon(1,1), lon(iilen,jjlen)
+  IF (l_dbg) WRITE (*,*) ' lat ', lat(1,1), lat(iilen,jjlen)
+!-
+  CALL flinopen_work &
+    (filename, iideb, iilen, jjdeb, jjlen, do_test, &
+     iim, jjm, llm, lon, lat, lev, ttm, itaus, date0, dt, fid_out)
+!-----------------------------
+END SUBROUTINE flinopen_zoom2d
+!-
+!===
+!-
+SUBROUTINE flinopen_nozoom &
+  (filename, do_test, iim, jjm, llm, lon, lat, lev, ttm, &
+   itaus, date0, dt, fid_out)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+! ARGUMENTS
+!-
+  CHARACTER(LEN=*) :: filename
+  LOGICAL :: do_test
+  INTEGER :: iim, jjm, llm, ttm
+  REAL :: lon(iim,jjm), lat(iim,jjm), lev(llm)
+  INTEGER :: itaus(ttm)
+  REAL :: date0, dt
+  INTEGER :: fid_out
+  INTEGER :: iimc, jjmc
+!---------------------------------------------------------------------
+  iimc=iim
+  jjmc=jjm
+  CALL flinopen_work &
+    (filename, 1, iimc, 1, jjmc, do_test, &
+     iim, jjm, llm, lon, lat, lev, ttm, itaus, date0, dt, fid_out)
+!-------------------------
+END SUBROUTINE flinopen_nozoom
+!-
+!===
+!-
+SUBROUTINE flinopen_work &
+  (filename, iideb, iilen, jjdeb, jjlen, do_test, &
+   iim, jjm, llm, lon, lat, lev, ttm, itaus, date0, dt, fid_out)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+! ARGUMENTS
+!-
+  CHARACTER(LEN=*) :: filename
+  LOGICAL :: do_test
+  INTEGER :: iim, jjm, llm, ttm, iideb, iilen, jjdeb, jjlen
+  REAL :: lon(iilen,jjlen), lat(iilen,jjlen), lev(llm)
+  INTEGER :: itaus(ttm)
+  REAL :: date0, dt
+  INTEGER :: fid_out
+!-
+! LOCAL
+!-
+  REAL, PARAMETER :: eps = 1.e-4
+!-
+  INTEGER :: iret, vid, fid, nbdim, i, iilast, jjlast
+  INTEGER :: gdtt_id, old_id, iv, gdtmaf_id
+  CHARACTER(LEN=250) :: name
+  CHARACTER(LEN=80) :: units, calendar
+  INTEGER :: tmp_iim, tmp_jjm, tmp_llm, tmp_ttm
+  REAL :: x_first, x_last
+  INTEGER :: year, month, day
+  REAL :: r_year, r_month, r_day
+  INTEGER :: year0, month0, day0, hours0, minutes0, seci
+  REAL :: sec, sec0
+  CHARACTER :: strc
+!-
+  REAL,DIMENSION(:),ALLOCATABLE :: vec_tmp
+!-
+  LOGICAL :: open_file
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+
+  iilast = iideb+iilen-1
+  jjlast = jjdeb+jjlen-1
+  IF (l_dbg) WRITE (*,*) &
+    ' flinopen_work zoom 2D information '// &
+    ' iideb, iilen, iilast, jjdeb, jjlen, jjlast ', &
+      iideb, iilen, iilast, jjdeb, jjlen, jjlast
+!-
+! 1.0 get all infos on the file
+!-
+! Either the fid_out has not been initialized (0 or very large)
+! then we have to open anyway. Else we only need to open the file
+! if it has not been opened before.
+!-
+  IF ( (fid_out < 1).OR.(fid_out > nbfile_max) ) THEN
+    open_file = .TRUE.
+  ELSE IF (.NOT.ncfileopen(fid_out)) THEN
+    open_file = .TRUE.
+  ELSE
+    open_file = .FALSE.
+  ENDIF
+!-
+  IF (open_file) THEN
+    CALL flininfo (filename,tmp_iim,tmp_jjm,tmp_llm,tmp_ttm,fid_out)
+  ELSE
+!-- The user has already opened the file
+!-- and we trust that he knows the dimensions
+    tmp_iim = iim
+    tmp_jjm = jjm
+    tmp_llm = llm
+    tmp_ttm = ttm
+  ENDIF
+!-
+  IF (l_dbg) &
+    WRITE(ipslout,*) 'OUT OF flininfo :',tmp_iim,tmp_jjm,tmp_llm,tmp_ttm
+!-
+  fid = ncids(fid_out)
+!-
+! 2.0 get the sizes and names of the different coordinates
+!     and do a first set of verification.
+!-
+! 2.2 We test the axis if we have to.
+!-
+  IF (l_dbg) &
+    WRITE(ipslout,*) 'flininfo 2.2 We test if we have to test : ',do_test
+!-
+  IF (do_test) THEN
+    IF      (iim /= tmp_iim) THEN
+      CALL histerr (3,'flinopen', &
+        'file '//filename//' does not have the ', &
+        'required dimension in x direction (longitude)',' ')
+    ELSE IF (jjm /= tmp_jjm) THEN
+      CALL histerr (3,'flinopen', &
+        'file '//filename//' does not have the ', &
+        'required dimension in y direction (latitude)',' ')
+    ELSE IF ( llm /= tmp_llm .AND. llm > 0 ) THEN
+      CALL histerr (3,'flinopen', &
+        'file '//filename//' does not have the ', &
+        'required dimension in the vertical',' ')
+    ENDIF
+  ELSE
+!---
+!-- 2.3 Else the sizes of the axes are returned to the user
+!---
+    IF (l_dbg) WRITE(ipslout,*) 'flinopen 2.3 Else sizes are returned'
+!---
+    iim = tmp_iim
+    jjm = tmp_jjm
+    llm = tmp_llm
+  ENDIF
+!-
+  ttm = tmp_ttm
+!-
+! 3.0 Check if we are realy talking about the same coodinate system
+!     if not then we get the lon, lat and lev variables from the file
+!-
+  IF (l_dbg) WRITE(ipslout,*) 'flinopen 3.0 we are realy talking'
+!-
+  IF (do_test) THEN
+!---
+    CALL flinfindcood (fid_out, 'lon', vid, nbdim)
+    iret = NF90_GET_VAR (fid, vid, x_first, start=(/ iideb, jjdeb /))
+    iret = NF90_GET_VAR (fid, vid, x_last, start=(/ iilast, jjlast /))
+!---
+    IF (l_dbg) &
+      WRITE(ipslout,*) 'from file lon first and last, modulo 360. ', &
+        x_first, x_last, MODULO(x_first,360.), MODULO(x_last,360.)
+    IF (l_dbg) &
+      WRITE(ipslout,*) 'from model lon first and last, modulo 360. ', &
+        lon(1,1),lon(iilen,jjlen), &
+        MODULO(lon(1,1),360.), MODULO(lon(iilen,jjlen),360.)
+    IF (    (ABS( MODULO(x_first,360.) &
+                 -MODULO(lon(1,1),360.)) > eps) &
+        .OR.(ABS( MODULO(x_last,360.) &
+                 -MODULO(lon(iilen ,jjlen),360.)) > eps ) ) THEN
+      CALL histerr (3,'flinopen', &
+        'file '//filename//' and the model do not', &
+        'share the same longitude coordinate', &
+        'Obtained by comparing the first and last values ')
+    ENDIF
+!---
+    CALL flinfindcood (fid_out, 'lat', vid, nbdim)
+    iret = NF90_GET_VAR (fid, vid, x_first, start=(/ iideb, jjdeb /))
+    iret = NF90_GET_VAR (fid, vid, x_last, start=(/ iilast, jjlast /))
+!---
+    IF (l_dbg) WRITE(ipslout,*) &
+      'from file lat first and last ',x_first,x_last
+    IF (l_dbg) WRITE(ipslout,*) &
+      'from model lat first and last ',lat(1,1),lat(iilen,jjlen)
+!---
+    IF (    (ABS(x_first-lat(1,1)) > eps) &
+        .OR.(ABS(x_last-lat(iilen,jjlen)) > eps) ) THEN
+      CALL histerr (3,'flinopen', &
+        'file '//filename//' and the model do not', &
+        'share the same latitude coordinate', &
+        'Obtained by comparing the first and last values ')
+    ENDIF
+!---
+    IF (llm > 0) THEN
+      CALL flinfindcood (fid_out, 'lev', vid, nbdim)
+      iret = NF90_GET_VAR (fid, vid, x_first, start=(/ 1 /))
+      iret = NF90_GET_VAR (fid, vid, x_last, start=(/ llm /))
+!-----
+      IF (l_dbg) WRITE(ipslout,*) &
+        'from file lev first and last ',x_first ,x_last
+      IF (l_dbg) WRITE(ipslout,*) &
+        'from model lev first and last ',lev(1),lev(llm)
+!-----
+      IF (    (ABS(x_first-lev(1)) > eps) &
+          .OR.(ABS(x_last-lev(llm)) > eps) ) THEN
+        CALL histerr (3,'flinopen', &
+          'file '//filename//' and the model do not', &
+          'share the same vertical coordinate', &
+          'Obtained by comparing the first and last values')
+      ENDIF
+    ENDIF
+!---
+  ELSE
+!---
+!-- 4.0 extracting the coordinates if we do not check
+!---
+    IF (l_dbg) WRITE(ipslout,*) 'flinopen 4.0 extracting the coordinates'
+!---
+    CALL flinfindcood (fid_out, 'lon', vid, nbdim)
+    IF (nbdim == 2) THEN
+      iret = NF90_GET_VAR (fid, vid, lon, &
+               start=(/ iideb, jjdeb /), count=(/ iilen, jjlen /))
+    ELSE
+      ALLOCATE(vec_tmp(iilen))
+      iret = NF90_GET_VAR (fid, vid, vec_tmp, &
+               start=(/ iideb /), count=(/ iilen /))
+      DO i=1,jjlen
+        lon(:,i) = vec_tmp(:)
+      ENDDO
+      DEALLOCATE(vec_tmp)
+    ENDIF
+!---
+    CALL flinfindcood (fid_out, 'lat', vid, nbdim)
+    IF (nbdim == 2) THEN
+      iret = NF90_GET_VAR (fid, vid, lat, &
+               start=(/ iideb, jjdeb /), count=(/ iilen, jjlen /))
+    ELSE
+      ALLOCATE(vec_tmp(jjlen))
+      iret = NF90_GET_VAR (fid, vid, vec_tmp, &
+               start=(/ jjdeb /), count=(/ jjlen /))
+      DO i=1,iilen
+        lat(i,:) = vec_tmp(:)
+      ENDDO
+      DEALLOCATE(vec_tmp)
+    ENDIF
+!---
+    IF (llm > 0) THEN
+      CALL flinfindcood (fid_out, 'lev', vid, nbdim)
+      IF (nbdim == 1) THEN
+        iret = NF90_GET_VAR (fid, vid, lev, &
+                 start=(/ 1 /), count=(/ llm /))
+      ELSE
+        CALL histerr (3,'flinopen', &
+          'Can not handle vertical coordinates that have more',&
+          'than 1 dimension',' ')
+      ENDIF
+    ENDIF
+  ENDIF
+!-
+! 5.0 Get all the details for the time if possible needed
+!-
+  IF (l_dbg) WRITE(ipslout,*) 'flinopen 5.0 Get time'
+!-
+  IF (ttm > 0) THEN
+!---
+!-- 5.1 Find the time axis. Prefered method is the 'timestep since'
+!---
+    gdtmaf_id = -1
+    gdtt_id = -1
+    old_id = -1
+    DO iv=1,ncnbva(fid_out)
+      name=''
+      iret = NF90_INQUIRE_VARIABLE (fid, iv, name=name)
+      units=''
+      iret = NF90_GET_ATT (fid, iv, 'units', units)
+      IF (INDEX(units,'seconds since') > 0) gdtmaf_id = iv
+      IF (INDEX(units,'timesteps since') > 0) gdtt_id = iv
+      IF (INDEX(name, 'tstep') > 0 .OR. INDEX(name,'time') > 0 ) old_id = iv
+    ENDDO
+!---
+    IF (gdtt_id > 0) THEN
+      vid = gdtt_id
+    ELSE IF (gdtmaf_id > 0) THEN
+      vid = gdtmaf_id
+    ELSE IF (old_id > 0) THEN
+      vid = old_id
+    ELSE
+      CALL histerr (3, 'flinopen', 'No time axis found',' ',' ')
+    ENDIF
+!---
+    ALLOCATE(vec_tmp(ttm))
+    iret = NF90_GET_VAR (fid,vid,vec_tmp,start=(/ 1 /),count=(/ ttm /))
+    itaus(1:ttm) = NINT(vec_tmp(1:ttm))
+    DEALLOCATE(vec_tmp)
+!---
+    IF (l_dbg) WRITE(ipslout,*) 'flinopen 5.1 Times ',itaus
+!---
+!-- Getting all the details for the time axis
+!---
+!-- Find the calendar
+    calendar = ''
+    iret = NF90_GET_ATT (fid,gdtmaf_id,'calendar',calendar)
+    IF (iret == NF90_NOERR) THEN
+      CALL ioconf_calendar(calendar)
+    ENDIF
+!--
+    units = ''
+    iret = NF90_GET_ATT (fid,vid,'units',units)
+    IF (gdtt_id > 0) THEN
+      units = units(INDEX(units,'since')+6:LEN_TRIM(units))
+      READ (units,'(I4.4,5(a,I2.2))') &
+        year0, strc, month0, strc, day0, &
+               strc, hours0, strc, minutes0, strc, seci
+      sec0 = hours0*3600. + minutes0*60. + seci
+      CALL ymds2ju (year0, month0, day0, sec0, date0)
+      IF (l_dbg) &
+        WRITE(ipslout,*) 'flinopen 5.1 gdtt_id year0 ... date0 ', &
+                   year0, month0, day0, sec0, date0
+!-----
+      iret = NF90_GET_ATT (fid, gdtt_id, 'tstep_sec', dt)
+    ELSE IF (gdtmaf_id > 0) THEN
+      units = units(INDEX(units,'since')+6:LEN_TRIM(units))
+      READ (units,'(I4.4,5(a,I2.2))') &
+        year0, strc, month0, strc, day0, &
+               strc, hours0, strc, minutes0, strc, seci
+      sec0 = hours0*3600. + minutes0*60. + seci
+      CALL ymds2ju (year0, month0, day0, sec0, date0)
+!-----
+      IF (l_dbg) &
+        WRITE(ipslout,*) 'flinopen 5.1 gdtmaf_id year0 ... date0 ', &
+                   year0, month0, day0, sec0, date0
+    ELSE IF (old_id > 0) THEN
+      iret = NF90_GET_ATT (fid, NF90_GLOBAL, 'delta_tstep_sec', dt)
+      iret = NF90_GET_ATT (fid, NF90_GLOBAL, 'day0', r_day)
+      iret = NF90_GET_ATT (fid, NF90_GLOBAL, 'sec0', sec)
+      iret = NF90_GET_ATT (fid, NF90_GLOBAL, 'year0', r_year)
+      iret = NF90_GET_ATT (fid, NF90_GLOBAL, 'month0', r_month)
+!-----
+      day = INT(r_day)
+      month = INT(r_month)
+      year = INT(r_year)
+!-----
+      CALL ymds2ju (year, month, day, sec, date0)
+    ENDIF
+  ENDIF
+!-
+  IF (l_dbg) WRITE(ipslout,*) 'flinopen 6.0 File opened', date0, dt
+!---------------------------
+END SUBROUTINE flinopen_work
+!-
+!===
+!-
+SUBROUTINE flininfo (filename, iim, jjm, llm, ttm, fid_out)
+!---------------------------------------------------------------------
+!- This subroutine allows to get some information.
+!- It is usualy done within flinopen but the user may want to call
+!- it before in order to allocate the space needed to extract the
+!- data from the file.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+! ARGUMENTS
+!-
+  CHARACTER(LEN=*) :: filename
+  INTEGER :: iim, jjm, llm, ttm, fid_out
+!-
+! LOCAL
+!-
+  INTEGER :: iret, fid, ndims, nvars, nb_atts, id_unlim
+  INTEGER :: iv, lll
+  INTEGER :: xid, yid, zid, tid
+  CHARACTER(LEN=:), ALLOCATABLE :: name
+  CHARACTER(LEN=30) :: axname
+!-
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+
+  lll = LEN_TRIM(filename)
+  IF (filename(lll-2:lll) /= '.nc') THEN
+    name = filename(1:lll)//'.nc'
+  ELSE
+    name = filename(1:lll)
+  ENDIF
+!-
+  iret = NF90_OPEN (name, NF90_NOWRITE, fid)
+  IF (iret /= NF90_NOERR) THEN
+    CALL histerr(3, 'flininfo','Could not open file :',TRIM(name),' ')
+  ENDIF
+!-
+  iret = NF90_INQUIRE (fid, nDimensions=ndims, nVariables=nvars, &
+                      nAttributes=nb_atts, unlimitedDimId=id_unlim)
+!-
+  xid = -1; iim = 0;
+  yid = -1; jjm = 0;
+  zid = -1; llm = 0;
+  tid = -1; ttm = 0;
+!-
+  DO iv=1,ndims
+!---
+    iret = NF90_INQUIRE_DIMENSION (fid, iv, name=axname, len=lll)
+    CALL strlowercase (axname)
+    axname = ADJUSTL(axname)
+!---
+    IF (l_dbg) WRITE(ipslout,*) &
+      'flininfo - getting axname',iv,axname,lll
+!---
+    IF      (    (INDEX(axname,'x') == 1) &
+             .OR.(INDEX(axname,'lon') == 1) ) THEN
+      xid = iv; iim = lll;
+    ELSE IF (    (INDEX(axname,'y') == 1) &
+             .OR.(INDEX(axname,'lat') == 1) ) THEN
+      yid = iv; jjm = lll;
+    ELSE IF (    (INDEX(axname,'lev') == 1) &
+             .OR.(INDEX(axname,'plev') == 1) &
+             .OR.(INDEX(axname,'z') == 1) &
+             .OR.(INDEX(axname,'depth') == 1) ) THEN
+      zid = iv; llm = lll;
+    ELSE IF (    (INDEX(axname,'tstep') == 1) &
+             .OR.(INDEX(axname,'time') == 1) &
+             .OR.(INDEX(axname,'time_counter') == 1) ) THEN
+!---- For the time we certainly need to allow for other names
+      tid = iv; ttm = lll;
+    ELSE IF (ndims == 1) THEN
+!---- Nothing was found and ndims=1 then we have a vector of data
+      xid = 1; iim = lll;
+    ENDIF
+!---
+  ENDDO
+!-
+! Keep all this information
+!-
+  nbfiles = nbfiles+1
+!-
+  IF (nbfiles > nbfile_max) THEN
+    CALL histerr (3,'flininfo', &
+      'Too many files. Please increase nbfil_max', &
+      'in program flincom.F90.',' ')
+  ENDIF
+!-
+  ncids(nbfiles) = fid
+  ncnbd(nbfiles) = ndims
+!-
+  ncdims(nbfiles,1:4) = (/ iim, jjm, llm, ttm /)
+!-
+  ncfunli(nbfiles) = id_unlim
+  ncnba(nbfiles)   = nb_atts
+  ncnbva(nbfiles)  = nvars
+  ncfileopen(nbfiles) = .TRUE.
+!-
+  fid_out = nbfiles
+!----------------------
+END SUBROUTINE flininfo
+!-
+!===
+!-
+SUBROUTINE flinput_r1d &
+  (fid_in,varname,iim,nlonid,jjm,nlatid,llm,zdimid,ttm,tdimid,var)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid_in
+  CHARACTER(LEN=*) :: varname
+  INTEGER :: iim, nlonid, jjm, nlatid, llm, zdimid, ttm, tdimid
+  REAL :: var(:)
+!-
+  INTEGER :: fid, ncvarid, ndim, iret
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+
+  IF (l_dbg) WRITE(ipslout,*) &
+     "flinput_r1d : SIZE(var) = ",SIZE(var)
+!-
+  CALL flinput_mat &
+    (fid_in,varname,iim,nlonid,jjm,nlatid,llm,zdimid,ttm,tdimid, &
+     fid,ncvarid,ndim)
+!-
+  iret = NF90_PUT_VAR (fid, ncvarid, var, &
+           start=w_sta(1:ndim), count=w_len(1:ndim))
+!-------------------------
+END SUBROUTINE flinput_r1d
+!-
+!===
+!-
+SUBROUTINE flinput_r2d &
+  (fid_in,varname,iim,nlonid,jjm,nlatid,llm,zdimid,ttm,tdimid,var)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid_in
+  CHARACTER(LEN=*) :: varname
+  INTEGER :: iim, nlonid, jjm, nlatid, llm, zdimid, ttm, tdimid
+  REAL :: var(:,:)
+!-
+  INTEGER :: fid, ncvarid, ndim, iret
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+
+  IF (l_dbg) WRITE(ipslout,*) &
+     "flinput_r2d : SIZE(var) = ",SIZE(var)
+!-
+  CALL flinput_mat &
+    (fid_in,varname,iim,nlonid,jjm,nlatid,llm,zdimid,ttm,tdimid, &
+     fid,ncvarid,ndim)
+!-
+  iret = NF90_PUT_VAR (fid, ncvarid, var, &
+           start=w_sta(1:ndim), count=w_len(1:ndim))
+!-------------------------
+END SUBROUTINE flinput_r2d
+!-
+!===
+!-
+SUBROUTINE flinput_r3d &
+  (fid_in,varname,iim,nlonid,jjm,nlatid,llm,zdimid,ttm,tdimid,var)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid_in
+  CHARACTER(LEN=*) :: varname
+  INTEGER :: iim, nlonid, jjm, nlatid, llm, zdimid, ttm, tdimid
+  REAL :: var(:,:,:)
+!-
+  INTEGER :: fid, ncvarid, ndim, iret
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+
+  IF (l_dbg) WRITE(ipslout,*) &
+     "flinput_r3d : SIZE(var) = ",SIZE(var)
+!-
+  CALL flinput_mat &
+    (fid_in,varname,iim,nlonid,jjm,nlatid,llm,zdimid,ttm,tdimid, &
+     fid,ncvarid,ndim)
+!-
+  iret = NF90_PUT_VAR (fid, ncvarid, var, &
+           start=w_sta(1:ndim), count=w_len(1:ndim))
+!-------------------------
+END SUBROUTINE flinput_r3d
+!-
+!===
+!-
+SUBROUTINE flinput_r4d &
+  (fid_in,varname,iim,nlonid,jjm,nlatid,llm,zdimid,ttm,tdimid,var)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid_in
+  CHARACTER(LEN=*) :: varname
+  INTEGER :: iim, nlonid, jjm, nlatid, llm, zdimid, ttm, tdimid
+  REAL :: var(:,:,:,:)
+!-
+  INTEGER :: fid, ncvarid, ndim, iret
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+
+  IF (l_dbg) WRITE(ipslout,*) &
+     "flinput_r4d : SIZE(var) = ",SIZE(var)
+!-
+  CALL flinput_mat &
+    (fid_in,varname,iim,nlonid,jjm,nlatid,llm,zdimid,ttm,tdimid, &
+     fid,ncvarid,ndim)
+!-
+  iret = NF90_PUT_VAR (fid, ncvarid, var, &
+           start=w_sta(1:ndim), count=w_len(1:ndim))
+!-------------------------
+END SUBROUTINE flinput_r4d
+!-
+!===
+!-
+SUBROUTINE flinput_mat &
+  (fid_in,varname,iim,nlonid,jjm,nlatid, &
+                  llm,zdimid,ttm,tdimid,fid,ncvarid,ndim)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid_in
+  CHARACTER(LEN=*) :: varname
+  INTEGER :: iim, nlonid, jjm, nlatid, llm, zdimid, ttm, tdimid
+  INTEGER :: fid, ncvarid, ndim
+!-
+! LOCAL
+!-
+  INTEGER :: iret
+!---------------------------------------------------------------------
+  fid = ncids(fid_in)
+!-
+  w_sta(1:4) = (/      1,      1,  1,  1 /)
+  w_len(1:2) = (/    iim,    jjm /)
+  w_dim(1:2) = (/ nlonid, nlatid /)
+!-
+  IF ( (llm > 0).AND.(ttm > 0) ) THEN
+    ndim = 4
+    w_len(3:4) = (/    llm,    ttm /)
+    w_dim(3:4) = (/ zdimid, tdimid /)
+  ELSE IF (llm > 0) THEN
+    ndim = 3
+    w_dim(3) = zdimid
+    w_len(3) = llm
+  ELSE IF (ttm > 0) THEN
+    ndim = 3
+    w_dim(3) = tdimid
+    w_len(3) = ttm
+  ELSE
+    ndim = 2
+  ENDIF
+!-
+  iret = NF90_REDEF   (fid)
+  iret = NF90_DEF_VAR (fid,varname,NF90_FLOAT,w_dim(1:ndim),ncvarid)
+  iret = NF90_PUT_ATT (fid,ncvarid,'short_name',TRIM(varname))
+  iret = NF90_ENDDEF  (fid)
+!--------------------------
+END  SUBROUTINE flinput_mat
+!-
+!===
+!-
+SUBROUTINE flinput_scal &
+  (fid_in, varname, iim, nlonid, jjm, nlatid, &
+                    llm, zdimid, ttm, tdimid, var)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid_in
+  CHARACTER(LEN=*) :: varname
+  INTEGER :: iim, nlonid, jjm, nlatid, llm, zdimid, ttm, tdimid
+  REAL :: var
+!-
+! LOCAL
+!-
+  INTEGER :: fid, iret
+!---------------------------------------------------------------------
+  fid = ncids(fid_in)
+!-
+  iret = NF90_REDEF   (fid)
+  iret = NF90_PUT_ATT (fid, NF90_GLOBAL, varname, REAL(var,KIND=4))
+  iret = NF90_ENDDEF  (fid)
+!---------------------------
+END  SUBROUTINE flinput_scal
+!-
+!===
+!-
+SUBROUTINE flinget_r1d &
+  (fid_in,varname,iim,jjm,llm,ttm,itau_dep,itau_fin,var)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid_in
+  CHARACTER(LEN=*) :: varname
+  INTEGER :: iim, jjm, llm, ttm, itau_dep, itau_fin
+  REAL :: var(:)
+!-
+  INTEGER :: jl, ji
+  REAL,DIMENSION(:),ALLOCATABLE,SAVE :: buff_tmp
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+
+  IF (.NOT.ALLOCATED(buff_tmp)) THEN
+    IF (l_dbg) WRITE(ipslout,*) &
+      "flinget_r1d : allocate buff_tmp for buff_sz = ",SIZE(var)
+    ALLOCATE (buff_tmp(SIZE(var)))
+  ELSE IF (SIZE(var) > SIZE(buff_tmp)) THEN
+    IF (l_dbg) WRITE(ipslout,*) &
+      "flinget_r1d : re-allocate buff_tmp for buff_sz = ",SIZE(var)
+    DEALLOCATE (buff_tmp)
+    ALLOCATE (buff_tmp(SIZE(var)))
+  ENDIF
+!-
+  CALL flinget_mat (fid_in,varname,iim,jjm,llm,ttm, &
+                    itau_dep,itau_fin,1,iim,1,jjm,buff_tmp)
+!-
+  jl=0
+  DO ji=1,SIZE(var,1)
+    jl=jl+1
+    var(ji) = buff_tmp(jl)
+  ENDDO
+!-------------------------
+  IF (ALLOCATED(buff_tmp)) DEALLOCATE(buff_tmp)
+END SUBROUTINE flinget_r1d
+!-
+!===
+!-
+SUBROUTINE flinget_r2d &
+  (fid_in,varname,iim,jjm,llm,ttm,itau_dep,itau_fin,var)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid_in
+  CHARACTER(LEN=*) :: varname
+  INTEGER :: iim, jjm, llm, ttm, itau_dep, itau_fin
+  REAL :: var(:,:)
+!-
+  INTEGER :: jl, jj, ji
+  REAL,DIMENSION(:),ALLOCATABLE,SAVE :: buff_tmp
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+
+  IF (.NOT.ALLOCATED(buff_tmp)) THEN
+    IF (l_dbg) WRITE(ipslout,*) &
+      "flinget_r2d : allocate buff_tmp for buff_sz = ",SIZE(var)
+    ALLOCATE (buff_tmp(SIZE(var)))
+  ELSE IF (SIZE(var) > SIZE(buff_tmp)) THEN
+    IF (l_dbg) WRITE(ipslout,*) &
+      "flinget_r2d : re-allocate buff_tmp for buff_sz = ",SIZE(var)
+    DEALLOCATE (buff_tmp)
+    ALLOCATE (buff_tmp(SIZE(var)))
+  ENDIF
+!-
+  CALL flinget_mat (fid_in,varname,iim,jjm,llm,ttm, &
+                    itau_dep,itau_fin,1,iim,1,jjm,buff_tmp)
+!-
+  jl=0
+  DO jj=1,SIZE(var,2)
+    DO ji=1,SIZE(var,1)
+      jl=jl+1
+      var(ji,jj) = buff_tmp(jl)
+    ENDDO
+  ENDDO
+!-------------------------
+  IF (ALLOCATED(buff_tmp)) DEALLOCATE(buff_tmp)
+END SUBROUTINE flinget_r2d
+!-
+!===
+!-
+SUBROUTINE flinget_r2d_zoom2d &
+  (fid_in,varname,iim,jjm,llm,ttm, &
+   itau_dep,itau_fin,iideb,iilen,jjdeb,jjlen,var)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid_in
+  CHARACTER(LEN=*) :: varname
+  INTEGER :: iim,jjm,llm,ttm,itau_dep,itau_fin,iideb,jjdeb,iilen,jjlen
+  REAL :: var(:,:)
+!-
+  INTEGER :: jl, jj, ji
+  REAL,DIMENSION(:),ALLOCATABLE,SAVE :: buff_tmp
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+
+  IF (.NOT.ALLOCATED(buff_tmp)) THEN
+    IF (l_dbg) WRITE(ipslout,*) &
+      "flinget_r2d_zoom : allocate buff_tmp for buff_sz = ",SIZE(var)
+    ALLOCATE (buff_tmp(SIZE(var)))
+  ELSE IF (SIZE(var) > SIZE(buff_tmp)) THEN
+    IF (l_dbg) WRITE(ipslout,*) &
+      "flinget_r2d_zoom : re-allocate buff_tmp for buff_sz = ",SIZE(var)
+    DEALLOCATE (buff_tmp)
+    ALLOCATE (buff_tmp(SIZE(var)))
+  ENDIF
+!-
+  CALL flinget_mat (fid_in,varname,iim,jjm,llm,ttm, &
+                    itau_dep,itau_fin,iideb,iilen,jjdeb,jjlen,buff_tmp)
+!-
+  jl=0
+  DO jj=1,SIZE(var,2)
+    DO ji=1,SIZE(var,1)
+      jl=jl+1
+      var(ji,jj) = buff_tmp(jl)
+    ENDDO
+  ENDDO
+  IF (ALLOCATED(buff_tmp)) DEALLOCATE(buff_tmp)
+!--------------------------------
+END SUBROUTINE flinget_r2d_zoom2d
+!-
+!===
+!-
+SUBROUTINE flinget_r3d &
+  (fid_in,varname,iim,jjm,llm,ttm,itau_dep,itau_fin,var)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid_in
+  CHARACTER(LEN=*) :: varname
+  INTEGER :: iim, jjm, llm, ttm, itau_dep, itau_fin
+  REAL :: var(:,:,:)
+!-
+  INTEGER :: jl, jk, jj, ji
+  REAL,DIMENSION(:),ALLOCATABLE,SAVE :: buff_tmp
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+
+  IF (.NOT.ALLOCATED(buff_tmp)) THEN
+    IF (l_dbg) WRITE(ipslout,*) &
+      "flinget_r3d : allocate buff_tmp for buff_sz = ",SIZE(var)
+    ALLOCATE (buff_tmp(SIZE(var)))
+  ELSE IF (SIZE(var) > SIZE(buff_tmp)) THEN
+    IF (l_dbg) WRITE(ipslout,*) &
+      "flinget_r3d : re-allocate buff_tmp for buff_sz = ",SIZE(var)
+    DEALLOCATE (buff_tmp)
+    ALLOCATE (buff_tmp(SIZE(var)))
+  ENDIF
+!-
+  CALL flinget_mat (fid_in,varname,iim,jjm,llm,ttm, &
+                    itau_dep,itau_fin,1,iim,1,jjm,buff_tmp)
+!-
+  jl=0
+  DO jk=1,SIZE(var,3)
+    DO jj=1,SIZE(var,2)
+      DO ji=1,SIZE(var,1)
+        jl=jl+1
+        var(ji,jj,jk) = buff_tmp(jl)
+      ENDDO
+    ENDDO
+  ENDDO
+!-------------------------
+  IF (ALLOCATED(buff_tmp)) DEALLOCATE(buff_tmp)
+END SUBROUTINE flinget_r3d
+!-
+!===
+!-
+SUBROUTINE flinget_r3d_zoom2d &
+  (fid_in,varname,iim,jjm,llm,ttm, &
+   itau_dep,itau_fin,iideb,iilen,jjdeb,jjlen,var)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid_in
+  CHARACTER(LEN=*) :: varname
+  INTEGER :: iim,jjm,llm,ttm,itau_dep,itau_fin,iideb,jjdeb,iilen,jjlen
+  REAL :: var(:,:,:)
+!-
+  INTEGER :: jl, jk, jj, ji
+  REAL,DIMENSION(:),ALLOCATABLE,SAVE :: buff_tmp
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+
+  IF (.NOT.ALLOCATED(buff_tmp)) THEN
+    IF (l_dbg) WRITE(ipslout,*) &
+      "flinget_r3d_zoom : allocate buff_tmp for buff_sz = ",SIZE(var)
+    ALLOCATE (buff_tmp(SIZE(var)))
+  ELSE IF (SIZE(var) > SIZE(buff_tmp)) THEN
+    IF (l_dbg) WRITE(ipslout,*) &
+      "flinget_r3d_zoom : re-allocate buff_tmp for buff_sz = ",SIZE(var)
+    DEALLOCATE (buff_tmp)
+    ALLOCATE (buff_tmp(SIZE(var)))
+  ENDIF
+!-
+  CALL flinget_mat (fid_in,varname,iim,jjm,llm,ttm, &
+                    itau_dep,itau_fin,iideb,iilen,jjdeb,jjlen,buff_tmp)
+!-
+  jl=0
+  DO jk=1,SIZE(var,3)
+    DO jj=1,SIZE(var,2)
+      DO ji=1,SIZE(var,1)
+        jl=jl+1
+        var(ji,jj,jk) = buff_tmp(jl)
+      ENDDO
+    ENDDO
+  ENDDO
+!--------------------------------
+  IF (ALLOCATED(buff_tmp)) DEALLOCATE(buff_tmp)
+END SUBROUTINE flinget_r3d_zoom2d
+!-
+!===
+!-
+SUBROUTINE flinget_r4d &
+  (fid_in,varname,iim,jjm,llm,ttm,itau_dep,itau_fin,var)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid_in
+  CHARACTER(LEN=*) :: varname
+  INTEGER :: iim, jjm, llm, ttm, itau_dep, itau_fin
+  REAL :: var(:,:,:,:)
+!-
+  INTEGER :: jl, jk, jj, ji, jm
+  REAL,DIMENSION(:),ALLOCATABLE,SAVE :: buff_tmp
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+
+  IF (.NOT.ALLOCATED(buff_tmp)) THEN
+    IF (l_dbg) WRITE(ipslout,*) &
+      "flinget_r4d : allocate buff_tmp for buff_sz = ",SIZE(var)
+    ALLOCATE (buff_tmp(SIZE(var)))
+  ELSE IF (SIZE(var) > SIZE(buff_tmp)) THEN
+    IF (l_dbg) WRITE(ipslout,*) &
+      "flinget_r4d : re-allocate buff_tmp for buff_sz = ",SIZE(var)
+    DEALLOCATE (buff_tmp)
+    ALLOCATE (buff_tmp(SIZE(var)))
+  ENDIF
+!-
+  CALL flinget_mat (fid_in,varname,iim,jjm,llm,ttm, &
+                    itau_dep,itau_fin,1,iim,1,jjm,buff_tmp)
+!-
+  jl=0
+  DO jm=1,SIZE(var,4)
+    DO jk=1,SIZE(var,3)
+      DO jj=1,SIZE(var,2)
+        DO ji=1,SIZE(var,1)
+          jl=jl+1
+          var(ji,jj,jk,jm) = buff_tmp(jl)
+        ENDDO
+      ENDDO
+    ENDDO
+  ENDDO
+!-------------------------
+  IF (ALLOCATED(buff_tmp)) DEALLOCATE(buff_tmp)
+END SUBROUTINE flinget_r4d
+!-
+!===
+!-
+SUBROUTINE flinget_r4d_zoom2d &
+  (fid_in,varname,iim,jjm,llm,ttm, &
+   itau_dep,itau_fin,iideb,iilen,jjdeb,jjlen,var)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid_in
+  CHARACTER(LEN=*) :: varname
+  INTEGER :: iim,jjm,llm,ttm,itau_dep,itau_fin,iideb,jjdeb,iilen,jjlen
+  REAL :: var(:,:,:,:)
+!-
+  INTEGER :: jl, jk, jj, ji, jm
+  REAL,DIMENSION(:),ALLOCATABLE,SAVE :: buff_tmp
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+
+  IF (.NOT.ALLOCATED(buff_tmp)) THEN
+    IF (l_dbg) WRITE(ipslout,*) &
+      "flinget_r4d_zoom : allocate buff_tmp for buff_sz = ",SIZE(var)
+    ALLOCATE (buff_tmp(SIZE(var)))
+  ELSE IF (SIZE(var) > SIZE(buff_tmp)) THEN
+    IF (l_dbg) WRITE(ipslout,*) &
+      "flinget_r4d_zoom : re-allocate buff_tmp for buff_sz = ",SIZE(var)
+    DEALLOCATE (buff_tmp)
+    ALLOCATE (buff_tmp(SIZE(var)))
+  ENDIF
+!-
+  CALL flinget_mat (fid_in,varname,iim,jjm,llm,ttm, &
+                    itau_dep,itau_fin,iideb,iilen,jjdeb,jjlen,buff_tmp)
+!-
+  jl=0
+  DO jm=1,SIZE(var,4)
+    DO jk=1,SIZE(var,3)
+      DO jj=1,SIZE(var,2)
+        DO ji=1,SIZE(var,1)
+          jl=jl+1
+          var(ji,jj,jk,jm) = buff_tmp(jl)
+        ENDDO
+      ENDDO
+    ENDDO
+  ENDDO
+!--------------------------------
+  IF (ALLOCATED(buff_tmp)) DEALLOCATE(buff_tmp)
+END SUBROUTINE flinget_r4d_zoom2d
+!-
+!===
+!-
+SUBROUTINE flinget_mat &
+  (fid_in, varname, iim, jjm, llm, ttm, itau_dep, &
+   itau_fin, iideb, iilen, jjdeb, jjlen, var)
+!---------------------------------------------------------------------
+!- This subroutine will read the variable named varname from
+!- the file previously opened by flinopen and identified by fid
+!-
+!- It is checked that the dimensions of the variable to be read
+!- correspond to what the user requested when he specified
+!- iim, jjm and llm. The only exception which is allowed is
+!- for compressed data where the horizontal grid is not expected
+!- to be iim x jjm.
+!-
+!- If variable is of size zero a global attribute is read.
+!- This global attribute will be of type real
+!-
+!- INPUT
+!-
+!- fid      : File ID returned by flinopen
+!- varname  : Name of the variable to be read from the file
+!- iim      : | These three variables give the size of the variables
+!- jjm      : | to be read. It will be verified that the variables
+!- llm      : | fits in there.
+!- ttm      : |
+!- itau_dep : Time step at which we will start to read
+!- itau_fin : Time step until which we are going to read
+!-            For the moment this is done on indexes
+!-            but it should be in the physical space.
+!-            If there is no time-axis in the file then use a
+!-            itau_fin < itau_dep, this will tell flinget not to
+!-            expect a time-axis in the file.
+!- iideb    : index i for zoom
+!- iilen    : length of zoom
+!- jjdeb    : index j for zoom
+!- jjlen    : length of zoom
+!-
+!- OUTPUT
+!-
+!- var      : array that will contain the data
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+! ARGUMENTS
+!-
+  INTEGER, INTENT(IN) :: fid_in
+  CHARACTER(LEN=*), INTENT(IN) :: varname
+  INTEGER, INTENT(IN) :: iim, jjm, llm, ttm
+  INTEGER, INTENT(IN) :: itau_dep, itau_fin, iideb, iilen, jjdeb, jjlen
+  REAL, INTENT(OUT) :: var(:)
+!-
+! LOCAL
+!-
+  INTEGER :: iret, fid
+  INTEGER :: vid, cvid, clen
+  CHARACTER(LEN=70) :: str1
+  CHARACTER(LEN=250) :: att_n, tmp_n
+  CHARACTER(LEN=5) :: axs_l
+  INTEGER :: tmp_i
+  REAL,SAVE :: mis_v=0.
+  REAL :: tmp_r
+  INTEGER :: ndims, x_typ, nb_atts
+  INTEGER,DIMENSION(NF90_MAX_VAR_DIMS) :: dimids
+  INTEGER :: i, nvars, i2d, cnd
+  REAL,DIMENSION(:),ALLOCATABLE,SAVE :: var_tmp
+  INTEGER :: itau_len
+  LOGICAL :: uncompress = .FALSE.
+  INTEGER :: il, ip, i2p, it
+  !-
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+  !-
+  fid = ncids(fid_in)
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) &
+    'flinget_mat : fid_in, fid, varname :', fid_in, fid, TRIM(varname)
+    WRITE(ipslout,*) &
+    'flinget_mat : iim, jjm, llm, ttm, itau_dep, itau_fin :', &
+    iim, jjm, llm, ttm, itau_dep, itau_fin
+    WRITE(ipslout,*) &
+    'flinget_mat : iideb, iilen, jjdeb, jjlen :', &
+    iideb, iilen, jjdeb, jjlen
+  ENDIF
+!-
+  uncompress = .FALSE.
+!-
+! 1.0 We get first all the details on this variable from the file
+!-
+  nvars = ncnbva(fid_in)
+!-
+  vid = -1
+  iret = NF90_INQ_VARID (fid, varname, vid)
+!-
+  IF (vid < 0 .OR. iret /= NF90_NOERR) THEN
+    CALL histerr (3,'flinget', &
+      'Variable '//TRIM(varname)//' not found in file',' ',' ')
+  ENDIF
+!-
+  iret = NF90_INQUIRE_VARIABLE (fid, vid, &
+           ndims=ndims, dimids=dimids, nAtts=nb_atts)
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) &
+    'flinget_mat : fid, vid :', fid, vid
+    WRITE(ipslout,*) &
+    'flinget_mat : ndims, dimids(1:ndims), nb_atts :', &
+    ndims, dimids(1:ndims), nb_atts
+  ENDIF
+!-
+  w_dim(:) = 0
+  DO i=1,ndims
+    iret  = NF90_INQUIRE_DIMENSION (fid, dimids(i), len=w_dim(i))
+  ENDDO
+  IF (l_dbg) WRITE(ipslout,*) &
+    'flinget_mat : w_dim :', w_dim(1:ndims)
+!-
+  mis_v = 0.0; axs_l = ' ';
+!-
+  IF (nb_atts > 0) THEN
+     IF (l_dbg) THEN
+      WRITE(ipslout,*) 'flinget_mat : attributes for variable :'
+    ENDIF
+  ENDIF
+  DO i=1,nb_atts
+    iret = NF90_INQ_ATTNAME (fid, vid, i, att_n)
+    iret = NF90_INQUIRE_ATTRIBUTE (fid, vid, att_n, xtype=x_typ)
+    CALL strlowercase (att_n)
+    IF      (    (x_typ == NF90_INT).OR.(x_typ == NF90_SHORT) &
+             .OR.(x_typ == NF90_BYTE) ) THEN
+      iret = NF90_GET_ATT (fid, vid, att_n, tmp_i)
+        IF (l_dbg) THEN
+        WRITE(ipslout,*) '   ',TRIM(att_n),' : ',tmp_i
+      ENDIF
+    ELSE IF ( (x_typ == NF90_FLOAT).OR.(x_typ == NF90_DOUBLE) ) THEN
+      iret = NF90_GET_ATT (fid, vid, att_n, tmp_r)
+        IF (l_dbg) THEN
+        WRITE(ipslout,*) '   ',TRIM(att_n),' : ',tmp_r
+      ENDIF
+      IF (index(att_n,'missing_value') > 0) THEN
+        mis_v = tmp_r
+      ENDIF
+    ELSE
+      tmp_n = ''
+      iret = NF90_GET_ATT (fid, vid, att_n, tmp_n)
+        IF (l_dbg) THEN
+        WRITE(ipslout,*) '   ',TRIM(att_n),' : ',TRIM(tmp_n)
+      ENDIF
+      IF (index(att_n,'axis') > 0) THEN
+        axs_l = tmp_n
+      ENDIF
+    ENDIF
+  ENDDO
+!?
+!!!!!!!!!! We will need a verification on the type of the variable
+!?
+!-
+! 2.0 The dimensions are analysed to determine what is to be read
+!-
+! 2.1 the longitudes
+!-
+  IF ( w_dim(1) /= iim .OR. w_dim(2) /= jjm) THEN
+!---
+!-- There is a possibility that we have to deal with a compressed axis !
+!---
+    iret = NF90_INQUIRE_DIMENSION (fid, dimids(1), &
+             name=tmp_n, len=clen)
+    iret = NF90_INQ_VARID (fid, tmp_n, cvid)
+!---
+    IF (l_dbg) WRITE(ipslout,*) &
+      'Dimname, iret , NF90_NOERR : ',TRIM(tmp_n),iret,NF90_NOERR
+!---
+!-- If we have an axis which has the same name
+!-- as the dimension we can see if it is compressed
+!---
+!-- TODO TODO for zoom2d
+!---
+    IF (iret == NF90_NOERR) THEN
+      iret = NF90_GET_ATT (fid, cvid, 'compress', str1)
+!-----
+      IF (iret == NF90_NOERR) THEN
+        iret = NF90_INQUIRE_VARIABLE (fid,cvid,xtype=x_typ,ndims=cnd)
+!-------
+        IF ( cnd /= 1 .AND. x_typ /= NF90_INT) THEN
+          CALL histerr (3,'flinget', &
+            'Variable '//TRIM(tmp_n)//' can not be a compressed axis', &
+            'Either it has too many dimensions'// &
+            ' or it is not of type integer', ' ')
+        ELSE
+!---------
+!-------- Let us see if we already have that index table
+!---------
+          IF (    (cind_len /= clen).OR.(cind_vid /= cvid) &
+              .OR.(cind_fid /= fid) ) THEN
+            IF (ALLOCATED(cindex))   DEALLOCATE(cindex)
+            ALLOCATE(cindex(clen))
+            cind_len = clen
+            cind_vid = cvid
+            cind_fid = fid
+            iret = NF90_GET_VAR (fid, cvid, cindex)
+          ENDIF
+!---------
+!-------- In any case we need to set the slab of data to be read
+!---------
+          uncompress = .TRUE.
+          w_sta(1) = 1
+          w_len(1) = clen
+          i2d = 1
+        ENDIF
+      ELSE
+        str1 = 'The horizontal dimensions of '//varname
+        CALL histerr (3,'flinget',str1, &
+          'is not compressed and does not'// &
+          ' correspond to the requested size',' ')
+      ENDIF
+    ELSE
+      IF (w_dim(1) /= iim) THEN
+        str1 = 'The longitude dimension of '//varname
+        CALL histerr (3,'flinget',str1, &
+          'in the file is not equal to the dimension', &
+          'that should be read')
+      ENDIF
+      IF (w_dim(2) /= jjm) THEN
+        str1 = 'The latitude dimension of '//varname
+        CALL histerr (3,'flinget',str1, &
+          'in the file is not equal to the dimension', &
+          'that should be read')
+      ENDIF
+    ENDIF
+  ELSE
+    w_sta(1:2) = (/ iideb, jjdeb /)
+    w_len(1:2) = (/ iilen, jjlen /)
+    i2d = 2
+  ENDIF
+!-
+! 2.3 Now the difficult part, the 3rd dimension which can be
+! time or levels.
+!-
+! Priority is given to the time axis if only three axes are present.
+!-
+  IF (ndims > i2d) THEN
+!---
+!-- 2.3.1 We have a vertical axis
+!---
+    IF (llm == 1 .AND. ndims == i2d+2 .OR. llm == w_dim(i2d+1)) THEN
+!-----
+      IF (w_dim(i2d+1) /= llm) THEN
+        CALL histerr (3,'flinget', &
+          'The vertical dimension of '//varname, &
+          'in the file is not equal to the dimension', &
+          'that should be read')
+      ELSE
+        w_sta(i2d+1) = 1
+        IF (llm > 0) THEN
+          w_len(i2d+1) = llm
+        ELSE
+          w_len(i2d+1) = w_sta(i2d+1)
+        ENDIF
+      ENDIF
+!-----
+      IF ((itau_fin-itau_dep) >= 0) THEN
+        IF      (ndims /= i2d+2) THEN
+          CALL histerr (3,'flinget', &
+            'You attempt to read a time slab', &
+            'but there is no time axis on this variable', varname)
+        ELSE IF ((itau_fin - itau_dep) <= w_dim(i2d+2)) THEN
+          w_sta(i2d+2) = itau_dep
+          w_len(i2d+2) = itau_fin-itau_dep+1
+        ELSE
+          CALL histerr (3,'flinget', &
+            'The time step you try to read is not', &
+            'in the file (1)', varname)
+        ENDIF
+      ELSE IF (ndims == i2d+2 .AND. w_dim(i2d+2) > 1) THEN
+        CALL histerr (3,'flinget', &
+          'There is a time axis in the file but no', &
+          'time step give in the call', varname)
+      ELSE
+        w_sta(i2d+2) = 1
+        w_len(i2d+2) = 1
+      ENDIF
+    ELSE
+!-----
+!---- 2.3.2 We do not have any vertical axis
+!-----
+      IF (ndims == i2d+2) THEN
+        CALL histerr (3,'flinget', &
+          'The file contains 4 dimensions', &
+          'but only 3 are requestes for variable ', varname)
+      ENDIF
+      IF ((itau_fin-itau_dep) >= 0) THEN
+        IF (ndims == i2d+1) THEN
+          IF ((itau_fin-itau_dep) < w_dim(i2d+1) ) THEN
+            w_sta(i2d+1) = itau_dep
+            w_len(i2d+1) = itau_fin-itau_dep+1
+          ELSE
+            CALL histerr (3,'flinget', &
+              'The time step you try to read is not', &
+              'in the file (2)', varname)
+          ENDIF
+        ELSE
+          CALL histerr (3,'flinget', &
+            'From your input you sould have 3 dimensions', &
+            'in the file but there are 4', varname)
+        ENDIF
+      ELSE
+        IF (ndims == i2d+1 .AND. w_dim(i2d+1) > 1) THEN
+          CALL histerr (3,'flinget', &
+            'There is a time axis in the file but no', &
+            'time step given in the call', varname)
+        ELSE
+          w_sta(i2d+1) = 1
+          w_len(i2d+1) = 1
+        ENDIF
+      ENDIF
+    ENDIF
+  ELSE
+!---
+!-- 2.3.3 We do not have any vertical axis
+!---
+    w_sta(i2d+1:i2d+2) = (/ 0, 0 /)
+    w_len(i2d+1:i2d+2) = (/ 0, 0 /)
+  ENDIF
+!-
+! 3.0 Reading the data
+!-
+  IF (l_dbg) WRITE(ipslout,*) &
+    'flinget_mat 3.0 : ', uncompress, w_sta, w_len
+!---
+  var(:) = mis_v
+  IF (uncompress) THEN
+!---
+    IF (ALLOCATED(var_tmp)) THEN
+      IF (SIZE(var_tmp) < PRODUCT(w_len(:),mask=(w_len>1))) THEN
+         DEALLOCATE(var_tmp)
+         ALLOCATE(var_tmp(PRODUCT(w_len(:),mask=(w_len>1))))
+      ENDIF
+    ELSE
+      ALLOCATE(var_tmp(PRODUCT(w_len(:),mask=(w_len>1))))
+    ENDIF
+!---
+    iret = NF90_GET_VAR (fid, vid, var_tmp, &
+             start=w_sta(:), count=w_len(:))
+!---
+    IF (iret /= NF90_NOERR) THEN
+        WRITE(ipslout,*) 'flinget_mat 2.9 : ',NF90_STRERROR (iret)
+        CALL ipslerr(3, 'flinget_mat','Error on netcdf NF90_GET_VAR','', '')
+    ENDIF
+!---
+    itau_len=itau_fin-itau_dep+1
+    IF (l_dbg) WRITE(ipslout,*) 'flinget_mat 3.0 : clen, itau_len ',clen,itau_len
+    var(:) = mis_v
+    IF (itau_len > 0) THEN
+       DO it=1,itau_len
+          DO il=1,clen
+             ip = il + (it-1)*clen
+             i2p = cindex(il)+(it-1)*iim*jjm
+             var(i2p) = var_tmp(ip)
+          ENDDO
+       ENDDO
+    ELSE
+       var(cindex(:)) = var_tmp(:)
+    ENDIF
+!---
+  ELSE
+    iret = NF90_GET_VAR (fid, vid, var, &
+             start=w_sta(:), count=w_len(:))
+
+    IF (iret /= NF90_NOERR) THEN
+        WRITE(ipslout,*) 'flinget_mat 3.1 : ',NF90_STRERROR (iret)
+        CALL  ipslerr(3, 'flinget_mat','Error on netcdf NF90_GET_VAR','Read in netcdf failed', '')
+    ENDIF
+  ENDIF
+!-
+  IF (l_dbg) WRITE(ipslout,*) 'flinget_mat 3.2 : ',NF90_STRERROR (iret)
+!--------------------------
+END  SUBROUTINE flinget_mat
+!-
+!===
+!-
+SUBROUTINE flinget_scal &
+  (fid_in, varname, iim, jjm, llm, ttm, itau_dep, itau_fin, var)
+!---------------------------------------------------------------------
+!- This subroutine will read the variable named varname from
+!- the file previously opened by flinopen and identified by fid
+!-
+!- If variable is of size zero a global attribute is read. This
+!- global attribute will be of type real
+!-
+!- INPUT
+!-
+!- fid      : File ID returned by flinopen
+!- varname  : Name of the variable to be read from the file
+!- iim      : | These three variables give the size of the variables
+!- jjm      : | to be read. It will be verified that the variables
+!- llm      : | fits in there.
+!- ttm      : |
+!- itau_dep : Time step at which we will start to read
+!- itau_fin : Time step until which we are going to read
+!-           For the moment this is done on indeces but it should be
+!-           in the physical space
+!-           If there is no time-axis in the file then use a
+!-           itau_fin < itau_dep, this will tell flinget not to
+!-           expect a time-axis in the file.
+!-
+!- OUTPUT
+!-
+!- var      : scalar that will contain the data
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+! ARGUMENTS
+!-
+  INTEGER :: fid_in
+  CHARACTER(LEN=*) :: varname
+  INTEGER :: iim, jjm, llm, ttm, itau_dep, itau_fin
+  REAL :: var
+!-
+! LOCAL
+!-
+  INTEGER :: iret, fid, vid
+  INTEGER :: attlen, attnum
+  INTEGER :: ndims, nb_atts
+  INTEGER,DIMENSION(NF90_MAX_VAR_DIMS) :: dimids
+  LOGICAL :: var_exists
+!-
+  LOGICAL :: l_dbg
+  INTEGER :: lll
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+
+  IF (l_dbg) THEN
+    WRITE (ipslout,*) 'flinget_scal in file with id ',fid_in
+  ENDIF
+!-
+  fid = ncids(fid_in)
+  iret = NF90_INQUIRE_ATTRIBUTE(fid, NF90_GLOBAL, varname, len=attlen, attnum=attnum)
+!-
+! 1.0 Reading a global attribute
+!-
+  IF ( iret == nf90_noerr ) THEN
+     !
+     ! This seems to be a Global attribute
+     !
+     iret = NF90_GET_ATT (fid, NF90_GLOBAL, varname, var)
+  ELSE
+     !
+     ! If there was an error on the test for a global attribute it
+     ! is perhaps a scalar variable.
+     !
+     vid = -1
+     iret = NF90_INQ_VARID (fid, varname, vid)
+     !
+     IF ( (vid >= 0).AND.(iret == NF90_NOERR) ) THEN
+        iret = NF90_INQUIRE_VARIABLE (fid, vid, &
+             ndims=ndims, dimids=dimids, nAtts=nb_atts)
+        IF (ndims == 1) THEN
+           iret = NF90_INQUIRE_DIMENSION (fid, dimids(1), len=lll)
+        ENDIF
+
+        IF ( ((ndims == 0) .OR. ((ndims == 1).AND.(lll == 1))) .AND. (nb_atts >= 0) ) THEN
+           iret = NF90_GET_VAR(fid, vid, var)
+        ELSE
+           CALL histerr (3,'flinget_scal', &
+                'The variable has coordinates and thus is probably not a scalar.', &
+                'Check your netCDF file.', " ")
+        ENDIF
+     ENDIF
+     IF (l_dbg) THEN
+        WRITE(ipslout,*) "Reading a Scalar value for varibale ", varname," It has value ", var
+     ENDIF
+  ENDIF
+!-
+!---------------------------
+END  SUBROUTINE flinget_scal
+!-
+!===
+!-
+SUBROUTINE flinfindcood (fid_in, axtype, vid, ndim)
+!---------------------------------------------------------------------
+!- This subroutine explores the file in order to find
+!- the coordinate according to a number of rules
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+! ARGUMENTS
+!-
+  INTEGER :: fid_in, vid, ndim
+  CHARACTER(LEN=3) :: axtype
+!-
+! LOCAL
+!-
+  INTEGER :: iv, iret, dimnb
+  CHARACTER(LEN=40) :: dimname, dimuni1, dimuni2, dimuni3
+  CHARACTER(LEN=80) :: str1
+  LOGICAL :: found_rule = .FALSE.
+!---------------------------------------------------------------------
+  vid = -1
+!-
+! Make sure all strings are invalid
+!-
+  dimname = '?-?'
+  dimuni1 = '?-?'
+  dimuni2 = '?-?'
+  dimuni3 = '?-?'
+!-
+! First rule : we look for the correct units
+! lon : east
+! lat : north
+! We make an exact check as it would be too easy to mistake
+! some units by just comparing the substrings.
+!-
+  SELECTCASE(axtype)
+  CASE ('lon')
+    dimuni1 = 'degree_e'
+    dimuni2 = 'degrees_e'
+    found_rule = .TRUE.
+  CASE('lat')
+    dimuni1 = 'degree_n'
+    dimuni2 = 'degrees_n'
+    found_rule = .TRUE.
+  CASE('lev')
+    dimuni1 = 'm'
+    dimuni2 = 'km'
+    dimuni3 = 'hpa'
+    found_rule = .TRUE.
+  CASE DEFAULT
+    found_rule = .FALSE.
+  END SELECT
+!-
+  IF (found_rule) THEN
+    iv = 0
+    DO WHILE ( (vid < 0).AND.(iv < ncnbva(fid_in)) )
+      iv = iv+1
+      str1 = ''
+      iret = NF90_GET_ATT (ncids(fid_in), iv, 'units', str1)
+      IF (iret == NF90_NOERR) THEN
+        CALL strlowercase (str1)
+        IF (    (INDEX(str1, TRIM(dimuni1)) == 1) &
+            .OR.(INDEX(str1, TRIM(dimuni2)) == 1) &
+            .OR.(INDEX(str1, TRIM(dimuni3)) == 1) ) THEN
+          vid = iv
+          iret = NF90_INQUIRE_VARIABLE (ncids(fid_in), iv, ndims=ndim)
+        ENDIF
+      ENDIF
+    ENDDO
+  ENDIF
+!-
+! Second rule : we find specific names :
+! lon : nav_lon
+! lat : nav_lat
+! Here we can check if we find the substring as the
+! names are more specific.
+!-
+  SELECTCASE(axtype)
+  CASE ('lon')
+    dimname = 'nav_lon lon longitude'
+    found_rule = .TRUE.
+  CASE('lat')
+    dimname = 'nav_lat lat latitude'
+    found_rule = .TRUE.
+  CASE('lev')
+    dimname = 'plev level depth deptht'
+    found_rule = .TRUE.
+  CASE DEFAULT
+    found_rule = .FALSE.
+  END SELECT
+!-
+  IF (found_rule) THEN
+    iv = 0
+    DO WHILE ( (vid < 0).AND.(iv < ncnbva(fid_in)) )
+      iv = iv+1
+      str1=''
+      iret = NF90_INQUIRE_VARIABLE (ncids(fid_in), iv, &
+                                    name=str1, ndims=ndim)
+      IF (INDEX(dimname,TRIM(str1)) >= 1) THEN
+        vid = iv
+      ENDIF
+    ENDDO
+  ENDIF
+!-
+! Third rule : we find a variable with the same name as the dimension
+! lon = 1
+! lat = 2
+! lev = 3
+!-
+  IF (vid < 0) THEN
+    SELECTCASE(axtype)
+    CASE ('lon')
+      dimnb = 1
+      found_rule = .TRUE.
+    CASE('lat')
+      dimnb = 2
+      found_rule = .TRUE.
+    CASE('lev')
+      dimnb = 3
+      found_rule = .TRUE.
+    CASE DEFAULT
+      found_rule = .FALSE.
+    END SELECT
+!---
+    IF (found_rule) THEN
+      iret = NF90_INQUIRE_DIMENSION (ncids(fid_in), dimnb, name=dimname)
+      iv = 0
+      DO WHILE ( (vid < 0).AND.(iv < ncnbva(fid_in)) )
+        iv = iv+1
+        str1=''
+        iret = NF90_INQUIRE_VARIABLE (ncids(fid_in), iv, &
+                                      name=str1, ndims=ndim)
+        IF (INDEX(dimname,TRIM(str1)) == 1) THEN
+          vid = iv
+        ENDIF
+      ENDDO
+    ENDIF
+  ENDIF
+!-
+! Stop the program if no coordinate was found
+!-
+  IF (vid < 0) THEN
+    CALL histerr (3,'flinfindcood', &
+           'No coordinate axis was found in the file', &
+           'The data in this file can not be used', axtype)
+  ENDIF
+!--------------------------
+END SUBROUTINE flinfindcood
+!-
+!===
+!-
+SUBROUTINE flinclo (fid_in)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid_in
+!-
+  INTEGER :: iret
+!---------------------------------------------------------------------
+  iret = NF90_CLOSE (ncids(fid_in))
+  ncfileopen(fid_in) = .FALSE.
+!---------------------
+END SUBROUTINE flinclo
+!-
+!===
+!-
+SUBROUTINE flinquery_var(fid_in, varname, exists)
+!---------------------------------------------------------------------
+!- Queries the existance of a variable in the file.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid_in
+  CHARACTER(LEN=*) varname
+  LOGICAL :: exists
+!-
+  INTEGER :: iret, fid, vid
+!---------------------------------------------------------------------
+  fid = ncids(fid_in)
+  vid = -1
+  iret = NF90_INQ_VARID (fid, varname, vid)
+!-
+  exists = ( (vid >= 0).AND.(iret == NF90_NOERR) )
+!---------------------------
+END SUBROUTINE flinquery_var
+!-
+!===
+!-
+SUBROUTINE flininspect (fid_in)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+! fid : File id to inspect
+!-
+  INTEGER :: fid_in
+!-
+!- LOCAL
+!-
+  INTEGER :: iim, jjm, llm, ttm, fid_out
+  INTEGER :: iret, fid, ndims, nvars, nb_atts, id_unlim
+  INTEGER :: iv, in, lll
+  INTEGER :: xid, yid, zid, tid
+  INTEGER,DIMENSION(NF90_MAX_VAR_DIMS) :: idimid
+  CHARACTER(LEN=80) :: name
+  CHARACTER(LEN=30) :: axname
+!---------------------------------------------------------------------
+  fid = ncids(fid_in)
+!-
+  iret = NF90_INQUIRE (fid, nDimensions=ndims, nVariables=nvars, &
+                       nAttributes=nb_atts, unlimitedDimId=id_unlim)
+!-
+  WRITE (*,*) 'IOIPSL ID                   : ',fid_in
+  WRITE (*,*) 'NetCDF ID                   : ',fid
+  WRITE (*,*) 'Number of dimensions        : ',ndims
+  WRITE (*,*) 'Number of variables         : ',nvars
+  WRITE (*,*) 'Number of global attributes : ',nb_atts
+  WRITE (*,*) 'ID unlimited                : ',id_unlim
+!-
+  xid = -1; iim = 0;
+  yid = -1; jjm = 0;
+  zid = -1; llm = 0;
+  tid = -1; ttm = 0;
+!-
+  DO iv=1,ndims
+!---
+    iret = NF90_INQUIRE_DIMENSION (fid, iv, name=axname, len=lll)
+    CALL strlowercase (axname)
+    axname = ADJUSTL(axname)
+!---
+    WRITE (*,*) 'Dimension number : ',iv
+    WRITE (*,*) 'Dimension name   : ',TRIM(axname)
+!---
+    IF      (    (INDEX(axname,'x') == 1) &
+             .OR.(INDEX(axname,'lon') == 1)) THEN
+      xid = iv; iim = lll;
+      WRITE (*,*) 'Dimension X size   : ',iim
+    ELSE IF (    (INDEX(axname,'y') == 1) &
+             .OR.(INDEX(axname,'lat') == 1)) THEN
+      yid = iv; jjm = lll;
+      WRITE (*,*) 'Dimension Y size   : ',jjm
+    ELSE IF (    (INDEX(axname,'lev') == 1) &
+             .OR.(INDEX(axname,'plev') == 1) &
+             .OR.(INDEX(axname,'z') == 1) &
+             .OR.(INDEX(axname,'depth') == 1)) THEN
+      zid = iv; llm = lll;
+      WRITE (*,*) 'Dimension Z size   : ',llm
+    ELSE IF (    (INDEX(axname,'tstep') == 1) &
+             .OR.(INDEX(axname,'time') == 1) &
+             .OR.(INDEX(axname,'time_counter') == 1)) THEN
+!---- For the time we certainly need to allow for other names
+      tid = iv; ttm = lll;
+    ELSE IF (ndims == 1) THEN
+!---- Nothing was found and ndims=1 then we have a vector of data
+      xid = 1; iim = lll;
+    ENDIF
+!---
+  ENDDO
+!-
+! Keep all this information
+!-
+  nbfiles = nbfiles+1
+!-
+  IF (nbfiles > nbfile_max) THEN
+    CALL histerr(3,'flininspect', &
+      'Too many files. Please increase nbfil_max', &
+      'in program flincom.F90.',' ')
+  ENDIF
+!-
+  ncids(nbfiles) = fid
+  ncnbd(nbfiles) = ndims
+!-
+  ncdims(nbfiles,1:4) = (/ iim, jjm, llm, ttm /)
+!-
+  ncfunli(nbfiles) = id_unlim
+  ncnba(nbfiles)   = nb_atts
+  ncnbva(nbfiles)  = nvars
+  ncfileopen(nbfiles) = .TRUE.
+!-
+  fid_out = nbfiles
+!-
+  DO in=1,nvars
+    iret = NF90_INQUIRE_VARIABLE (fid, in, &
+             name=name, ndims=ndims, dimids=idimid, nAtts=nb_atts)
+    WRITE (*,*) 'Variable number  ------------ > ', in
+    WRITE (*,*) 'Variable name        : ', TRIM(name)
+    WRITE (*,*) 'Number of dimensions : ', ndims
+    WRITE (*,*) 'Dimensions ID''s     : ', idimid(1:ndims)
+    WRITE (*,*) 'Number of attributes : ', nb_atts
+  ENDDO
+!-------------------------
+END SUBROUTINE flininspect
+!-
+!===
+!-
+END MODULE flincom
Index: /IOIPSL/tags/v2_2_5/src/fliocom.f90
===================================================================
--- /IOIPSL/tags/v2_2_5/src/fliocom.f90	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/src/fliocom.f90	(revision 5058)
@@ -0,0 +1,5179 @@
+MODULE fliocom
+!-
+!$Id$
+!-
+! This software is governed by the CeCILL license
+! See IOIPSL/IOIPSL_License_CeCILL.txt
+!---------------------------------------------------------------------
+USE netcdf
+!-
+USE defprec
+USE calendar,  ONLY : lock_calendar,ioget_calendar, &
+ &                    ioconf_calendar,ju2ymds,ymds2ju
+USE errioipsl, ONLY : ipslerr,ipsldbg, ipslout
+USE stringop,  ONLY : strlowercase,str_xfw
+!-
+IMPLICIT NONE
+!-
+PRIVATE
+!-
+PUBLIC :: &
+ &  fliocrfd, fliopstc, fliodefv, flioputv, flioputa, &
+ &  flioopfd, flioinqf, flioinqn, fliogstc, &
+ &  flioinqv, fliogetv, flioinqa, fliogeta, &
+ &  fliorenv, fliorena, fliodela, fliocpya, &
+ &  flioqstc, fliosync, flioclo,  fliodmpf, &
+ &  flio_dom_set,    flio_dom_unset, &
+ &  flio_dom_defset, flio_dom_defunset, flio_dom_definq, &
+ &  flio_dom_file,   flio_dom_att
+!-
+!!--------------------------------------------------------------------
+!! The following PUBLIC parameters (with "flio_" prefix)
+!! are used in the module "fliocom" :
+!!
+!! flio_max_files     : maximum number of simultaneously opened files
+!! flio_max_dims      : maximum number of dimensions for a file
+!! flio_max_var_dims  : maximum number of dimensions for a variable
+!!
+!! FLIO_DOM_NONE    : "named constant" for no_domain identifier
+!! FLIO_DOM_DEFAULT : "named constant" for default_domain identifier
+!!
+!! flio_i  : standard INTEGER external type
+!! flio_r  : standard REAL external type
+!! flio_c  : CHARACTER external type
+!! flio_i1 : INTEGER*1 external type
+!! flio_i2 : INTEGER*2 external type
+!! flio_i4 : INTEGER*4 external type
+!! flio_r4 : REAL*4 external type
+!! flio_r8 : REAL*8 external type
+!!--------------------------------------------------------------------
+  INTEGER,PARAMETER,PUBLIC :: &
+ &  flio_max_files=100, flio_max_dims=25, flio_max_var_dims=5
+  INTEGER,PARAMETER,PUBLIC :: &
+ &  flio_i = -1,        flio_r = -2,        flio_c =nf90_char, &
+ &  flio_i1=nf90_int1,  flio_i2=nf90_int2,  flio_i4=nf90_int4, &
+ &  flio_r4=nf90_real4, flio_r8=nf90_real8
+!-
+  INTEGER,PARAMETER,PUBLIC :: FLIO_DOM_NONE    =-1
+  INTEGER,PARAMETER,PUBLIC :: FLIO_DOM_DEFAULT = 0
+!-
+!!--------------------------------------------------------------------
+!! The "fliocrfd" routine creates a model file
+!! which contains the dimensions needed.
+!!
+!! SUBROUTINE fliocrfd (f_n,f_d_n,f_d_l,f_i,id_dom,mode,c_f_n)
+!!
+!! INPUT
+!!
+!! (C) f_n      : Name of the file to be created
+!! (C) f_d_n(:) : Array of (max nb_fd_mx) names of the dimensions
+!! (I) f_d_l(:) : Array of (max nb_fd_mx) lengths of the dimensions
+!!                For an unlimited dimension, enter a length of -1.
+!!                Actually, only one unlimited dimension is supported.
+!!
+!! OUTPUT
+!!
+!! (I) f_i  : Model file identifier
+!!
+!! Optional INPUT arguments
+!!
+!! (I) id_dom : Identifier of a domain defined by calling
+!!              "flio_dom_set". If this argument is present,
+!!              and not equal to FLIO_DOM_NONE, it will be
+!!              appended to the file name and
+!!              the attributes describing the related DOMAIN
+!!              will be put in the created file.
+!!              This argument can be equal to FLIO_DOM_DEFAULT
+!!              (see "flio_dom_defset").
+!! (C) mode   : String of (case insensitive) blank-separated words
+!!              defining the mode used to create the file.
+!!              Supported keywords : REPLACE, 32, 64
+!!              If this argument is present with the keyword "REPLACE",
+!!              the file will be created in mode "CLOBBER",
+!!              else the file will be created in mode "NOCLOBBER".
+!!              "32/64" defines the offset mode.
+!!              The default offset mode is 64 bits.
+!!              Keywords "NETCDF4" and "CLASSIC" are reserved
+!!              for future use.
+!!
+!! Optional OUTPUT arguments
+!!
+!! (C) c_f_n : Name of the created file.
+!!             This name can be different of "f_n",
+!!             if a suffix is added to the original name
+!!             (".nc" or "DOMAIN_identifier.nc").
+!!             The length of "c_f_n" must be sufficient
+!!             to receive the created file name.
+!!
+!!- NOTES
+!!
+!! The names used to identify the spatio-temporal dimensions
+!! (dimension associated to a coordinate variable)
+!! are the following :
+!!
+!!  Axis       Names
+!!
+!!    x        'x[...]'  'lon[...]'
+!!    y        'y[...]'  'lat[...]'
+!!    z        'z[...]'  'lev[...]'  'plev[...]'   'depth[...]'
+!!    t        't'       'time'      'tstep[...]'  'time_counter[...]'
+!!
+!! Please, apply these rules so that coordinates are
+!! correctly defined.
+!!--------------------------------------------------------------------
+!-
+!!--------------------------------------------------------------------
+!! The "fliopstc" routine defines the major coordinates system
+!! (spatio-temporal axis) of the model file (created by fliocrfd).
+!!
+!! SUBROUTINE fliopstc &
+!! & (f_i,x_axis,x_axis_2d,y_axis,y_axis_2d,z_axis, &
+!! &      t_axis,t_init,t_step,t_calendar)
+!!
+!! INPUT
+!!
+!! (I) f_i  : Model file identifier
+!!
+!! Optional INPUT arguments
+!!
+!! (R) x_axis(:)      : longitudinal grids
+!! (R) x_axis_2d(:,:) : longitudinal grids
+!! (R) y_axis(:)      : latitudinal grids
+!! (R) y_axis_2d(:,:) : latitudinal grids
+!! (R) z_axis(:)      : vertical grid
+!! (I) t_axis(:)      : timesteps on the time axis
+!! (R) t_init         : date in julian days at the beginning
+!! (R) t_step         : timestep in seconds between t_axis steps
+!! (C) t_calendar     : calendar
+!!
+!! [x/y]_axis and [x/y]_axis_2d are mutually exclusive.
+!!
+!!- NOTES
+!!
+!! The variables corresponding to the spatio-temporal coordinates
+!! are created according to the following characteristics :
+!!
+!!- Longitude axis     x_axis / x_axis_2d
+!!   Variable name     'lon'  / 'nav_lon'
+!!   Attributes        Values
+!!   'axis'            "X"
+!!   'standard_name'   "longitude"
+!!   'units'           "degrees_east"
+!!   'valid_min'       MINVAL(x_axis/x_axis_2d)
+!!   'valid_max'       MAXVAL(x_axis/x_axis_2d)
+!!
+!!- Latitude axis      y_axis / y_axis_2d
+!!   Variable name     'lat'  / 'nav_lat'
+!!   Attributes        Values
+!!   'axis'            "Y"
+!!   'standard_name'   "latitude"
+!!   'units'           "degrees_north"
+!!   'valid_min'       MINVAL(y_axis/y_axis_2d)
+!!   'valid_max'       MAXVAL(y_axis/y_axis_2d)
+!!
+!!- Vertical axis      z_axis
+!!   Variable name     'lev'
+!!   Attributes        Values
+!!   'axis'            "Z"
+!!   'standard_name'   "model_level_number"
+!!   'units'           "sigma_level"
+!!   'long_name'       "Sigma Levels"
+!!   'valid_min'       MINVAL(z_axis)
+!!   'valid_max'       MAXVAL(z_axis)
+!!
+!!- Time axis          t_axis
+!!   Variable name     'time'
+!!   Attributes        Values
+!!   'axis'            "T"
+!!   'standard_name'   "time"
+!!   'long_name'       "time steps"
+!!  ['calendar'        user/default valued]
+!!   'units'           calculated
+!!
+!! If you are not satisfied, it is possible
+!! to rename variables ("fliorenv")
+!! or overload the values of attributes ("flioputa").
+!! Be careful : the new values you use must allow to read variables
+!! as coordinates.
+!!
+!! The dimensions associated to the coordinates variables
+!! are searched according to their names (see "fliocrfd")
+!!--------------------------------------------------------------------
+!-
+INTERFACE fliodefv
+!!--------------------------------------------------------------------
+!! The "fliodefv" routines define a variable in a model file.
+!!
+!! SUBROUTINE fliodefv &
+!! & (f_i,v_n,[v_d],v_t, &
+!! &  axis,standard_name,long_name,units, &
+!! &  valid_min,valid_max,fillvalue)
+!!
+!! INPUT
+!!
+!! (I)  f_i  : Model file identifier
+!! (C)  v_n  : Name of variable to be defined
+!! (I) [v_d] :
+!!             "not present"
+!!                --> scalar variable
+!!             "array of one or several integers containing
+!!              the identifiers of the dimensions of the variable
+!!              (in the order specified to "fliocrfd"
+!!               or obtained from "flioopfd")"
+!!                --> multidimensioned variable
+!!
+!! Optional INPUT arguments
+!!
+!! (I) v_t : External type of the variable
+!!           "present"     --> see flio_..
+!!           "not present" --> type of standard real
+!! (C) axis,standard_name,long_name,units : Attributes
+!!     (axis should be used only for coordinates)
+!! (R) valid_min,valid_max,fillvalue : Attributes
+!!--------------------------------------------------------------------
+  MODULE PROCEDURE &
+ &  fliodv_r0d,fliodv_rnd
+END INTERFACE
+!-
+INTERFACE flioputv
+!!--------------------------------------------------------------------
+!! The "flioputv" routines put a variable (defined by fliodefv)
+!! in a model file.
+!!
+!! SUBROUTINE flioputv (f_i,v_n,v_v,start,count)
+!!
+!! INPUT
+!!
+!! (I) f_i    : model file identifier
+!! (C) v_n    : name of the variable to be written
+!! (R/I) v_v  : scalar or array (up to flio_max_var_dims dimensions)
+!!              containing the (standard) real/integer values
+!!
+!! Optional INPUT arguments
+!!
+!! (I) start(:) : array of integers specifying the index
+!!                where the first data value will be written
+!! (I) count(:) : array of integers specifying the number of
+!!                indices that will be written along each dimension
+!!                (not present if v_v is a scalar)
+!!--------------------------------------------------------------------
+!?INTEGERS of KIND 1 are not supported on all computers
+  MODULE PROCEDURE &
+ & fliopv_i40,fliopv_i41,fliopv_i42,fliopv_i43,fliopv_i44,fliopv_i45, &
+ & fliopv_i20,fliopv_i21,fliopv_i22,fliopv_i23,fliopv_i24,fliopv_i25, &
+!& fliopv_i10,fliopv_i11,fliopv_i12,fliopv_i13,fliopv_i14,fliopv_i15, &
+ & fliopv_r40,fliopv_r41,fliopv_r42,fliopv_r43,fliopv_r44,fliopv_r45, &
+ & fliopv_r80,fliopv_r81,fliopv_r82,fliopv_r83,fliopv_r84,fliopv_r85
+END INTERFACE
+!-
+INTERFACE flioputa
+!!--------------------------------------------------------------------
+!! The "flioputa" routines put a value for an attribute
+!! in a model file.
+!! If this attribute does not exist, it will be created.
+!!
+!! SUBROUTINE flioputa (f_i,v_n,a_n,a_v)
+!!
+!! INPUT
+!!
+!! (I) f_i  : Model file identifier
+!! (C) v_n  : Name of variable to which the attribute is assigned.
+!!            If this name is "?", the attribute will be global.
+!! (C) a_n  : Name of the attribute to be defined.
+!! ( ) a_v  : scalar or array of real (kind 4 or 8) or integer values,
+!!            or character string
+!!--------------------------------------------------------------------
+  MODULE PROCEDURE &
+ &  fliopa_r4_0d,fliopa_r4_1d,fliopa_r8_0d,fliopa_r8_1d, &
+ &  fliopa_i4_0d,fliopa_i4_1d,fliopa_tx_0d
+END INTERFACE
+!-
+!!--------------------------------------------------------------------
+!! The "flioopfd" routine opens an existing model file,
+!! and returns the dimensions used in the file and a file identifier.
+!! This information can be used to allocate the space needed
+!! to extract the data from the file.
+!!
+!! SUBROUTINE flioopfd (f_n,f_i,mode,nb_dim,nb_var,nb_gat)
+!!
+!! INPUT
+!!
+!! (C) f_n     : Name of the file to be opened
+!!
+!! OUTPUT
+!!
+!! (I) f_i      : Model file identifier
+!!
+!! Optional INPUT arguments
+!!
+!! (C) mode : Access mode to the file.
+!!            If this argument is present with the value "WRITE",
+!!            the file will be accessed in mode "READ-WRITE",
+!!            else the file will be accessed in mode "READ-ONLY".
+!!
+!! Optional OUTPUT arguments
+!!
+!! (I) nb_dim : number of dimensions
+!! (I) nb_var : number of variables
+!! (I) nb_gat : number of global attributes
+!!--------------------------------------------------------------------
+!-
+!!--------------------------------------------------------------------
+!! The "flioinqf" routine returns information
+!! about an opened model file given its identifier.
+!!
+!! SUBROUTINE flioinqf &
+!! & (f_i,nb_dim,nb_var,nb_gat,id_uld,id_dim,ln_dim)
+!!
+!! INPUT
+!!
+!! (I) f_i  : Model file identifier
+!!
+!! Optional OUTPUT arguments
+!!
+!! (I) nb_dim    : number of dimensions
+!! (I) nb_var    : number of variables
+!! (I) nb_gat    : number of global attributes
+!! (I) id_uld    : identifier of the unlimited dimension (0 if none)
+!! (I) id_dim(:) : identifiers of the dimensions
+!! (I) ln_dim(:) : lengths of the dimensions
+!!--------------------------------------------------------------------
+!-
+!!--------------------------------------------------------------------
+!! The "flioinqn" routine returns the names
+!! of the entities encountered in an opened model file.
+!!
+!! SUBROUTINE flioinqn &
+!! & (f_i,cn_dim,cn_var,cn_gat,cn_uld, &
+!! &  id_start,id_count,iv_start,iv_count,ia_start,ia_count)
+!!
+!! INPUT
+!!
+!! (I) f_i  : Model file identifier
+!!
+!! Optional OUTPUT arguments
+!!
+!! (C) cn_dim(:) : names of dimensions
+!! (C) cn_var(:) : names of variables
+!! (C) cn_gat(:) : names of global attributes
+!! (C) cn_uld    : names of the unlimited dimension
+!!
+!! Optional INPUT arguments
+!!
+!! (I) id_start,id_count,iv_start,iv_count,ia_start,ia_count
+!!
+!!  The prefix ( id       / iv      / ia              ) specifies
+!!         the (dimensions/variables/global attributes) entities
+!!
+!!  The suffix "start" specify the index from which
+!!  the first name will be retrieved (1 by default)
+!!
+!!  The suffix "count" specifies the number of names to be retrieved
+!!  (all by default)
+!!
+!!  If a requested entity is not available, a "?" will be returned.
+!!--------------------------------------------------------------------
+!-
+!!--------------------------------------------------------------------
+!! The "fliogstc" routine extracts the major coordinates system
+!! (spatio-temporal axis) of the model file (opened by flioopfd).
+!!
+!! SUBROUTINE fliogstc &
+!! & (f_i,x_axis,x_axis_2d,y_axis,y_axis_2d,z_axis, &
+!! &      t_axis,t_init,t_step,t_calendar, &
+!! &      x_start,x_count,y_start,y_count, &
+!! &      z_start,z_count,t_start,t_count)
+!!
+!! INPUT
+!!
+!! (I) f_i  : Model file identifier
+!!
+!! Optional OUTPUT arguments
+!!
+!! (R) x_axis(:)      : longitudinal grids
+!! (R) x_axis_2d(:,:) : longitudinal grids
+!! (R) y_axis(:)      : latitudinal grids
+!! (R) y_axis_2d(:,:) : latitudinal grids
+!! (R) z_axis(:)      : vertical grid
+!! (I) t_axis(:)      : timesteps on the time axis
+!! (R) t_init         : date in julian days at the beginning
+!! (R) t_step         : timestep in seconds between t_axis steps
+!! (C) t_calendar     : calendar attribute
+!!                      (the value is "not found" if the attribute
+!!                       is not present in the model file)
+!!
+!! [x/y]_axis and [x/y]_axis_2d are mutually exclusive.
+!!
+!! Optional INPUT arguments
+!!
+!! (I) x_start,x_count,y_start,y_count,z_start,z_count,t_start,t_count
+!!
+!!  The prefix (x/y/z/t) specifies the concerned direction.
+!!
+!!  The suffix "start" specify the index from which
+!!  the first data value will be read (1 by default)
+!!
+!!  The suffix "count" specifies the number of values to be read
+!!  (all by default)
+!!--------------------------------------------------------------------
+!-
+!!--------------------------------------------------------------------
+!! The "flioinqv" routine returns information about a model
+!! variable given its name.
+!! This information can be used to allocate the space needed
+!! to extract the variable from the file.
+!!
+!! SUBROUTINE flioinqv &
+!! & (f_i,v_n,l_ex,nb_dims,len_dims,id_dims, &
+!! &  nb_atts,cn_atts,ia_start,ia_count)
+!!
+!! INPUT
+!!
+!! (I) f_i  : Model file identifier
+!! (C) v_n  : Name of the variable
+!!
+!! OUTPUT
+!!
+!! (L) l_ex  : Existence of the variable
+!!
+!! Optional OUTPUT arguments
+!!
+!! (I) v_t          : External type of the variable (see flio_..)
+!! (I) nb_dims      : number of dimensions of the variable
+!! (I) len_dims(:)  : list of dimension lengths of the variable
+!! (I) id_dims(:)   : list of dimension identifiers of the variable
+!! (I) nb_atts      : number of attributes of the variable
+!! (C) cn_atts(:)   : names of the attributes
+!!
+!! Optional INPUT arguments
+!!
+!! (I) ia_start : index of the first attribute whose the name
+!!                will be retrieved (1 by default)
+!! (I) ia_count : number of names to be retrieved (all by default)
+!!
+!!  If a requested entity is not available, a "?" will be returned.
+!!--------------------------------------------------------------------
+!-
+INTERFACE fliogetv
+!!--------------------------------------------------------------------
+!! The "fliogetv" routines get a variable from a model file.
+!!
+!! SUBROUTINE fliogetv (f_i,v_n,v_v,start,count)
+!!
+!! INPUT
+!!
+!! (I) f_i  : Model file identifier
+!! (C) v_n  : Name of the variable to be read
+!!
+!! OUTPUT
+!!
+!! (R/I) v_v  : scalar or array (up to flio_max_var_dims dimensions)
+!!              that will contain the (standard) real/integer values
+!!
+!! Optional INPUT arguments
+!!
+!! (I) start(:) : array of integers specifying the index
+!!                from which the first data value will be read
+!! (I) count(:) : array of integers specifying the number of
+!!                indices that will be read along each dimension
+!!                (not present if v_v is a scalar)
+!!--------------------------------------------------------------------
+!?INTEGERS of KIND 1 are not supported on all computers
+  MODULE PROCEDURE &
+ & fliogv_i40,fliogv_i41,fliogv_i42,fliogv_i43,fliogv_i44,fliogv_i45, &
+ & fliogv_i20,fliogv_i21,fliogv_i22,fliogv_i23,fliogv_i24,fliogv_i25, &
+!& fliogv_i10,fliogv_i11,fliogv_i12,fliogv_i13,fliogv_i14,fliogv_i15, &
+ & fliogv_r40,fliogv_r41,fliogv_r42,fliogv_r43,fliogv_r44,fliogv_r45, &
+ & fliogv_r80,fliogv_r81,fliogv_r82,fliogv_r83,fliogv_r84,fliogv_r85
+END INTERFACE
+!-
+!!--------------------------------------------------------------------
+!! The "flioinqa" routine returns information about an
+!! attribute of a variable given their names, in a model file.
+!! Information about a variable includes its existence,
+!! and the number of values currently stored in the attribute.
+!! For a string-valued attribute, this is the number of
+!! characters in the string.
+!! This information can be used to allocate the space needed
+!! to extract the attribute from the file.
+!!
+!! SUBROUTINE flioinqa (f_i,v_n,a_n,l_ex,a_t,a_l)
+!!
+!! INPUT
+!!
+!! (I) f_i : Model file identifier
+!! (C) v_n : Name of variable to which the attribute is assigned.
+!!           This name is "?" for a global attribute.
+!! (C) a_n : Name of the concerned attribute.
+!!
+!! OUTPUT
+!!
+!! (L) l_ex : existence of the variable
+!!
+!! Optional OUTPUT arguments
+!!
+!! (I) a_t : external type of the attribute
+!! (I) a_l : number of values of the attribute
+!!--------------------------------------------------------------------
+!-
+INTERFACE fliogeta
+!!--------------------------------------------------------------------
+!! The "fliogeta" routines get a value for an attribute
+!! in a model file.
+!!
+!! SUBROUTINE fliogeta (f_i,v_n,a_n,a_v)
+!!
+!! INPUT
+!!
+!! (I) f_i  : Model file identifier
+!! (C) v_n  : Name of variable to which the attribute is assigned.
+!!            This name is "?" for a global attribute.
+!! (C) a_n  : Name of the attribute to be retrieved.
+!! ( ) a_v  : scalar or array of real (kind 4 or 8) or integer values,
+!!            or character string
+!!--------------------------------------------------------------------
+  MODULE PROCEDURE &
+ &  flioga_r4_0d,flioga_r4_1d,flioga_r8_0d,flioga_r8_1d, &
+ &  flioga_i4_0d,flioga_i4_1d,flioga_tx_0d
+END INTERFACE
+!-
+!!--------------------------------------------------------------------
+!! The "fliorenv" routine renames a variable, in a model file.
+!!
+!! SUBROUTINE fliorenv (f_i,v_o_n,v_n_n)
+!!
+!! INPUT
+!!
+!! (I) f_i    : Model file identifier
+!! (C) v_o_n  : Old name of the variable
+!! (C) v_n_n  : New name of the variable
+!!--------------------------------------------------------------------
+!-
+!!--------------------------------------------------------------------
+!! The "fliorena" routine renames an attribute
+!! of a variable, in a model file.
+!!
+!! SUBROUTINE fliorena (f_i,v_n,a_o_n,a_n_n)
+!!
+!! INPUT
+!!
+!! (I) f_i    : Model file identifier
+!! (C) v_n    : Name of variable to which the attribute is assigned.
+!!              This name is "?" for a global attribute.
+!! (C) a_o_n  : Old name of the concerned attribute.
+!! (C) a_n_n  : New name of the concerned attribute.
+!!--------------------------------------------------------------------
+!-
+!!--------------------------------------------------------------------
+!! The "fliodela" routine deletes an attribute in a model file.
+!!
+!! SUBROUTINE fliodela (f_i,v_n,a_n)
+!!
+!! INPUT
+!!
+!! (I) f_i  : Model file identifier
+!! (C) v_n  : Name of variable to which the attribute is assigned.
+!!            This name is "?" for a global attribute.
+!! (C) a_n  : Name of the concerned attribute.
+!!--------------------------------------------------------------------
+!-
+!!--------------------------------------------------------------------
+!! The "fliocpya" routine copies an attribute
+!! from one open model file to another.
+!! It can also be used to copy an attribute from
+!! one variable to another within the same model file.
+!!
+!! SUBROUTINE fliocpya (f_i_i,v_n_i,a_n,f_i_o,v_n_o)
+!!
+!! INPUT
+!!
+!! (I) f_i_i : Identifier of the input  model file
+!! (C) v_n_i : Name of the input variable
+!!             This name is "?" for a global attribute.
+!! (C) a_n   : Name of the concerned attribute.
+!! (I) f_i_o : Identifier of the output model file
+!!             It can be the same as the input identifier.
+!! (C) v_n_o : Name of the output variable
+!!             This name is "?" for a global attribute.
+!!--------------------------------------------------------------------
+!-
+!!--------------------------------------------------------------------
+!! The "flioqstc" routine search for a spatio-temporal coordinate
+!! in a model file and returns its name.
+!!
+!! SUBROUTINE flioqstc (f_i,c_type,l_ex,c_name)
+!!
+!! INPUT
+!!
+!! (I) f_i     : Model file identifier
+!! (C) c_type  : Type of the coordinate ("x"/"y"/"z"/"t")
+!!
+!! OUTPUT
+!!
+!! (L) l_ex    : existence of the coordinate
+!! (C) c_name  : name of the coordinate
+!!
+!!- NOTES
+!!
+!! The following rules are used for searching variables
+!! which are spatio-temporal coordinates (x/y/z/t).
+!!
+!!-- Rule 1 : we look for a variable with one dimension
+!!--          and which has the same name as its dimension
+!!
+!!-- Rule 2 : we look for a correct "axis" attribute
+!!
+!!  Axis       Axis attribute             Number of dimensions
+!!             (case insensitive)
+!!
+!!    x         X                         1/2
+!!    y         Y                         1/2
+!!    z         Z                         1
+!!    t         T                         1
+!!
+!!-- Rule 3 : we look for a correct "standard_name" attribute
+!!
+!!  Axis       Axis attribute          Number of dimensions
+!!             (case insensitive)
+!!
+!!    x         longitude              1/2
+!!    y         latitude               1/2
+!!    z         model_level_number     1
+!!    t         time                   1
+!!
+!!-- Rule 4 : we look for a specific name
+!!
+!!  Axis   Names
+!!
+!!    x    'nav_lon' 'lon'    'longitude'
+!!    y    'nav_lat' 'lat'    'latitude'
+!!    z    'depth'   'deptht' 'height'      'level'
+!!         'lev'     'plev'   'sigma_level' 'layer'
+!!    t    'time'    'tstep'  'timesteps'
+!!
+!!--------------------------------------------------------------------
+!-
+!!--------------------------------------------------------------------
+!! The "fliosync" routine synchronise one or all opened model files,
+!! to minimize data loss in case of abnormal termination.
+!!
+!! SUBROUTINE fliosync (f_i)
+!!
+!! Optional INPUT arguments
+!!
+!! (I) f_i  : Model file identifier
+!!            If this argument is not present,
+!!            all the opened model files are synchronised.
+!---------------------------------------------------------------------
+!-
+!!--------------------------------------------------------------------
+!! The "flioclo" routine closes one or all opened model files
+!! and frees the space needed to keep information about the files
+!!
+!! SUBROUTINE flioclo (f_i)
+!!
+!! Optional INPUT arguments
+!!
+!! (I) f_i  : Model file identifier
+!!            If this argument is not present,
+!!            all the opened model files are closed.
+!!--------------------------------------------------------------------
+!-
+!!--------------------------------------------------------------------
+!! The "fliodmpf" routine dumps a model file
+!! and prints the result on the standard output.
+!!
+!! SUBROUTINE fliodmpf (f_n)
+!!
+!! INPUT
+!!
+!! (C) f_n  : Name of the model file to be dumped
+!!--------------------------------------------------------------------
+!-
+!!--------------------------------------------------------------------
+!! This "flio_dom_set" sets up the domain activity of IOIPSL.
+!! It stores all the domain information and allows it to be stored
+!! in the model file and change the file names.
+!!
+!! This routine must be called by the user before opening
+!! the model file.
+!!
+!! SUBROUTINE flio_dom_set &
+!!  & (dtnb,dnb,did,dsg,dsl,dpf,dpl,dhs,dhe,cdnm,id_dom)
+!!
+!! INPUT
+!!
+!! (I) dtnb   : total number of domains
+!! (I) dnb    : domain number
+!! (I) did(:) : distributed dimensions identifiers
+!!              (up to 5 dimensions are supported)
+!! (I) dsg(:) : total number of points for each dimension
+!! (I) dsl(:) : local number of points for each dimension
+!! (I) dpf(:) : position of first local point for each dimension
+!! (I) dpl(:) : position of last local point for each dimension
+!! (I) dhs(:) : start halo size for each dimension
+!! (I) dhe(:) : end halo size for each dimension
+!! (C) cdnm   : Model domain definition name.
+!!              The names actually supported are :
+!!              "BOX", "APPLE", "ORANGE".
+!!              These names are case insensitive.
+!!
+!! OUTPUT argument
+!!
+!! (I) id_dom : Model domain identifier
+!!
+!!--------------------------------------------------------------------
+!!
+!!--------------------------------------------------------------------
+!! The "flio_dom_unset" routine unsets one or all set domains
+!! and frees the space needed to keep information about the domains
+!!
+!! This routine should be called by the user to free useless domains.
+!!
+!! SUBROUTINE flio_dom_unset (id_dom)
+!!
+!! Optional INPUT arguments
+!!
+!! (I) id_dom : Model domain identifier
+!!      >=1 & <= dom_max_nb : the domain is closed
+!!      not present         : all the set model domains are unset
+!!--------------------------------------------------------------------
+!!
+!!--------------------------------------------------------------------
+!! The "flio_dom_defset" sets
+!! the default domain identifier.
+!!
+!! SUBROUTINE flio_dom_defset (id_dom)
+!!
+!! INPUT argument
+!!
+!! (I) id_dom : Model default domain identifier
+!!     ( >=1 & <= dom_max_nb )
+!!     This identifier will be able to be taken by calling
+!!     "flio_dom_definq" and used to create model files
+!!     with the corresponding domain definitions
+!!--------------------------------------------------------------------
+!!
+!!--------------------------------------------------------------------
+!! The "flio_dom_defunset" routine unsets
+!! the default domain identifier.
+!!
+!! SUBROUTINE flio_dom_defunset ()
+!!
+!!--------------------------------------------------------------------
+!!
+!!--------------------------------------------------------------------
+!! The "flio_dom_definq" routine inquires about
+!! the default domain identifier.
+!! You should call this procedure to safeguard the current
+!! default domain identifier if you wish to use locally
+!! another default domain, in order to restore it.
+!!
+!! SUBROUTINE flio_dom_definq (id_dom)
+!!
+!! OUTPUT argument
+!!
+!! (I) id_dom : Model default domain identifier
+!!     IF no default domain identifier has been set,
+!!     the returned value is "FLIO_DOM_NONE".
+!!--------------------------------------------------------------------
+!-
+!---------------------------------------------------------------------
+! This is the data we keep concerning each file we open
+!---------------------------------------------------------------------
+!- For each file
+!- (I) nw_id(f_i)   : index to access at this file
+!- (I) nw_nd(f_i)   : number of dimensions
+!- (I) nw_nv(f_i)   : number of variables
+!- (I) nw_na(f_i)   : number of global attributes
+!- (I) nw_un(f_i)   : ID of the first unlimited dimension
+!- (L) lw_hm(f_i)   : for mode handling (.TRUE. define, .FALSE. data)
+!- (I) nw_di(:,f_i) : dimension IDs in the file "f_i"
+!- (I) nw_dl(:,f_i) : dimension lengths in the file "f_i"
+!- (I) nw_ai(:,f_i) : dimension Ids for the axis in the file "f_i"
+!---------------------------------------------------------------------
+  INTEGER,PARAMETER :: &
+ &  nb_fi_mx=flio_max_files, &
+ &  nb_fd_mx=flio_max_dims, &
+ &  nb_vd_mx=flio_max_var_dims
+  INTEGER,PARAMETER :: nb_ax_mx=4
+!-
+  INTEGER,PARAMETER :: k_lon=1, k_lat=2, k_lev=3, k_tim=4
+!-
+  INTEGER,DIMENSION(nb_fi_mx),SAVE :: &
+ &  nw_id=-1,nw_nd,nw_nv,nw_na,nw_un
+  LOGICAL,DIMENSION(nb_fi_mx),SAVE :: lw_hm
+  INTEGER,DIMENSION(nb_fd_mx,nb_fi_mx),SAVE :: nw_di=-1,nw_dl=-1
+  INTEGER,DIMENSION(nb_ax_mx,nb_fi_mx),SAVE :: nw_ai=-1
+!-
+! Maximum number of simultaneously defined domains
+  INTEGER,PARAMETER :: dom_max_nb=64
+!-
+! Maximum number of distributed dimensions for each domain
+  INTEGER,PARAMETER :: dom_max_dims=5
+!-
+! Default domain identifier
+  INTEGER,SAVE :: id_def_dom=FLIO_DOM_NONE
+!-
+! Supported domain definition names
+  INTEGER,PARAMETER :: n_dns=3, l_dns=7
+  CHARACTER(LEN=l_dns),DIMENSION(n_dns),SAVE :: &
+ &  c_dns=(/ "box    ","apple  ","orange "/)
+!-
+! DOMAINS related variables
+  INTEGER,DIMENSION(1:dom_max_nb),SAVE :: &
+ &  d_d_n=-1, d_n_t=0, d_n_c=0
+  INTEGER,DIMENSION(1:dom_max_dims,1:dom_max_nb),SAVE :: &
+ &  d_d_i, d_s_g, d_s_l, d_p_f, d_p_l, d_h_s, d_h_e
+  CHARACTER(LEN=l_dns),DIMENSION(1:dom_max_nb),SAVE :: c_d_t
+!-
+!===
+CONTAINS
+!===
+!-
+!---------------------------------------------------------------------
+!- Public procedures
+!---------------------------------------------------------------------
+!-
+!===
+SUBROUTINE fliocrfd (f_n,f_d_n,f_d_l,f_i,id_dom,mode,c_f_n)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*),INTENT(IN) :: f_n
+  CHARACTER(LEN=*),DIMENSION(:),INTENT(IN) :: f_d_n
+  INTEGER,DIMENSION(:),INTENT(IN) :: f_d_l
+  INTEGER,INTENT(OUT) :: f_i
+  INTEGER,OPTIONAL,INTENT(IN) :: id_dom
+  CHARACTER(LEN=*),OPTIONAL,INTENT(IN) :: mode
+  CHARACTER(LEN=*),OPTIONAL,INTENT(OUT) :: c_f_n
+!-
+  INTEGER :: i_rc,f_e,idid,ii,m_c,n_u
+  CHARACTER(LEN=NF90_MAX_NAME) :: f_nw
+  INTEGER,PARAMETER :: l_string=80,l_word=10
+  CHARACTER(LEN=l_string) :: c_string
+  CHARACTER(LEN=l_word)   :: c_word
+  LOGICAL :: l_ok
+  INTEGER,PARAMETER :: k_replace=1
+  INTEGER,PARAMETER :: k_32=1,k_64=2
+!- !? : Code to be activated for NETCDF4
+!?  INTEGER,PARAMETER :: k_netcdf4=1,k_classic=1
+  INTEGER,PARAMETER :: n_opt=4
+  INTEGER,DIMENSION(n_opt) :: i_opt
+!-
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "->fliocrfd - file name : ",TRIM(f_n)
+  ENDIF
+!-
+! Search for a free local identifier
+  f_i = flio_rid()
+  IF (f_i < 0) THEN
+    CALL ipslerr (3,'fliocrfd', &
+ &   'Too many files.','Please increase nb_fi_mx', &
+ &   'in module fliocom.f90.')
+  ENDIF
+!-
+! Update the name of the file
+  f_nw = f_n
+  CALL flio_dom_file (f_nw,id_dom)
+!-
+! Check the dimensions
+  IF (SIZE(f_d_l) /= SIZE(f_d_n)) THEN
+    CALL ipslerr (3,'fliocrfd', &
+ &   'The number of names is not equal to the number of lengths', &
+ &   'for the dimensions of the file',TRIM(f_nw))
+  ENDIF
+  IF (SIZE(f_d_l) > nb_fd_mx) THEN
+    CALL ipslerr (3,'fliocrfd', &
+ &   'Too many dimensions','to create the file',TRIM(f_nw))
+  ENDIF
+!-
+! Check the mode
+!-
+  i_opt(:)=-1
+!-
+  IF (PRESENT(mode)) THEN
+!---
+    IF (LEN_TRIM(mode) > l_string) THEN
+      CALL ipslerr (3,'fliocrfd', &
+ &     '"mode" argument','too long','to be treated')
+    ENDIF
+    c_string = mode(:)
+    CALL strlowercase (c_string)
+!---
+    DO
+      CALL str_xfw  (c_string,c_word,l_ok)
+      IF (l_ok) THEN
+!- !? : Code to be activated for NETCDF4
+        SELECT CASE (TRIM(c_word))
+        CASE('replace')
+          IF (i_opt(1) > 0) THEN
+            CALL ipslerr (3,'fliocrfd', &
+ &           'Replace option','already','defined')
+          ELSE
+            i_opt(1) = k_replace
+          ENDIF
+!?      CASE('netcdf4')
+!?        IF (i_opt(2) > 0) THEN
+!?          CALL ipslerr (3,'fliocrfd', &
+!? &         'Netcdf4 format','already','defined')
+!?        ELSE
+!?          i_opt(2) = k_netcdf4
+!?        ENDIF
+        CASE('32')
+          IF (i_opt(3) > 0) THEN
+            CALL ipslerr (3,'fliocrfd', &
+ &           'Offset format','already','defined')
+          ELSE
+            i_opt(3) = k_32
+          ENDIF
+        CASE('64')
+          IF (i_opt(3) > 0) THEN
+            CALL ipslerr (3,'fliocrfd', &
+ &           'Offset format','already','defined')
+          ELSE
+            i_opt(3) = k_64
+          ENDIF
+!?      CASE('CLASSIC')
+!?        IF (i_opt(4) > 0) THEN
+!?          CALL ipslerr (3,'fliocrfd', &
+!? &         'Netcdf4 classic format','already','defined')
+!?        ELSE
+!?          i_opt(4) = k_classic
+!?        ENDIF
+        CASE DEFAULT
+          CALL ipslerr (3,'fliocrfd', &
+ &         'Option '//TRIM(c_word),'not','supported')
+        END SELECT
+      ELSE
+        EXIT
+      ENDIF
+    ENDDO
+  ENDIF
+!-
+  IF (i_opt(1) == k_replace) THEN
+    m_c = NF90_CLOBBER
+  ELSE
+    m_c = NF90_NOCLOBBER
+  ENDIF
+!-
+!- Code to be replaced by the following for NETCDF4
+!?  IF (i_opt(2) == k_netcdf4) THEN
+!?    m_c = IOR(m_c,NF90_NETCDF4)
+!?    IF (i_opt(3) > 0) THEN
+!?      CALL ipslerr (3,'fliocrfd', &
+!? &     'Netcdf4 format','and offset option','are not compatible')
+!?    ELSE IF (i_opt(4) == k_classic) THEN
+!?      m_c = IOR(m_c,NF90_CLASSIC_MODEL)
+!?    ENDIF
+!?   LSE IF (i_opt(4) > 0) THEN
+!?    CALL ipslerr (3,'fliocrfd', &
+!? &   'Classic option','is reserved','for the Netcdf4 format')
+!?  ELSE
+    IF (i_opt(3) /= k_32) THEN
+      m_c = IOR(m_c,NF90_64BIT_OFFSET)
+    ENDIF
+!?  ENDIF
+!-
+! Create file (and enter the definition mode)
+  i_rc = NF90_CREATE(f_nw,m_c,f_e)
+  lw_hm(f_i) = .TRUE.
+  IF (i_rc /= NF90_NOERR) THEN
+    CALL ipslerr (3,'fliocrfd', &
+ &   'Could not create file :',TRIM(f_nw), &
+ &   TRIM(NF90_STRERROR(i_rc))//' (Netcdf)')
+  ENDIF
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) '  fliocrfd, external model file-id : ',f_e
+  ENDIF
+!-
+! Create dimensions
+  n_u = 0
+  DO ii=1,SIZE(f_d_l)
+    IF (f_d_l(ii) == -1) THEN
+      IF (n_u == 0) THEN
+        i_rc = NF90_DEF_DIM(f_e,TRIM(f_d_n(ii)),NF90_UNLIMITED,idid)
+        n_u = n_u+1
+      ELSE
+        CALL ipslerr (3,'fliocrfd', &
+ &       'Can not handle more than one unlimited dimension', &
+ &       'for file :',TRIM(f_nw))
+      ENDIF
+    ELSE IF (f_d_l(ii) > 0) THEN
+      i_rc = NF90_DEF_DIM(f_e,TRIM(f_d_n(ii)),f_d_l(ii),idid)
+    ENDIF
+    IF ( ((f_d_l(ii) == -1).OR.(f_d_l(ii) > 0)) &
+ &      .AND.(i_rc /= NF90_NOERR) ) THEN
+      CALL ipslerr (3,'fliocrfd', &
+ &     'One dimension can not be defined', &
+ &     'for the file :',TRIM(f_nw))
+    ENDIF
+  ENDDO
+!-
+! Define "Conventions" global attribute
+  i_rc = NF90_PUT_ATT(f_e,NF90_GLOBAL,'Conventions',"CF-1.1")
+!-
+! Add the DOMAIN attributes if needed
+  CALL flio_dom_att (f_e,id_dom)
+!-
+! Keep the file information
+  nw_id(f_i) = f_e
+  CALL flio_inf (f_e, &
+ &  nb_dims=nw_nd(f_i),id_unlm=nw_un(f_i),nb_atts=nw_na(f_i), &
+ &  nn_idm=nw_di(:,f_i),nn_ldm=nw_dl(:,f_i),nn_aid=nw_ai(:,f_i))
+!-
+! Return the created file name if needed
+  IF (PRESENT(c_f_n)) THEN
+    IF (LEN(c_f_n) >= LEN_TRIM(f_nw)) THEN
+      c_f_n = TRIM(f_nw)
+    ELSE
+      CALL ipslerr (3,'fliocrfd', &
+ &     'the length of "c_f_n" is not sufficient to receive', &
+ &     'the name of the created file :',TRIM(f_nw))
+    ENDIF
+  ENDIF
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) '<-fliocrfd'
+  ENDIF
+!----------------------
+END SUBROUTINE fliocrfd
+!===
+SUBROUTINE fliopstc &
+ & (f_i,x_axis,x_axis_2d,y_axis,y_axis_2d,z_axis, &
+ &      t_axis,t_init,t_step,t_calendar)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  REAL,DIMENSION(:),OPTIONAL,INTENT(IN)    :: x_axis,y_axis
+  REAL,DIMENSION(:,:),OPTIONAL,INTENT(IN)  :: x_axis_2d,y_axis_2d
+  REAL,DIMENSION(:),OPTIONAL,INTENT(IN)    :: z_axis
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: t_axis
+  CHARACTER(LEN=*),OPTIONAL,INTENT(IN)     :: t_calendar
+  REAL,OPTIONAL,INTENT(IN)                 :: t_init,t_step
+!-
+  INTEGER :: i_rc,f_e
+  INTEGER :: lonid,latid,levid,timeid
+  INTEGER :: j_yy,j_mo,j_dd,j_hh,j_mn,j_ss
+  REAL    :: dt,r_ss,v_min,v_max
+  INTEGER :: k,k_1,k_2
+  LOGICAL :: l_tmp
+  CHARACTER(LEN=20) :: c_tmp1
+  CHARACTER(LEN=40) :: c_tmp2
+  CHARACTER(LEN=80) :: c_tmp3
+!-
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "->fliopstc"
+  ENDIF
+!-
+! Retrieve the external file index
+  CALL flio_qvid ('fliopstc',f_i,f_e)
+!-
+! Validate the coherence of the arguments
+!-
+  IF (    (PRESENT(x_axis).AND.PRESENT(x_axis_2d)) &
+ &    .OR.(PRESENT(y_axis).AND.PRESENT(y_axis_2d)) ) THEN
+    CALL ipslerr (3,'fliopstc', &
+ &    'The [x/y]_axis arguments', &
+ &    'are not coherent :',&
+ &    'can not handle two [x/y]_axis')
+  ENDIF
+!-
+  IF (    PRESENT(x_axis).OR.PRESENT(x_axis_2d) &
+ &    .OR.PRESENT(y_axis).OR.PRESENT(y_axis_2d) ) THEN
+    k_1=nw_ai(k_lon,f_i); k_2=nw_ai(k_lat,f_i);
+  ENDIF
+!-
+! Define the longitude axis
+!-
+  IF (PRESENT(x_axis).OR.PRESENT(x_axis_2d)) THEN
+!---
+    IF (l_dbg) THEN
+      WRITE(ipslout,*) '  fliopstc : Define the Longitude axis'
+    ENDIF
+!---
+    IF (PRESENT(x_axis)) THEN
+      IF (SIZE(x_axis) /= nw_dl(k_1,f_i)) THEN
+        CALL ipslerr (3,'fliopstc', &
+ &       'Invalid x_axis dimension :', &
+ &       'not equal to the dimension', &
+ &       'defined at the creation of the file')
+      ENDIF
+    ELSE
+      IF (    (SIZE(x_axis_2d,DIM=1) /= nw_dl(k_1,f_i)) &
+ &        .OR.(SIZE(x_axis_2d,DIM=2) /= nw_dl(k_2,f_i)) ) THEN
+        CALL ipslerr (3,'fliopstc', &
+ &       'Invalid x_axis_2d dimensions :', &
+ &       'not equal to the dimensions', &
+ &       'defined at the creation of the file')
+      ENDIF
+    ENDIF
+!---
+    CALL flio_hdm (f_i,f_e,.TRUE.)
+    IF (PRESENT(x_axis)) THEN
+      i_rc = NF90_DEF_VAR(f_e,"lon",NF90_REAL4, &
+ &                        nw_di(k_1,f_i),lonid)
+      v_min = MINVAL(x_axis)
+      v_max = MAXVAL(x_axis)
+    ELSE
+      i_rc = NF90_DEF_VAR(f_e,"nav_lon",NF90_REAL4, &
+ &             nw_di((/k_1,k_2/),f_i),lonid)
+      v_min = MINVAL(x_axis_2d)
+      v_max = MAXVAL(x_axis_2d)
+    ENDIF
+    i_rc = NF90_PUT_ATT(f_e,lonid,"axis","X")
+    i_rc = NF90_PUT_ATT(f_e,lonid,'standard_name',"longitude")
+    i_rc = NF90_PUT_ATT(f_e,lonid,'units',"degrees_east")
+    i_rc = NF90_PUT_ATT(f_e,lonid,'valid_min',REAL(v_min,KIND=4))
+    i_rc = NF90_PUT_ATT(f_e,lonid,'valid_max',REAL(v_max,KIND=4))
+  ENDIF
+!-
+! Define the Latitude axis
+!-
+  IF (PRESENT(y_axis).OR.PRESENT(y_axis_2d)) THEN
+!---
+    IF (l_dbg) THEN
+      WRITE(ipslout,*) '  fliopstc : Define the Latitude axis'
+    ENDIF
+!---
+    IF (PRESENT(y_axis)) THEN
+      IF (SIZE(y_axis) /= nw_dl(k_2,f_i)) THEN
+        CALL ipslerr (3,'fliopstc', &
+ &       'Invalid y_axis dimension :', &
+ &       'not equal to the dimension', &
+ &       'defined at the creation of the file')
+      ENDIF
+    ELSE
+      IF (    (SIZE(y_axis_2d,DIM=1) /= nw_dl(k_1,f_i)) &
+ &        .OR.(SIZE(y_axis_2d,DIM=2) /= nw_dl(k_2,f_i)) ) THEN
+        CALL ipslerr (3,'fliopstc', &
+ &       'Invalid y_axis_2d dimensions :', &
+ &       'not equal to the dimensions', &
+ &       'defined at the creation of the file')
+      ENDIF
+    ENDIF
+!---
+    CALL flio_hdm (f_i,f_e,.TRUE.)
+    IF (PRESENT(y_axis)) THEN
+      i_rc = NF90_DEF_VAR(f_e,"lat",NF90_REAL4, &
+ &                        nw_di(k_2,f_i),latid)
+      v_min = MINVAL(y_axis)
+      v_max = MAXVAL(y_axis)
+    ELSE
+      i_rc = NF90_DEF_VAR(f_e,"nav_lat",NF90_REAL4, &
+ &             nw_di((/k_1,k_2/),f_i),latid)
+      v_min = MINVAL(y_axis_2d)
+      v_max = MAXVAL(y_axis_2d)
+    ENDIF
+    i_rc = NF90_PUT_ATT(f_e,latid,"axis","Y")
+    i_rc = NF90_PUT_ATT(f_e,latid,'standard_name',"latitude")
+    i_rc = NF90_PUT_ATT(f_e,latid,'units',"degrees_north")
+    i_rc = NF90_PUT_ATT(f_e,latid,'valid_min',REAL(v_min,KIND=4))
+    i_rc = NF90_PUT_ATT(f_e,latid,'valid_max',REAL(v_max,KIND=4))
+  ENDIF
+!-
+! Define the Vertical axis
+!-
+  IF (PRESENT(z_axis)) THEN
+!---
+    IF (l_dbg) THEN
+      WRITE(ipslout,*) '  fliopstc : Define the Vertical axis'
+    ENDIF
+!---
+    k_1=nw_ai(k_lev,f_i);
+!---
+    IF (SIZE(z_axis) /= nw_dl(k_1,f_i)) THEN
+      CALL ipslerr (3,'fliopstc', &
+ &     'Invalid z_axis dimension :', &
+ &     'not equal to the dimension', &
+ &     'defined at the creation of the file')
+    ENDIF
+!---
+    v_min = MINVAL(z_axis)
+    v_max = MAXVAL(z_axis)
+!---
+    CALL flio_hdm (f_i,f_e,.TRUE.)
+    i_rc = NF90_DEF_VAR(f_e,'lev',NF90_REAL4, &
+ &                      nw_di(k_1,f_i),levid)
+    i_rc = NF90_PUT_ATT(f_e,levid,"axis","Z")
+    i_rc = NF90_PUT_ATT(f_e,levid,'standard_name','model_level_number')
+    i_rc = NF90_PUT_ATT(f_e,levid,'units','sigma_level')
+    i_rc = NF90_PUT_ATT(f_e,levid,'long_name','Sigma Levels')
+    i_rc = NF90_PUT_ATT(f_e,levid,'valid_min',REAL(v_min,KIND=4))
+    i_rc = NF90_PUT_ATT(f_e,levid,'valid_max',REAL(v_max,KIND=4))
+  ENDIF
+!-
+! Define the Time axis
+!-
+  IF (PRESENT(t_axis).AND.PRESENT(t_init).AND.PRESENT(t_step)) THEN
+!---
+    IF (l_dbg) THEN
+      WRITE(ipslout,*) '  fliopstc : Define the Time axis'
+    ENDIF
+!---
+    k_1=nw_ai(k_tim,f_i);
+!---
+    IF (     (nw_dl(k_1,f_i) /= 0) &
+ &      .AND.(SIZE(t_axis) /= nw_dl(k_1,f_i)) ) THEN
+      CALL ipslerr (3,'fliopstc', &
+ &     'Invalid t_axis dimension :', &
+ &     'not equal to the dimension', &
+ &     'defined at the creation of the file')
+    ENDIF
+!-- Retrieve the calendar date
+    CALL lock_calendar (old_status=l_tmp)
+    IF (PRESENT(t_calendar)) THEN
+      CALL ioget_calendar (c_tmp1)
+      CALL lock_calendar (new_status=.FALSE.)
+      CALL ioconf_calendar (TRIM(t_calendar))
+    ENDIF
+    CALL ju2ymds (t_init,j_yy,j_mo,j_dd,r_ss)
+    IF (PRESENT(t_calendar)) THEN
+      CALL lock_calendar (new_status=.FALSE.)
+      CALL ioconf_calendar (TRIM(c_tmp1))
+    ENDIF
+    CALL lock_calendar (new_status=l_tmp)
+!--
+    k=NINT(r_ss)
+    j_hh=k/3600
+    k=k-3600*j_hh
+    j_mn=k/60
+    j_ss=k-60*j_mn
+!-- Calculate the step unit
+    IF      (ABS(t_step) >= 604800.) THEN
+      dt = t_step/604800.
+      c_tmp2 = 'weeks'
+    ELSE IF (ABS(t_step) >= 86400.) THEN
+      dt = t_step/86400.
+      c_tmp2 = 'days'
+    ELSE IF (ABS(t_step) >=  3600.) THEN
+      dt = t_step/3600.
+      c_tmp2 = 'hours'
+    ELSE IF (ABS(t_step) >=    60.) THEN
+      dt = t_step/60.
+      c_tmp2 = 'minutes'
+    ELSE
+      dt = t_step
+      c_tmp2 = 'seconds'
+    ENDIF
+!---
+    c_tmp1 = ''
+    IF (ABS(dt-NINT(dt)) <= ABS(10.*EPSILON(dt))) THEN
+      IF (NINT(dt) /= 1) THEN
+        WRITE (UNIT=c_tmp1,FMT='(I15)') NINT(dt)
+      ENDIF
+    ELSE
+      IF (dt < 1.) THEN
+       WRITE (UNIT=c_tmp1,FMT='(F8.5)') dt
+      ELSE
+       WRITE (UNIT=c_tmp1,FMT='(F17.5)') dt
+      ENDIF
+      DO k=LEN_TRIM(c_tmp1),1,-1
+        IF (c_tmp1(k:k) /= '0') THEN
+          EXIT
+        ELSE
+          c_tmp1(k:k) = ' '
+        ENDIF
+      ENDDO
+    ENDIF
+    c_tmp2 = TRIM(c_tmp1)//' '//TRIM(c_tmp2)
+    WRITE (UNIT=c_tmp3, &
+ &   FMT='(A,I4.4,"-",I2.2,"-",I2.2," ",I2.2,":",I2.2,":",I2.2)') &
+ &    TRIM(ADJUSTL(c_tmp2))//' since ',j_yy,j_mo,j_dd,j_hh,j_mn,j_ss
+!---
+    CALL flio_hdm (f_i,f_e,.TRUE.)
+    i_rc = NF90_DEF_VAR(f_e,'time',NF90_REAL4, &
+ &                      nw_di(k_1,f_i),timeid)
+    i_rc = NF90_PUT_ATT(f_e,timeid,"axis",'T')
+    i_rc = NF90_PUT_ATT(f_e,timeid,'standard_name','time')
+    i_rc = NF90_PUT_ATT(f_e,timeid,'long_name','time steps')
+    IF (PRESENT(t_calendar)) THEN
+      i_rc = NF90_PUT_ATT(f_e,timeid,'calendar',TRIM(t_calendar))
+    ENDIF
+    i_rc = NF90_PUT_ATT(f_e,timeid,'units',TRIM(c_tmp3))
+  ELSE IF (PRESENT(t_axis).OR.PRESENT(t_init).OR.PRESENT(t_step)) THEN
+    CALL ipslerr (3,'fliopstc', &
+ &   'For time axis and coordinates', &
+ &   'arguments t_axis AND t_init AND t_step', &
+ &   'must be PRESENT')
+  ENDIF
+!-
+! Ensuring data mode
+!-
+  CALL flio_hdm (f_i,f_e,.FALSE.)
+!-
+! Create the longitude axis
+!-
+  IF (PRESENT(x_axis).OR.PRESENT(x_axis_2d)) THEN
+    IF (l_dbg) THEN
+      WRITE(ipslout,*) '  fliopstc : Create the Longitude axis'
+    ENDIF
+    IF (PRESENT(x_axis)) THEN
+      i_rc = NF90_PUT_VAR(f_e,lonid,x_axis(:))
+    ELSE
+      i_rc = NF90_PUT_VAR(f_e,lonid,x_axis_2d(:,:))
+    ENDIF
+  ENDIF
+!-
+! Create the Latitude axis
+!-
+  IF (PRESENT(y_axis).OR.PRESENT(y_axis_2d)) THEN
+    IF (l_dbg) THEN
+      WRITE(ipslout,*) '  fliopstc : Create the Latitude axis'
+    ENDIF
+    IF (PRESENT(y_axis)) THEN
+      i_rc = NF90_PUT_VAR(f_e,latid,y_axis(:))
+    ELSE
+      i_rc = NF90_PUT_VAR(f_e,latid,y_axis_2d(:,:))
+    ENDIF
+  ENDIF
+!-
+! Create the Vertical axis
+!-
+  IF (PRESENT(z_axis)) THEN
+    IF (l_dbg) THEN
+      WRITE(ipslout,*) '  fliopstc : Create the Vertical axis'
+    ENDIF
+    i_rc = NF90_PUT_VAR(f_e,levid,z_axis(:))
+  ENDIF
+!-
+! Create the Time axis
+!-
+  IF (PRESENT(t_axis)) THEN
+    IF (l_dbg) THEN
+      WRITE(ipslout,*) '  fliopstc : Create the Time axis'
+    ENDIF
+    i_rc = NF90_PUT_VAR(f_e,timeid,REAL(t_axis(:)))
+  ENDIF
+!-
+! Keep all this information
+!-
+  CALL flio_inf (f_e,nb_vars=nw_nv(f_i),nb_atts=nw_na(f_i))
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "<-fliopstc"
+  ENDIF
+!----------------------
+END SUBROUTINE fliopstc
+!===
+SUBROUTINE fliodv_r0d &
+ & (f_i,v_n,v_t, &
+ &  axis,standard_name,long_name,units,valid_min,valid_max,fillvalue)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER,OPTIONAL,INTENT(IN) :: v_t
+  CHARACTER(LEN=*),OPTIONAL,INTENT(IN) :: &
+ & axis,standard_name,long_name,units
+  REAL,OPTIONAL,INTENT(IN) :: valid_min,valid_max,fillvalue
+!---------------------------------------------------------------------
+  CALL flio_udv &
+ &  (f_i,0,v_n,(/0/),v_t, &
+ &   axis,standard_name,long_name,units,valid_min,valid_max,fillvalue)
+!------------------------
+END SUBROUTINE fliodv_r0d
+!===
+SUBROUTINE fliodv_rnd &
+ & (f_i,v_n,v_d,v_t, &
+ &  axis,standard_name,long_name,units,valid_min,valid_max,fillvalue)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER,DIMENSION(:),INTENT(IN) :: v_d
+  INTEGER,OPTIONAL,INTENT(IN) :: v_t
+  CHARACTER(LEN=*),OPTIONAL,INTENT(IN) :: &
+ & axis,standard_name,long_name,units
+  REAL,OPTIONAL,INTENT(IN) :: valid_min,valid_max,fillvalue
+!---------------------------------------------------------------------
+  CALL flio_udv &
+ &  (f_i,SIZE(v_d),v_n,v_d,v_t, &
+ &   axis,standard_name,long_name,units,valid_min,valid_max,fillvalue)
+!------------------------
+END SUBROUTINE fliodv_rnd
+!===
+SUBROUTINE flio_udv &
+ & (f_i,n_d,v_n,v_d,v_t, &
+ &  axis,standard_name,long_name,units,valid_min,valid_max,fillvalue)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i,n_d
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER,DIMENSION(:),INTENT(IN) :: v_d
+  INTEGER,OPTIONAL,INTENT(IN) :: v_t
+  CHARACTER(LEN=*),OPTIONAL,INTENT(IN) :: &
+ & axis,standard_name,long_name,units
+  REAL,OPTIONAL,INTENT(IN) :: valid_min,valid_max,fillvalue
+!-
+  INTEGER :: f_e,m_k,i_v,i_rc,ii,idd
+  INTEGER,DIMENSION(nb_vd_mx) :: a_i
+!-
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "->fliodefv ",TRIM(v_n)," ",n_d,"D"
+  ENDIF
+!-
+! Retrieve the external file index
+  CALL flio_qvid ('fliodefv',f_i,f_e)
+!-
+  IF (n_d > 0) THEN
+    IF (n_d > nb_vd_mx) THEN
+      CALL ipslerr (3,'fliodefv', &
+ &     'Too many dimensions', &
+ &     'required for the variable',TRIM(v_n))
+    ENDIF
+  ENDIF
+!-
+  DO ii=1,n_d
+    IF ( (v_d(ii) >= 1).AND.(v_d(ii) <= nb_fd_mx) ) THEN
+      idd = nw_di(v_d(ii),f_i)
+      IF (idd > 0) THEN
+        a_i(ii) = idd
+      ELSE
+        CALL ipslerr (3,'fliodefv', &
+ &       'Invalid dimension identifier','(not defined)',' ')
+      ENDIF
+    ELSE
+      CALL ipslerr (3,'fliodefv', &
+ &     'Invalid dimension identifier','(not supported)',' ')
+    ENDIF
+  ENDDO
+!-
+  i_rc = NF90_INQ_VARID(f_e,v_n,i_v)
+  IF (i_rc /= NF90_NOERR) THEN
+    CALL flio_hdm (f_i,f_e,.TRUE.)
+!---
+    IF (PRESENT(v_t)) THEN
+      SELECT CASE (v_t)
+      CASE(flio_i)
+        IF (i_std == i_8) THEN
+!-------- I8 not yet supported by NETCDF
+!-------- m_k = flio_i8
+          m_k = flio_i4
+        ELSE
+          m_k = flio_i4
+        ENDIF
+      CASE(flio_r)
+        IF (r_std == r_8) THEN
+          m_k = flio_r8
+        ELSE
+          m_k = flio_r4
+        ENDIF
+      CASE(flio_c,flio_i1,flio_i2,flio_i4,flio_r4,flio_r8)
+        m_k = v_t
+      CASE DEFAULT
+        CALL ipslerr (3,'fliodefv', &
+ &        'Variable '//TRIM(v_n),'External type','not supported')
+      END SELECT
+    ELSE IF (r_std == r_8) THEN
+      m_k = flio_r8
+    ELSE
+      m_k = flio_r4
+    ENDIF
+!---
+    IF (n_d > 0) THEN
+      i_rc = NF90_DEF_VAR(f_e,v_n,m_k,a_i(1:n_d),i_v)
+    ELSE
+      i_rc = NF90_DEF_VAR(f_e,v_n,m_k,i_v)
+    ENDIF
+    IF (i_rc /= NF90_NOERR) THEN
+      CALL ipslerr (3,'fliodefv', &
+ &      'Variable '//TRIM(v_n)//' not defined','Error :', &
+ &      TRIM(NF90_STRERROR(i_rc)))
+    ENDIF
+    nw_nv(f_i) = nw_nv(f_i)+1
+!---
+    IF (PRESENT(axis)) THEN
+      i_rc = NF90_PUT_ATT(f_e,i_v,'axis',TRIM(axis))
+    ENDIF
+    IF (PRESENT(standard_name)) THEN
+      i_rc = NF90_PUT_ATT(f_e,i_v,'standard_name',TRIM(standard_name))
+    ENDIF
+    IF (PRESENT(long_name)) THEN
+      i_rc = NF90_PUT_ATT(f_e,i_v,'long_name',TRIM(long_name))
+    ENDIF
+    IF (PRESENT(units)) THEN
+      i_rc = NF90_PUT_ATT(f_e,i_v,'units',TRIM(units))
+    ENDIF
+    IF (PRESENT(valid_min)) THEN
+      SELECT CASE (m_k)
+      CASE(flio_i1,flio_i2)
+        i_rc = NF90_PUT_ATT(f_e,i_v,'valid_min',NINT(valid_min,KIND=i_2))
+      CASE(flio_i4)
+        i_rc = NF90_PUT_ATT(f_e,i_v,'valid_min',NINT(valid_min,KIND=i_4))
+      CASE(flio_r4)
+        i_rc = NF90_PUT_ATT(f_e,i_v,'valid_min',REAL(valid_min,KIND=r_4))
+      CASE(flio_r8)
+        i_rc = NF90_PUT_ATT(f_e,i_v,'valid_min',REAL(valid_min,KIND=r_8))
+      CASE DEFAULT
+        CALL ipslerr (2,'fliodefv', &
+   &      'Variable '//TRIM(v_n),'attribute valid_min', &
+   &      'not supported for this external type')
+      END SELECT
+    ENDIF
+    IF (PRESENT(valid_max)) THEN
+      SELECT CASE (m_k)
+      CASE(flio_i1,flio_i2)
+        i_rc = NF90_PUT_ATT(f_e,i_v,'valid_max',NINT(valid_max,KIND=i_2))
+      CASE(flio_i4)
+        i_rc = NF90_PUT_ATT(f_e,i_v,'valid_max',NINT(valid_max,KIND=i_4))
+      CASE(flio_r4)
+        i_rc = NF90_PUT_ATT(f_e,i_v,'valid_max',REAL(valid_max,KIND=r_4))
+      CASE(flio_r8)
+        i_rc = NF90_PUT_ATT(f_e,i_v,'valid_max',REAL(valid_max,KIND=r_8))
+      CASE DEFAULT
+        CALL ipslerr (2,'fliodefv', &
+   &      'Variable '//TRIM(v_n),'attribute valid_max', &
+   &      'not supported for this external type')
+      END SELECT
+    ENDIF
+    IF (PRESENT(fillvalue)) THEN
+      SELECT CASE (m_k)
+      CASE(flio_i1,flio_i2)
+        i_rc = NF90_PUT_ATT(f_e,i_v,'_FillValue',NINT(fillvalue,KIND=i_2))
+      CASE(flio_i4)
+        i_rc = NF90_PUT_ATT(f_e,i_v,'_FillValue',NINT(fillvalue,KIND=i_4))
+      CASE(flio_r4)
+        i_rc = NF90_PUT_ATT(f_e,i_v,'_FillValue',REAL(fillvalue,KIND=r_4))
+      CASE(flio_r8)
+        i_rc = NF90_PUT_ATT(f_e,i_v,'_FillValue',REAL(fillvalue,KIND=r_8))
+      CASE DEFAULT
+        CALL ipslerr (2,'fliodefv', &
+   &      'Variable '//TRIM(v_n),'attribute fillvalue', &
+   &      'not supported for this external type')
+      END SELECT
+    ENDIF
+!---
+  ELSE
+    CALL ipslerr (3,'fliodefv','Variable',TRIM(v_n),'already exist')
+  ENDIF
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "<-fliodefv"
+  ENDIF
+!----------------------
+END SUBROUTINE flio_udv
+!===
+SUBROUTINE fliopv_i40 (f_i,v_n,v_v,start)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER(KIND=i_4),INTENT(IN) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start
+!---------------------------------------------------------------------
+  CALL flio_upv (f_i,v_n,i_40=v_v,start=start)
+!------------------------
+END SUBROUTINE fliopv_i40
+!===
+SUBROUTINE fliopv_i41 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER(KIND=i_4),DIMENSION(:),INTENT(IN) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_upv (f_i,v_n,i_41=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliopv_i41
+!===
+SUBROUTINE fliopv_i42 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER(KIND=i_4),DIMENSION(:,:),INTENT(IN) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_upv (f_i,v_n,i_42=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliopv_i42
+!===
+SUBROUTINE fliopv_i43 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER(KIND=i_4),DIMENSION(:,:,:),INTENT(IN) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_upv (f_i,v_n,i_43=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliopv_i43
+!===
+SUBROUTINE fliopv_i44 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER(KIND=i_4),DIMENSION(:,:,:,:),INTENT(IN) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_upv (f_i,v_n,i_44=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliopv_i44
+!===
+SUBROUTINE fliopv_i45 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER(KIND=i_4),DIMENSION(:,:,:,:,:),INTENT(IN) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_upv (f_i,v_n,i_45=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliopv_i45
+!===
+SUBROUTINE fliopv_i20 (f_i,v_n,v_v,start)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER(KIND=i_2),INTENT(IN) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start
+!---------------------------------------------------------------------
+  CALL flio_upv (f_i,v_n,i_20=v_v,start=start)
+!------------------------
+END SUBROUTINE fliopv_i20
+!===
+SUBROUTINE fliopv_i21 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER(KIND=i_2),DIMENSION(:),INTENT(IN) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_upv (f_i,v_n,i_21=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliopv_i21
+!===
+SUBROUTINE fliopv_i22 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER(KIND=i_2),DIMENSION(:,:),INTENT(IN) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_upv (f_i,v_n,i_22=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliopv_i22
+!===
+SUBROUTINE fliopv_i23 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER(KIND=i_2),DIMENSION(:,:,:),INTENT(IN) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_upv (f_i,v_n,i_23=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliopv_i23
+!===
+SUBROUTINE fliopv_i24 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER(KIND=i_2),DIMENSION(:,:,:,:),INTENT(IN) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_upv (f_i,v_n,i_24=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliopv_i24
+!===
+SUBROUTINE fliopv_i25 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER(KIND=i_2),DIMENSION(:,:,:,:,:),INTENT(IN) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_upv (f_i,v_n,i_25=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliopv_i25
+!===
+!?INTEGERS of KIND 1 are not supported on all computers
+!?SUBROUTINE fliopv_i10 (f_i,v_n,v_v,start)
+!?!---------------------------------------------------------------------
+!?  IMPLICIT NONE
+!?!-
+!?  INTEGER,INTENT(IN) :: f_i
+!?  CHARACTER(LEN=*),INTENT(IN) :: v_n
+!?  INTEGER(KIND=i_1),INTENT(IN) :: v_v
+!?  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start
+!?!---------------------------------------------------------------------
+!?  CALL flio_upv (f_i,v_n,i_10=v_v,start=start)
+!?!------------------------
+!?END SUBROUTINE fliopv_i10
+!?!===
+!?SUBROUTINE fliopv_i11 (f_i,v_n,v_v,start,count)
+!?!---------------------------------------------------------------------
+!?  IMPLICIT NONE
+!?!-
+!?  INTEGER,INTENT(IN) :: f_i
+!?  CHARACTER(LEN=*),INTENT(IN) :: v_n
+!?  INTEGER(KIND=i_1),DIMENSION(:),INTENT(IN) :: v_v
+!?  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!?!---------------------------------------------------------------------
+!?  CALL flio_upv (f_i,v_n,i_11=v_v,start=start,count=count)
+!?!------------------------
+!?END SUBROUTINE fliopv_i11
+!?!===
+!?SUBROUTINE fliopv_i12 (f_i,v_n,v_v,start,count)
+!?!---------------------------------------------------------------------
+!?  IMPLICIT NONE
+!?!-
+!?  INTEGER,INTENT(IN) :: f_i
+!?  CHARACTER(LEN=*),INTENT(IN) :: v_n
+!?  INTEGER(KIND=i_1),DIMENSION(:,:),INTENT(IN) :: v_v
+!?  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!?!---------------------------------------------------------------------
+!?  CALL flio_upv (f_i,v_n,i_12=v_v,start=start,count=count)
+!?!------------------------
+!?END SUBROUTINE fliopv_i12
+!?!===
+!?SUBROUTINE fliopv_i13 (f_i,v_n,v_v,start,count)
+!?!---------------------------------------------------------------------
+!?  IMPLICIT NONE
+!?!-
+!?  INTEGER,INTENT(IN) :: f_i
+!?  CHARACTER(LEN=*),INTENT(IN) :: v_n
+!?  INTEGER(KIND=i_1),DIMENSION(:,:,:),INTENT(IN) :: v_v
+!?  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!?!---------------------------------------------------------------------
+!?  CALL flio_upv (f_i,v_n,i_13=v_v,start=start,count=count)
+!?!------------------------
+!?END SUBROUTINE fliopv_i13
+!?!===
+!?SUBROUTINE fliopv_i14 (f_i,v_n,v_v,start,count)
+!?!---------------------------------------------------------------------
+!?  IMPLICIT NONE
+!?!-
+!?  INTEGER,INTENT(IN) :: f_i
+!?  CHARACTER(LEN=*),INTENT(IN) :: v_n
+!?  INTEGER(KIND=i_1),DIMENSION(:,:,:,:),INTENT(IN) :: v_v
+!?  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!?!---------------------------------------------------------------------
+!?  CALL flio_upv (f_i,v_n,i_14=v_v,start=start,count=count)
+!?!------------------------
+!?END SUBROUTINE fliopv_i14
+!?!===
+!?SUBROUTINE fliopv_i15 (f_i,v_n,v_v,start,count)
+!?!---------------------------------------------------------------------
+!?  IMPLICIT NONE
+!?!-
+!?  INTEGER,INTENT(IN) :: f_i
+!?  CHARACTER(LEN=*),INTENT(IN) :: v_n
+!?  INTEGER(KIND=i_1),DIMENSION(:,:,:,:,:),INTENT(IN) :: v_v
+!?  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!?!---------------------------------------------------------------------
+!?  CALL flio_upv (f_i,v_n,i_15=v_v,start=start,count=count)
+!?!------------------------
+!?END SUBROUTINE fliopv_i15
+!===
+SUBROUTINE fliopv_r40 (f_i,v_n,v_v,start)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  REAL(KIND=r_4),INTENT(IN) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start
+!---------------------------------------------------------------------
+  CALL flio_upv (f_i,v_n,r_40=v_v,start=start)
+!------------------------
+END SUBROUTINE fliopv_r40
+!===
+SUBROUTINE fliopv_r41 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  REAL(KIND=r_4),DIMENSION(:),INTENT(IN) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_upv (f_i,v_n,r_41=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliopv_r41
+!===
+SUBROUTINE fliopv_r42 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  REAL(KIND=r_4),DIMENSION(:,:),INTENT(IN) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_upv (f_i,v_n,r_42=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliopv_r42
+!===
+SUBROUTINE fliopv_r43 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  REAL(KIND=r_4),DIMENSION(:,:,:),INTENT(IN) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_upv (f_i,v_n,r_43=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliopv_r43
+!===
+SUBROUTINE fliopv_r44 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  REAL(KIND=r_4),DIMENSION(:,:,:,:),INTENT(IN) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_upv (f_i,v_n,r_44=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliopv_r44
+!===
+SUBROUTINE fliopv_r45 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  REAL(KIND=r_4),DIMENSION(:,:,:,:,:),INTENT(IN) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_upv (f_i,v_n,r_45=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliopv_r45
+!===
+SUBROUTINE fliopv_r80 (f_i,v_n,v_v,start)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  REAL(KIND=r_8),INTENT(IN) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start
+!---------------------------------------------------------------------
+  CALL flio_upv (f_i,v_n,r_80=v_v,start=start)
+!------------------------
+END SUBROUTINE fliopv_r80
+!===
+SUBROUTINE fliopv_r81 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  REAL(KIND=r_8),DIMENSION(:),INTENT(IN) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_upv (f_i,v_n,r_81=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliopv_r81
+!===
+SUBROUTINE fliopv_r82 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  REAL(KIND=r_8),DIMENSION(:,:),INTENT(IN) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_upv (f_i,v_n,r_82=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliopv_r82
+!===
+SUBROUTINE fliopv_r83 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  REAL(KIND=r_8),DIMENSION(:,:,:),INTENT(IN) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_upv (f_i,v_n,r_83=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliopv_r83
+!===
+SUBROUTINE fliopv_r84 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  REAL(KIND=r_8),DIMENSION(:,:,:,:),INTENT(IN) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_upv (f_i,v_n,r_84=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliopv_r84
+!===
+SUBROUTINE fliopv_r85 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  REAL(KIND=r_8),DIMENSION(:,:,:,:,:),INTENT(IN) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_upv (f_i,v_n,r_85=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliopv_r85
+!===
+SUBROUTINE flio_upv &
+ & (f_i,v_n, &
+ &  i_40,i_41,i_42,i_43,i_44,i_45, &
+ &  i_20,i_21,i_22,i_23,i_24,i_25, &
+!? &  i_10,i_11,i_12,i_13,i_14,i_15, &
+ &  r_40,r_41,r_42,r_43,r_44,r_45, &
+ &  r_80,r_81,r_82,r_83,r_84,r_85, &
+ &  start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER(KIND=i_4),INTENT(IN),OPTIONAL :: i_40
+  INTEGER(KIND=i_4),DIMENSION(:),INTENT(IN),OPTIONAL :: i_41
+  INTEGER(KIND=i_4),DIMENSION(:,:),INTENT(IN),OPTIONAL :: i_42
+  INTEGER(KIND=i_4),DIMENSION(:,:,:),INTENT(IN),OPTIONAL :: i_43
+  INTEGER(KIND=i_4),DIMENSION(:,:,:,:),INTENT(IN),OPTIONAL :: i_44
+  INTEGER(KIND=i_4),DIMENSION(:,:,:,:,:),INTENT(IN),OPTIONAL :: i_45
+  INTEGER(KIND=i_2),INTENT(IN),OPTIONAL :: i_20
+  INTEGER(KIND=i_2),DIMENSION(:),INTENT(IN),OPTIONAL :: i_21
+  INTEGER(KIND=i_2),DIMENSION(:,:),INTENT(IN),OPTIONAL :: i_22
+  INTEGER(KIND=i_2),DIMENSION(:,:,:),INTENT(IN),OPTIONAL :: i_23
+  INTEGER(KIND=i_2),DIMENSION(:,:,:,:),INTENT(IN),OPTIONAL :: i_24
+  INTEGER(KIND=i_2),DIMENSION(:,:,:,:,:),INTENT(IN),OPTIONAL :: i_25
+!?INTEGERS of KIND 1 are not supported on all computers
+!?INTEGER(KIND=i_1),INTENT(IN),OPTIONAL :: i_10
+!?INTEGER(KIND=i_1),DIMENSION(:),INTENT(IN),OPTIONAL :: i_11
+!?INTEGER(KIND=i_1),DIMENSION(:,:),INTENT(IN),OPTIONAL :: i_12
+!?INTEGER(KIND=i_1),DIMENSION(:,:,:),INTENT(IN),OPTIONAL :: i_13
+!?INTEGER(KIND=i_1),DIMENSION(:,:,:,:),INTENT(IN),OPTIONAL :: i_14
+!?INTEGER(KIND=i_1),DIMENSION(:,:,:,:,:),INTENT(IN),OPTIONAL :: i_15
+  REAL(KIND=r_4),INTENT(IN),OPTIONAL :: r_40
+  REAL(KIND=r_4),DIMENSION(:),INTENT(IN),OPTIONAL :: r_41
+  REAL(KIND=r_4),DIMENSION(:,:),INTENT(IN),OPTIONAL :: r_42
+  REAL(KIND=r_4),DIMENSION(:,:,:),INTENT(IN),OPTIONAL :: r_43
+  REAL(KIND=r_4),DIMENSION(:,:,:,:),INTENT(IN),OPTIONAL :: r_44
+  REAL(KIND=r_4),DIMENSION(:,:,:,:,:),INTENT(IN),OPTIONAL :: r_45
+  REAL(KIND=r_8),INTENT(IN),OPTIONAL :: r_80
+  REAL(KIND=r_8),DIMENSION(:),INTENT(IN),OPTIONAL :: r_81
+  REAL(KIND=r_8),DIMENSION(:,:),INTENT(IN),OPTIONAL :: r_82
+  REAL(KIND=r_8),DIMENSION(:,:,:),INTENT(IN),OPTIONAL :: r_83
+  REAL(KIND=r_8),DIMENSION(:,:,:,:),INTENT(IN),OPTIONAL :: r_84
+  REAL(KIND=r_8),DIMENSION(:,:,:,:,:),INTENT(IN),OPTIONAL :: r_85
+  INTEGER,DIMENSION(:),INTENT(IN),OPTIONAL :: start,count
+!-
+  INTEGER :: f_e,i_v,i_rc
+  CHARACTER(LEN=5) :: cvr_d
+!-
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (l_dbg) THEN
+    IF      (PRESENT(i_40)) THEN; cvr_d = "I1 0D";
+    ELSE IF (PRESENT(i_41)) THEN; cvr_d = "I1 1D";
+    ELSE IF (PRESENT(i_42)) THEN; cvr_d = "I1 2D";
+    ELSE IF (PRESENT(i_43)) THEN; cvr_d = "I1 3D";
+    ELSE IF (PRESENT(i_44)) THEN; cvr_d = "I1 4D";
+    ELSE IF (PRESENT(i_45)) THEN; cvr_d = "I1 5D";
+    ELSE IF (PRESENT(i_20)) THEN; cvr_d = "I2 0D";
+    ELSE IF (PRESENT(i_21)) THEN; cvr_d = "I2 1D";
+    ELSE IF (PRESENT(i_22)) THEN; cvr_d = "I2 2D";
+    ELSE IF (PRESENT(i_23)) THEN; cvr_d = "I2 3D";
+    ELSE IF (PRESENT(i_24)) THEN; cvr_d = "I2 4D";
+    ELSE IF (PRESENT(i_25)) THEN; cvr_d = "I2 5D";
+!?  ELSE IF (PRESENT(i_10)) THEN; cvr_d = "I4 0D";
+!?  ELSE IF (PRESENT(i_11)) THEN; cvr_d = "I4 1D";
+!?  ELSE IF (PRESENT(i_12)) THEN; cvr_d = "I4 2D";
+!?  ELSE IF (PRESENT(i_13)) THEN; cvr_d = "I4 3D";
+!?  ELSE IF (PRESENT(i_14)) THEN; cvr_d = "I4 4D";
+!?  ELSE IF (PRESENT(i_15)) THEN; cvr_d = "I4 5D";
+    ELSE IF (PRESENT(r_40)) THEN; cvr_d = "R4 0D";
+    ELSE IF (PRESENT(r_41)) THEN; cvr_d = "R4 1D";
+    ELSE IF (PRESENT(r_42)) THEN; cvr_d = "R4 2D";
+    ELSE IF (PRESENT(r_43)) THEN; cvr_d = "R4 3D";
+    ELSE IF (PRESENT(r_44)) THEN; cvr_d = "R4 4D";
+    ELSE IF (PRESENT(r_45)) THEN; cvr_d = "R4 5D";
+    ELSE IF (PRESENT(r_80)) THEN; cvr_d = "R8 0D";
+    ELSE IF (PRESENT(r_81)) THEN; cvr_d = "R8 1D";
+    ELSE IF (PRESENT(r_82)) THEN; cvr_d = "R8 2D";
+    ELSE IF (PRESENT(r_83)) THEN; cvr_d = "R8 3D";
+    ELSE IF (PRESENT(r_84)) THEN; cvr_d = "R8 4D";
+    ELSE IF (PRESENT(r_85)) THEN; cvr_d = "R8 5D";
+    ENDIF
+    WRITE(ipslout,*) "->flioputv ",TRIM(v_n)," ",TRIM(cvr_d)
+  ENDIF
+!-
+! Retrieve the external file index
+  CALL flio_qvid ('flioputv',f_i,f_e)
+!-
+! Ensuring data mode
+!-
+  CALL flio_hdm (f_i,f_e,.FALSE.)
+!-
+  i_rc = NF90_INQ_VARID(f_e,v_n,i_v)
+  IF (i_rc == NF90_NOERR) THEN
+    IF      (PRESENT(i_40)) THEN
+      i_rc = NF90_PUT_VAR(f_e,i_v,i_40,start=start)
+    ELSE IF (PRESENT(i_41)) THEN
+      i_rc = NF90_PUT_VAR(f_e,i_v,i_41,start=start,count=count)
+    ELSE IF (PRESENT(i_42)) THEN
+      i_rc = NF90_PUT_VAR(f_e,i_v,i_42,start=start,count=count)
+    ELSE IF (PRESENT(i_43)) THEN
+      i_rc = NF90_PUT_VAR(f_e,i_v,i_43,start=start,count=count)
+    ELSE IF (PRESENT(i_44)) THEN
+      i_rc = NF90_PUT_VAR(f_e,i_v,i_44,start=start,count=count)
+    ELSE IF (PRESENT(i_45)) THEN
+      i_rc = NF90_PUT_VAR(f_e,i_v,i_45,start=start,count=count)
+    ELSE IF (PRESENT(i_20)) THEN
+      i_rc = NF90_PUT_VAR(f_e,i_v,i_20,start=start)
+    ELSE IF (PRESENT(i_21)) THEN
+      i_rc = NF90_PUT_VAR(f_e,i_v,i_21,start=start,count=count)
+    ELSE IF (PRESENT(i_22)) THEN
+      i_rc = NF90_PUT_VAR(f_e,i_v,i_22,start=start,count=count)
+    ELSE IF (PRESENT(i_23)) THEN
+      i_rc = NF90_PUT_VAR(f_e,i_v,i_23,start=start,count=count)
+    ELSE IF (PRESENT(i_24)) THEN
+      i_rc = NF90_PUT_VAR(f_e,i_v,i_24,start=start,count=count)
+    ELSE IF (PRESENT(i_25)) THEN
+      i_rc = NF90_PUT_VAR(f_e,i_v,i_25,start=start,count=count)
+!?  ELSE IF (PRESENT(i_10)) THEN
+!?    i_rc = NF90_PUT_VAR(f_e,i_v,i_10,start=start)
+!?  ELSE IF (PRESENT(i_11)) THEN
+!?    i_rc = NF90_PUT_VAR(f_e,i_v,i_11,start=start,count=count)
+!?  ELSE IF (PRESENT(i_12)) THEN
+!?    i_rc = NF90_PUT_VAR(f_e,i_v,i_12,start=start,count=count)
+!?  ELSE IF (PRESENT(i_13)) THEN
+!?    i_rc = NF90_PUT_VAR(f_e,i_v,i_13,start=start,count=count)
+!?  ELSE IF (PRESENT(i_14)) THEN
+!?    i_rc = NF90_PUT_VAR(f_e,i_v,i_14,start=start,count=count)
+!?  ELSE IF (PRESENT(i_15)) THEN
+!?    i_rc = NF90_PUT_VAR(f_e,i_v,i_15,start=start,count=count)
+    ELSE IF (PRESENT(r_40)) THEN
+      i_rc = NF90_PUT_VAR(f_e,i_v,r_40,start=start)
+    ELSE IF (PRESENT(r_41)) THEN
+      i_rc = NF90_PUT_VAR(f_e,i_v,r_41,start=start,count=count)
+    ELSE IF (PRESENT(r_42)) THEN
+      i_rc = NF90_PUT_VAR(f_e,i_v,r_42,start=start,count=count)
+    ELSE IF (PRESENT(r_43)) THEN
+      i_rc = NF90_PUT_VAR(f_e,i_v,r_43,start=start,count=count)
+    ELSE IF (PRESENT(r_44)) THEN
+      i_rc = NF90_PUT_VAR(f_e,i_v,r_44,start=start,count=count)
+    ELSE IF (PRESENT(r_45)) THEN
+      i_rc = NF90_PUT_VAR(f_e,i_v,r_45,start=start,count=count)
+    ELSE IF (PRESENT(r_80)) THEN
+      i_rc = NF90_PUT_VAR(f_e,i_v,r_80,start=start)
+    ELSE IF (PRESENT(r_81)) THEN
+      i_rc = NF90_PUT_VAR(f_e,i_v,r_81,start=start,count=count)
+    ELSE IF (PRESENT(r_82)) THEN
+      i_rc = NF90_PUT_VAR(f_e,i_v,r_82,start=start,count=count)
+    ELSE IF (PRESENT(r_83)) THEN
+      i_rc = NF90_PUT_VAR(f_e,i_v,r_83,start=start,count=count)
+    ELSE IF (PRESENT(r_84)) THEN
+      i_rc = NF90_PUT_VAR(f_e,i_v,r_84,start=start,count=count)
+    ELSE IF (PRESENT(r_85)) THEN
+      i_rc = NF90_PUT_VAR(f_e,i_v,r_85,start=start,count=count)
+    ENDIF
+    IF (i_rc /= NF90_NOERR) THEN
+      CALL ipslerr (3,'flioputv', &
+ &      'Variable '//TRIM(v_n)//' not put','Error :', &
+ &      TRIM(NF90_STRERROR(i_rc)))
+    ENDIF
+  ELSE
+    CALL ipslerr (3,'flioputv','Variable',TRIM(v_n),'not defined')
+  ENDIF
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "<-flioputv"
+  ENDIF
+!----------------------
+END SUBROUTINE flio_upv
+!===
+SUBROUTINE fliopa_r4_0d (f_i,v_n,a_n,a_v)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n,a_n
+  REAL(KIND=4),INTENT(IN) :: a_v
+!---------------------------------------------------------------------
+  CALL flio_upa (f_i,1,v_n,a_n,avr4=(/a_v/))
+!--------------------------
+END SUBROUTINE fliopa_r4_0d
+!===
+SUBROUTINE fliopa_r4_1d (f_i,v_n,a_n,a_v)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n,a_n
+  REAL(KIND=4),DIMENSION(:),INTENT(IN) :: a_v
+!---------------------------------------------------------------------
+  CALL flio_upa (f_i,SIZE(a_v),v_n,a_n,avr4=a_v)
+!--------------------------
+END SUBROUTINE fliopa_r4_1d
+!===
+SUBROUTINE fliopa_r8_0d (f_i,v_n,a_n,a_v)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n,a_n
+  REAL(KIND=8),INTENT(IN) :: a_v
+!---------------------------------------------------------------------
+  CALL flio_upa (f_i,1,v_n,a_n,avr8=(/a_v/))
+!--------------------------
+END SUBROUTINE fliopa_r8_0d
+!===
+SUBROUTINE fliopa_r8_1d (f_i,v_n,a_n,a_v)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n,a_n
+  REAL(KIND=8),DIMENSION(:),INTENT(IN) :: a_v
+!---------------------------------------------------------------------
+  CALL flio_upa (f_i,SIZE(a_v),v_n,a_n,avr8=a_v)
+!--------------------------
+END SUBROUTINE fliopa_r8_1d
+!===
+SUBROUTINE fliopa_i4_0d (f_i,v_n,a_n,a_v)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n,a_n
+  INTEGER(KIND=4),INTENT(IN) :: a_v
+!---------------------------------------------------------------------
+  CALL flio_upa (f_i,1,v_n,a_n,avi4=(/a_v/))
+!--------------------------
+END SUBROUTINE fliopa_i4_0d
+!===
+SUBROUTINE fliopa_i4_1d (f_i,v_n,a_n,a_v)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n,a_n
+  INTEGER(KIND=4),DIMENSION(:),INTENT(IN) :: a_v
+!---------------------------------------------------------------------
+  CALL flio_upa (f_i,SIZE(a_v),v_n,a_n,avi4=a_v)
+!--------------------------
+END SUBROUTINE fliopa_i4_1d
+!===
+SUBROUTINE fliopa_tx_0d (f_i,v_n,a_n,a_v)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n,a_n
+  CHARACTER(LEN=*),INTENT(IN) :: a_v
+!---------------------------------------------------------------------
+  CALL flio_upa (f_i,1,v_n,a_n,avtx=a_v)
+!--------------------------
+END SUBROUTINE fliopa_tx_0d
+!===
+SUBROUTINE flio_upa (f_i,l_a,v_n,a_n,avr4,avr8,avi4,avtx)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i,l_a
+  CHARACTER(LEN=*),INTENT(IN) :: v_n,a_n
+  REAL(KIND=4),DIMENSION(:),OPTIONAL,INTENT(IN) :: avr4
+  REAL(KIND=8),DIMENSION(:),OPTIONAL,INTENT(IN) :: avr8
+  INTEGER(KIND=4),DIMENSION(:),OPTIONAL,INTENT(IN) :: avi4
+  CHARACTER(LEN=*),OPTIONAL,INTENT(IN) :: avtx
+!-
+  INTEGER :: f_e,i_v,i_a,i_rc
+!-
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "->flioputa ",TRIM(v_n)," ",TRIM(a_n)
+  ENDIF
+!-
+! Retrieve the external file index
+  CALL flio_qvid ('flioputa',f_i,f_e)
+!-
+  IF (TRIM(v_n) == '?') THEN
+    i_v = NF90_GLOBAL
+  ELSE
+    i_rc = NF90_INQ_VARID(f_e,v_n,i_v)
+    IF (i_rc /= NF90_NOERR) THEN
+      CALL ipslerr (3,'flioputa', &
+       'Variable :',TRIM(v_n),'not found')
+    ENDIF
+  ENDIF
+!-
+  i_rc = NF90_INQUIRE_ATTRIBUTE(f_e,i_v,a_n,attnum=i_a)
+  IF ( (i_v == NF90_GLOBAL).AND.(i_rc /= NF90_NOERR) ) THEN
+    nw_na(f_i) = nw_na(f_i)+1
+  ENDIF
+  CALL flio_hdm (f_i,f_e,.TRUE.)
+  IF      (PRESENT(avr4)) THEN
+    i_rc = NF90_PUT_ATT(f_e,i_v,a_n,avr4(1:l_a))
+  ELSE IF (PRESENT(avr8)) THEN
+    i_rc = NF90_PUT_ATT(f_e,i_v,a_n,avr8(1:l_a))
+  ELSE IF (PRESENT(avi4)) THEN
+    i_rc = NF90_PUT_ATT(f_e,i_v,a_n,avi4(1:l_a))
+  ELSE IF (PRESENT(avtx)) THEN
+    i_rc = NF90_PUT_ATT(f_e,i_v,a_n,TRIM(avtx))
+  ENDIF
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "<-flioputa"
+  ENDIF
+!----------------------
+END SUBROUTINE flio_upa
+!===
+SUBROUTINE flioopfd (f_n,f_i,mode,nb_dim,nb_var,nb_gat)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*),INTENT(IN) :: f_n
+  INTEGER,INTENT(OUT) :: f_i
+  CHARACTER(LEN=*),INTENT(IN),OPTIONAL :: mode
+  INTEGER,OPTIONAL,INTENT(OUT) :: nb_dim,nb_var,nb_gat
+!-
+  INTEGER :: i_rc,f_e,m_c
+!-
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) '->flioopfd, file name : ',TRIM(f_n)
+  ENDIF
+!-
+! Search for a free local identifier
+!-
+  f_i = flio_rid()
+  IF (f_i < 0) THEN
+    CALL ipslerr (3,'flioopfd', &
+      'Too many files.','Please increase nb_fi_mx', &
+      'in module fliocom.f90.')
+  ENDIF
+!-
+! Check the mode
+!-
+  IF (PRESENT(mode)) THEN
+    IF (TRIM(mode) == "WRITE") THEN
+      m_c = NF90_WRITE
+    ELSE
+      m_c = NF90_NOWRITE
+    ENDIF
+  ELSE
+    m_c = NF90_NOWRITE
+  ENDIF
+!-
+! Open the file.
+!-
+  i_rc = NF90_OPEN(TRIM(f_n),m_c,f_e)
+  IF (i_rc /= NF90_NOERR) THEN
+    CALL ipslerr (3,'flioopfd', &
+ &   'Could not open file :',TRIM(f_n), &
+ &   TRIM(NF90_STRERROR(i_rc))//' (Netcdf)')
+  ENDIF
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) '  flioopfd, model file-id : ',f_e
+  ENDIF
+!-
+! Retrieve and keep information about the file
+!-
+  nw_id(f_i) = f_e
+  lw_hm(f_i) = .FALSE.
+  CALL flio_inf (f_e, &
+ &  nb_dims=nw_nd(f_i),nb_vars=nw_nv(f_i), &
+ &  nb_atts=nw_na(f_i),id_unlm=nw_un(f_i), &
+ &  nn_idm=nw_di(:,f_i),nn_ldm=nw_dl(:,f_i),nn_aid=nw_ai(:,f_i))
+!-
+! Return information to the user
+!-
+  IF (PRESENT(nb_dim)) THEN
+    nb_dim = nw_nd(f_i)
+  ENDIF
+  IF (PRESENT(nb_var)) THEN
+    nb_var = nw_nv(f_i)
+  ENDIF
+  IF (PRESENT(nb_gat)) THEN
+    nb_gat = nw_na(f_i)
+  ENDIF
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,'("   flioopfd - dimensions :",/,(5(1X,I10),:))') &
+ &    nw_dl(:,f_i)
+    WRITE(ipslout,*) "<-flioopfd"
+  ENDIF
+!----------------------
+END SUBROUTINE flioopfd
+!===
+SUBROUTINE flioinqf &
+ & (f_i,nb_dim,nb_var,nb_gat,id_uld,id_dim,ln_dim)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  INTEGER,OPTIONAL,INTENT(OUT) :: nb_dim,nb_var,nb_gat,id_uld
+  INTEGER,OPTIONAL,INTENT(OUT),DIMENSION(:) :: id_dim,ln_dim
+!-
+  INTEGER :: lll
+!-
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "->flioinqf"
+  ENDIF
+!-
+  IF ( (f_i < 1).OR.(f_i > nb_fi_mx) ) THEN
+    CALL ipslerr (2,'flioinqf', &
+ &   'Invalid file identifier',' ',' ')
+  ELSE IF (nw_id(f_i) <= 0) THEN
+    CALL ipslerr (2,'flioinqf', &
+ &   'Unable to inquire about the file :','probably','not opened')
+  ELSE
+    IF (PRESENT(nb_dim)) THEN
+      nb_dim = nw_nd(f_i)
+    ENDIF
+    IF (PRESENT(nb_var)) THEN
+      nb_var = nw_nv(f_i)
+    ENDIF
+    IF (PRESENT(nb_gat)) THEN
+      nb_gat = nw_na(f_i)
+    ENDIF
+    IF (PRESENT(id_uld)) THEN
+      id_uld = nw_un(f_i)
+    ENDIF
+    IF (PRESENT(id_dim)) THEN
+      lll = SIZE(id_dim)
+      IF (lll < nw_nd(f_i)) THEN
+        CALL ipslerr (2,'flioinqf', &
+ &       'Only the first identifiers', &
+ &       'of the dimensions','will be returned')
+      ENDIF
+      lll=MIN(SIZE(id_dim),nw_nd(f_i))
+      id_dim(1:lll) = nw_di(1:lll,f_i)
+    ENDIF
+    IF (PRESENT(ln_dim)) THEN
+      lll = SIZE(ln_dim)
+      IF (lll < nw_nd(f_i)) THEN
+        CALL ipslerr (2,'flioinqf', &
+ &       'Only the first lengths', &
+ &       'of the dimensions','will be returned')
+      ENDIF
+      lll=MIN(SIZE(ln_dim),nw_nd(f_i))
+      ln_dim(1:lll) = nw_dl(1:lll,f_i)
+    ENDIF
+  ENDIF
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "<-flioinqf"
+  ENDIF
+!----------------------
+END SUBROUTINE flioinqf
+!===
+SUBROUTINE flioinqn &
+ & (f_i,cn_dim,cn_var,cn_gat,cn_uld, &
+ &  id_start,id_count,iv_start,iv_count,ia_start,ia_count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),DIMENSION(:),OPTIONAL,INTENT(OUT) :: &
+ & cn_dim,cn_var,cn_gat
+  CHARACTER(LEN=*),OPTIONAL,INTENT(OUT) :: &
+ & cn_uld
+  INTEGER,OPTIONAL,INTENT(IN) :: &
+ & id_start,id_count,iv_start,iv_count,ia_start,ia_count
+!-
+  INTEGER :: f_e,i_s,i_w,iws,iwc,i_rc
+  LOGICAL :: l_ok
+!-
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "->flioinqn"
+  ENDIF
+!-
+! Retrieve the external file index
+  CALL flio_qvid ('flioinqn',f_i,f_e)
+!-
+  IF (PRESENT(cn_dim)) THEN
+    l_ok = .TRUE.
+    i_s = SIZE(cn_dim)
+    DO i_w=1,i_s
+      cn_dim(i_w)(:) = '?'
+    ENDDO
+    IF (PRESENT(id_start)) THEN
+      iws = id_start
+    ELSE
+      iws = 1
+    ENDIF
+    IF (PRESENT(id_count)) THEN
+      iwc = id_count
+    ELSE
+      iwc = nw_nd(f_i)
+    ENDIF
+    IF (iws > nw_nd(f_i)) THEN
+      l_ok = .FALSE.
+      CALL ipslerr (2,'flioinqn', &
+ &     'The start index of requested dimensions', &
+ &     'is greater than the number of dimensions', &
+ &     'in the file')
+    ELSE IF (iws < 1) THEN
+      l_ok = .FALSE.
+      CALL ipslerr (2,'flioinqn', &
+ &     'The start index of requested dimensions', &
+ &     'is invalid', &
+ &     '( < 1 )')
+    ENDIF
+    IF ((iws+iwc-1) > nw_nd(f_i)) THEN
+      CALL ipslerr (2,'flioinqn', &
+ &     'The number of requested dimensions', &
+ &     'is greater than the number of dimensions', &
+ &     'in the file')
+    ENDIF
+    IF (iwc > i_s) THEN
+      CALL ipslerr (2,'flioinqn', &
+ &     'The number of dimensions to retrieve', &
+ &     'is greater than the size of the array,', &
+ &     'only the first dimensions of the file will be returned')
+    ELSE IF (iwc < 1) THEN
+      l_ok = .FALSE.
+      CALL ipslerr (2,'flioinqn', &
+ &     'The number of requested dimensions', &
+ &     'is invalid', &
+ &     '( < 1 )')
+    ENDIF
+    IF (l_ok) THEN
+      DO i_w=1,MIN(iwc,i_s,nw_nd(f_i)-iws+1)
+        i_rc = NF90_INQUIRE_DIMENSION(f_e,i_w+iws-1,name=cn_dim(i_w))
+      ENDDO
+    ENDIF
+  ENDIF
+!-
+  IF (PRESENT(cn_var)) THEN
+    l_ok = .TRUE.
+    i_s = SIZE(cn_var)
+    DO i_w=1,i_s
+      cn_var(i_w)(:) = '?'
+    ENDDO
+    IF (PRESENT(iv_start)) THEN
+      iws = iv_start
+    ELSE
+      iws = 1
+    ENDIF
+    IF (PRESENT(iv_count)) THEN
+      iwc = iv_count
+    ELSE
+      iwc = nw_nv(f_i)
+    ENDIF
+    IF (iws > nw_nv(f_i)) THEN
+      l_ok = .FALSE.
+      CALL ipslerr (2,'flioinqn', &
+ &     'The start index of requested variables', &
+ &     'is greater than the number of variables', &
+ &     'in the file')
+    ELSE IF (iws < 1) THEN
+      l_ok = .FALSE.
+      CALL ipslerr (2,'flioinqn', &
+ &     'The start index of requested variables', &
+ &     'is invalid', &
+ &     '( < 1 )')
+    ENDIF
+    IF ((iws+iwc-1) > nw_nv(f_i)) THEN
+      CALL ipslerr (2,'flioinqn', &
+ &     'The number of requested variables', &
+ &     'is greater than the number of variables', &
+ &     'in the file')
+    ENDIF
+    IF (iwc > i_s) THEN
+      CALL ipslerr (2,'flioinqn', &
+ &     'The number of variables to retrieve', &
+ &     'is greater than the size of the array,', &
+ &     'only the first variables of the file will be returned')
+    ELSE IF (iwc < 1) THEN
+      l_ok = .FALSE.
+      CALL ipslerr (2,'flioinqn', &
+ &     'The number of requested variables', &
+ &     'is invalid', &
+ &     '( < 1 )')
+    ENDIF
+    IF (l_ok) THEN
+      DO i_w=1,MIN(iwc,i_s,nw_nv(f_i)-iws+1)
+        i_rc = NF90_INQUIRE_VARIABLE(f_e,i_w+iws-1,name=cn_var(i_w))
+      ENDDO
+    ENDIF
+  ENDIF
+!-
+  IF (PRESENT(cn_gat)) THEN
+    l_ok = .TRUE.
+    i_s = SIZE(cn_gat)
+    DO i_w=1,i_s
+      cn_gat(i_w)(:) = '?'
+    ENDDO
+    IF (PRESENT(ia_start)) THEN
+      iws = ia_start
+    ELSE
+      iws = 1
+    ENDIF
+    IF (PRESENT(ia_count)) THEN
+      iwc = ia_count
+    ELSE
+      iwc = nw_na(f_i)
+    ENDIF
+    IF (iws > nw_na(f_i)) THEN
+      l_ok = .FALSE.
+      CALL ipslerr (2,'flioinqn', &
+ &     'The start index of requested global attributes', &
+ &     'is greater than the number of global attributes', &
+ &     'in the file')
+    ELSE IF (iws < 1) THEN
+      l_ok = .FALSE.
+      CALL ipslerr (2,'flioinqn', &
+ &     'The start index of requested global attributes', &
+ &     'is invalid', &
+ &     '( < 1 )')
+    ENDIF
+    IF ((iws+iwc-1) > nw_na(f_i)) THEN
+      CALL ipslerr (2,'flioinqn', &
+ &     'The number of requested global attributes', &
+ &     'is greater than the number of global attributes', &
+ &     'in the file')
+    ENDIF
+    IF (iwc > i_s) THEN
+      CALL ipslerr (2,'flioinqn', &
+ &     'The number of global attributes to retrieve', &
+ &     'is greater than the size of the array,', &
+ &     'only the first global attributes of the file will be returned')
+    ELSE IF (iwc < 1) THEN
+      l_ok = .FALSE.
+      CALL ipslerr (2,'flioinqn', &
+ &     'The number of requested global attributes', &
+ &     'is invalid', &
+ &     '( < 1 )')
+    ENDIF
+    IF (l_ok) THEN
+      DO i_w=1,MIN(iwc,i_s,nw_na(f_i)-iws+1)
+        i_rc = NF90_INQ_ATTNAME(f_e, &
+ &              NF90_GLOBAL,i_w+iws-1,name=cn_gat(i_w))
+      ENDDO
+    ENDIF
+  ENDIF
+!-
+  IF (PRESENT(cn_uld)) THEN
+    cn_uld = '?'
+    IF (nw_un(f_i) > 0) THEN
+      i_rc = NF90_INQUIRE_DIMENSION(f_e,nw_un(f_i),name=cn_uld)
+    ENDIF
+  ENDIF
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "<-flioinqn"
+  ENDIF
+!----------------------
+END SUBROUTINE flioinqn
+!===
+SUBROUTINE fliogstc &
+ & (f_i,x_axis,x_axis_2d,y_axis,y_axis_2d,z_axis, &
+ &      t_axis,t_init,t_step,t_calendar, &
+ &      x_start,x_count,y_start,y_count, &
+ &      z_start,z_count,t_start,t_count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  REAL,DIMENSION(:),OPTIONAL,INTENT(OUT)    :: x_axis,y_axis
+  REAL,DIMENSION(:,:),OPTIONAL,INTENT(OUT)  :: x_axis_2d,y_axis_2d
+  REAL,DIMENSION(:),OPTIONAL,INTENT(OUT)    :: z_axis
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(OUT) :: t_axis
+  REAL,OPTIONAL,INTENT(OUT)                 :: t_init,t_step
+  CHARACTER(LEN=*),OPTIONAL,INTENT(OUT)     :: t_calendar
+  INTEGER,OPTIONAL,INTENT(IN) :: &
+ &  x_start,x_count,y_start,y_count,z_start,z_count,t_start,t_count
+!-
+  INTEGER :: i_rc,f_e,i_v,it_t,nbdim,kv
+  INTEGER :: m_x,i_x,l_x,m_y,i_y,l_y,m_z,i_z,l_z,m_t,i_t,l_t
+  CHARACTER(LEN=NF90_MAX_NAME) :: name
+  CHARACTER(LEN=80) :: units
+  CHARACTER(LEN=20) :: c_tmp
+  CHARACTER(LEN=1) :: c_1
+  REAL    :: r_yy,r_mo,r_dd,r_ss,dtv,dtn
+  INTEGER :: j_yy,j_mo,j_dd,j_hh,j_mn,j_ss
+  LOGICAL :: l_ok,l_tmp
+!-
+  REAL,DIMENSION(:),ALLOCATABLE :: v_tmp
+!-
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "->fliogstc"
+  ENDIF
+!-
+! Retrieve the external file index
+  CALL flio_qvid ('fliogstc',f_i,f_e)
+!-
+! Validate the coherence of the arguments
+!-
+  IF (    (PRESENT(x_axis).AND.PRESENT(x_axis_2d)) &
+ &    .OR.(PRESENT(y_axis).AND.PRESENT(y_axis_2d)) ) THEN
+    CALL ipslerr (3,'fliogstc', &
+ &    'The [x/y]_axis arguments', &
+ &    'are not coherent :',&
+ &    'can not handle two [x/y]_axis')
+  ENDIF
+!-
+! Retrieve spatio-temporal dimensions
+!-
+  IF (nw_ai(k_lon,f_i) > 0) THEN
+    m_x = nw_dl(nw_ai(k_lon,f_i),f_i);
+  ELSE
+    m_x = -1;
+  ENDIF
+  IF (nw_ai(k_lat,f_i) > 0) THEN
+    m_y = nw_dl(nw_ai(k_lat,f_i),f_i);
+  ELSE
+    m_y = -1;
+  ENDIF
+  IF (nw_ai(k_lev,f_i) > 0) THEN
+    m_z = nw_dl(nw_ai(k_lev,f_i),f_i);
+  ELSE
+    m_z = -1;
+  ENDIF
+  IF (nw_ai(k_tim,f_i) > 0) THEN
+    m_t = nw_dl(nw_ai(k_tim,f_i),f_i);
+  ELSE
+    m_t = -1;
+  ENDIF
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,'("   fliogstc - dimensions :",/,(5(1X,I10),:))') &
+ &    m_x,m_y,m_z,m_t
+  ENDIF
+!-
+! Initialize the x-y indices
+!-
+  IF (    PRESENT(x_axis)    &
+ &    .OR.PRESENT(x_axis_2d) &
+ &    .OR.PRESENT(y_axis_2d) ) THEN
+    IF (PRESENT(x_start)) THEN
+      i_x = x_start
+    ELSE
+      i_x = 1
+    ENDIF
+    IF (PRESENT(x_count)) THEN
+      l_x = x_count
+    ELSE
+      l_x = m_x-i_x+1
+    ENDIF
+  ENDIF
+  IF (    PRESENT(y_axis)    &
+ &    .OR.PRESENT(y_axis_2d) &
+ &    .OR.PRESENT(x_axis_2d) ) THEN
+    IF (PRESENT(y_start)) THEN
+      i_y = y_start
+    ELSE
+      i_y = 1
+    ENDIF
+    IF (PRESENT(y_count)) THEN
+      l_y = y_count
+    ELSE
+      l_y = m_y-i_y+1
+    ENDIF
+  ENDIF
+  IF (PRESENT(x_axis)) THEN
+    IF (m_x <= 0) THEN
+      CALL ipslerr (3,'fliogstc', &
+ &      'Requested x_axis', &
+ &      'but the coordinate is not present','in the file')
+    ELSE IF ((i_x+l_x-1) > m_x) THEN
+      CALL ipslerr (3,'fliogstc', &
+ &      'The requested size for the x_axis', &
+ &      'is greater than the size of the coordinate','in the file')
+    ENDIF
+  ENDIF
+  IF (PRESENT(y_axis)) THEN
+    IF (m_y <= 0) THEN
+      CALL ipslerr (3,'fliogstc', &
+ &      'Requested y_axis', &
+ &      'but the coordinate is not present','in the file')
+    ELSE IF ((i_y+l_y-1) > m_y) THEN
+      CALL ipslerr (3,'fliogstc', &
+ &      'The requested size for the y_axis', &
+ &      'is greater than the size of the coordinate','in the file')
+    ENDIF
+  ENDIF
+  IF (PRESENT(x_axis_2d).OR.PRESENT(y_axis_2d) )THEN
+    IF ( (m_x <= 0).OR.(m_y <= 0) ) THEN
+      CALL ipslerr (3,'fliogstc', &
+ &      'Requested [x/y]_axis_2d', &
+ &      'but the coordinates are not iboth present','in the file')
+    ELSE IF ( ((i_x+l_x-1) > m_x).OR.((i_y+l_y-1) > m_y) ) THEN
+      CALL ipslerr (3,'fliogstc', &
+ &      'The requested size for the [x/y]_axis_2d', &
+ &      'is greater than the size of the coordinate','in the file')
+    ENDIF
+  ENDIF
+!-
+! Ensuring data mode
+!-
+  CALL flio_hdm (f_i,f_e,.FALSE.)
+!-
+! Extracting the x coordinate, if needed
+!-
+  IF (PRESENT(x_axis).OR.PRESENT(x_axis_2d)) THEN
+    CALL flio_qax (f_i,'x',i_v,nbdim)
+    IF (i_v > 0) THEN
+      IF      (nbdim == 1) THEN
+        IF (PRESENT(x_axis)) THEN
+          i_rc = NF90_GET_VAR(f_e,i_v,x_axis, &
+ &                 start=(/i_x/),count=(/l_x/))
+        ELSE
+          ALLOCATE(v_tmp(l_x))
+          i_rc = NF90_GET_VAR(f_e,i_v,v_tmp, &
+ &                 start=(/i_x/),count=(/l_x/))
+          DO kv=1,l_y
+            x_axis_2d(:,kv) = v_tmp(:)
+          ENDDO
+          DEALLOCATE(v_tmp)
+        ENDIF
+      ELSE IF (nbdim == 2) THEN
+        IF (PRESENT(x_axis)) THEN
+          l_ok = .TRUE.
+          IF (l_y > 1) THEN
+            ALLOCATE(v_tmp(l_y))
+            DO kv=i_x,i_x+l_x-1
+              i_rc = NF90_GET_VAR(f_e,i_v,v_tmp, &
+ &                     start=(/kv,i_y/),count=(/1,l_y/))
+              IF (ANY(v_tmp(2:l_y) /= v_tmp(1))) THEN
+                l_ok = .FALSE.
+                EXIT
+              ENDIF
+            ENDDO
+            DEALLOCATE(v_tmp)
+          ENDIF
+          IF (l_ok) THEN
+            i_rc = NF90_GET_VAR(f_e,i_v,x_axis, &
+ &                   start=(/i_x,i_y/),count=(/l_x,1/))
+          ELSE
+            CALL ipslerr (3,'fliogstc', &
+ &            'Requested 1D x_axis', &
+ &            'which have 2 not regular dimensions', &
+ &            'in the file')
+          ENDIF
+        ELSE
+          i_rc = NF90_GET_VAR(f_e,i_v,x_axis_2d, &
+ &                 start=(/i_x,i_y/),count=(/l_x,l_y/))
+        ENDIF
+      ELSE
+        CALL ipslerr (3,'fliogstc', &
+ &        'Can not handle x_axis', &
+ &        'that have more than 2 dimensions', &
+ &        'in the file')
+      ENDIF
+    ELSE
+      CALL ipslerr (3,'fliogstc','No x_axis found','in the file',' ')
+    ENDIF
+  ENDIF
+!-
+! Extracting the y coordinate, if needed
+!-
+  IF (PRESENT(y_axis).OR.PRESENT(y_axis_2d)) THEN
+    CALL flio_qax (f_i,'y',i_v,nbdim)
+    IF (i_v > 0) THEN
+      IF      (nbdim == 1) THEN
+        IF (PRESENT(y_axis)) THEN
+          i_rc = NF90_GET_VAR(f_e,i_v,y_axis, &
+ &                 start=(/i_y/),count=(/l_y/))
+        ELSE
+          ALLOCATE(v_tmp(l_y))
+          i_rc = NF90_GET_VAR(f_e,i_v,v_tmp, &
+ &                 start=(/i_y/),count=(/l_y/))
+          DO kv=1,l_x
+            y_axis_2d(kv,:) = v_tmp(:)
+          ENDDO
+          DEALLOCATE(v_tmp)
+        ENDIF
+      ELSE IF (nbdim == 2) THEN
+        IF (PRESENT(y_axis)) THEN
+          l_ok = .TRUE.
+          IF (l_x > 1) THEN
+            ALLOCATE(v_tmp(l_x))
+            DO kv=i_y,i_y+l_y-1
+              i_rc = NF90_GET_VAR(f_e,i_v,v_tmp, &
+ &                     start=(/i_x,kv/),count=(/l_x,1/))
+              IF (ANY(v_tmp(2:l_x) /= v_tmp(1))) THEN
+                l_ok = .FALSE.
+                EXIT
+              ENDIF
+            ENDDO
+            DEALLOCATE(v_tmp)
+          ENDIF
+          IF (l_ok) THEN
+            i_rc = NF90_GET_VAR(f_e,i_v,y_axis, &
+ &                   start=(/i_x,i_y/),count=(/1,l_y/))
+          ELSE
+            CALL ipslerr (3,'fliogstc', &
+ &            'Requested 1D y_axis', &
+ &            'which have 2 not regular dimensions', &
+ &            'in the file')
+          ENDIF
+        ELSE
+          i_rc = NF90_GET_VAR(f_e,i_v,y_axis_2d, &
+ &                 start=(/i_x,i_y/),count=(/l_x,l_y/))
+        ENDIF
+      ELSE
+        CALL ipslerr (3,'fliogstc', &
+ &        'Can not handle y axis', &
+ &        'that have more than 2 dimensions', &
+ &        'in the file')
+      ENDIF
+    ELSE
+      CALL ipslerr (3,'fliogstc','No y_axis found','in the file',' ')
+    ENDIF
+  ENDIF
+!-
+! Extracting the z coordinate, if needed
+!-
+  IF (PRESENT(z_axis)) THEN
+    IF (PRESENT(z_start)) THEN
+      i_z = z_start
+    ELSE
+      i_z = 1
+    ENDIF
+    IF (PRESENT(z_count)) THEN
+      l_z = z_count
+    ELSE
+      l_z = m_z-i_z+1
+    ENDIF
+    IF ((i_z+l_z-1) > m_z) THEN
+      CALL ipslerr (3,'fliogstc', &
+ &      'The requested size for the z axis', &
+ &      'is greater than the size of the coordinate',&
+ &      'in the file')
+    ENDIF
+    CALL flio_qax (f_i,'z',i_v,nbdim)
+    IF (i_v > 0) THEN
+      IF (nbdim == 1) THEN
+        i_rc = NF90_GET_VAR(f_e,i_v,z_axis, &
+ &               start=(/i_z/),count=(/l_z/))
+      ELSE
+        CALL ipslerr (3,'fliogstc', &
+ &        'Can not handle z_axis', &
+ &        'that have more than 1 dimension', &
+ &        'in the file')
+      ENDIF
+    ELSE
+      CALL ipslerr (3,'fliogstc','No z_axis found','in the file',' ')
+    ENDIF
+  ENDIF
+!-
+! Extracting the t coordinate, if needed
+!-
+  IF (PRESENT(t_axis).OR.PRESENT(t_init).OR.PRESENT(t_step)) THEN
+    CALL flio_qax (f_i,'t',i_v,nbdim)
+    IF (i_v < 0) THEN
+      CALL ipslerr (3,'fliogstc','No t_axis found','in the file',' ')
+    ENDIF
+!---
+    IF (l_dbg) THEN
+      WRITE(ipslout,*) '  fliogstc - get time details'
+    ENDIF
+!---
+!-- Get all the details for the time
+!-- Prefered method is '"time_steps" since'
+!---
+    name=''
+    i_rc = NF90_INQUIRE_VARIABLE(f_e,i_v,name=name)
+    units=''
+    i_rc = NF90_GET_ATT(f_e,i_v,'units',units)
+    IF      (INDEX(units,' since ') > 0) THEN
+      it_t = 1
+    ELSE IF (INDEX(name,'tstep') > 0) THEN
+      it_t = 2
+    ELSE
+      it_t = 0;
+    ENDIF
+  ENDIF
+!-
+! Extracting the t coordinate, if needed
+!-
+  IF (PRESENT(t_axis)) THEN
+    IF (PRESENT(t_start)) THEN
+      i_t = t_start
+    ELSE
+      i_t = 1
+    ENDIF
+    IF (PRESENT(t_count)) THEN
+      l_t = t_count
+    ELSE
+      l_t = m_t-i_t+1
+    ENDIF
+    IF ((i_t+l_t-1) > m_t) THEN
+      CALL ipslerr (3,'fliogstc', &
+ &      'The requested size for the t axis', &
+ &      'is greater than the size of the coordinate',&
+ &      'in the file')
+    ENDIF
+    ALLOCATE(v_tmp(l_t))
+    i_rc = NF90_GET_VAR(f_e,i_v,v_tmp, &
+ &           start=(/i_t/),count=(/l_t/))
+    t_axis(1:l_t) = NINT(v_tmp(1:l_t))
+    DEALLOCATE(v_tmp)
+!---
+    IF (l_dbg) THEN
+      WRITE(ipslout,*) '  fliogstc - first time : ',t_axis(1:1)
+    ENDIF
+  ENDIF
+!-
+! Extracting the time at the beginning, if needed
+!-
+  IF (PRESENT(t_init)) THEN
+!-- Find the calendar
+    CALL lock_calendar (old_status=l_tmp)
+    CALL ioget_calendar (c_tmp)
+    units = ''
+    i_rc = NF90_GET_ATT(f_e,i_v,'calendar',units)
+    IF (i_rc == NF90_NOERR) THEN
+      CALL lock_calendar (new_status=.FALSE.)
+      CALL ioconf_calendar (TRIM(units))
+    ENDIF
+    IF (it_t == 1) THEN
+      units = ''
+      i_rc = NF90_GET_ATT(f_e,i_v,'units',units)
+      units = units(INDEX(units,' since ')+7:LEN_TRIM(units))
+      READ (units,'(I4.4,5(A,I2.2))') &
+ &      j_yy,c_1,j_mo,c_1,j_dd,c_1,j_hh,c_1,j_mn,c_1,j_ss
+      r_ss = j_hh*3600.+j_mn*60.+j_ss
+      CALL ymds2ju (j_yy,j_mo,j_dd,r_ss,t_init)
+    ELSE IF (it_t == 2) THEN
+      i_rc = NF90_GET_ATT(f_e,NF90_GLOBAL,'year0',r_yy)
+      i_rc = NF90_GET_ATT(f_e,NF90_GLOBAL,'month0',r_mo)
+      i_rc = NF90_GET_ATT(f_e,NF90_GLOBAL,'day0',r_dd)
+      i_rc = NF90_GET_ATT(f_e,NF90_GLOBAL,'sec0',r_ss)
+      j_yy = NINT(r_yy); j_mo = NINT(r_mo); j_dd = NINT(r_dd);
+      CALL ymds2ju (j_yy,j_mo,j_dd,r_ss,t_init)
+    ELSE
+      t_init = 0.
+    ENDIF
+    CALL lock_calendar (new_status=.FALSE.)
+    CALL ioconf_calendar (TRIM(c_tmp))
+    CALL lock_calendar (new_status=l_tmp)
+    IF (l_dbg) THEN
+      WRITE(ipslout,*) '  fliogstc - time_type : '
+      WRITE(ipslout,*) it_t
+      WRITE(ipslout,*) '  fliogstc - year month day second t_init : '
+      WRITE(ipslout,*) j_yy,j_mo,j_dd,r_ss,t_init
+    ENDIF
+  ENDIF
+!-
+! Extracting the timestep in seconds, if needed
+!-
+  IF (PRESENT(t_step)) THEN
+    IF      (it_t == 1) THEN
+      units = ''
+      i_rc = NF90_GET_ATT(f_e,i_v,'units',units)
+      units = ADJUSTL(units(1:INDEX(units,' since ')-1))
+      dtn = 1.
+      IF      (INDEX(units,"week") /= 0) THEN
+        kv  = INDEX(units,"week")
+        dtv = 604800.
+      ELSE IF (INDEX(units,"day")  /= 0) THEN
+        kv  = INDEX(units,"day")
+        dtv = 86400.
+      ELSE IF (INDEX(units,"h")    /= 0) THEN
+        kv  = INDEX(units,"h")
+        dtv = 3600.
+      ELSE IF (INDEX(units,"min")  /= 0) THEN
+        kv  = INDEX(units,"min")
+        dtv = 60.
+      ELSE IF (INDEX(units,"sec")  /= 0) THEN
+        kv  = INDEX(units,"sec")
+        dtv = 1.
+      ELSE IF (INDEX(units,"timesteps") /= 0) THEN
+        kv  = INDEX(units,"timesteps")
+        i_rc = NF90_GET_ATT(f_e,i_v,'tstep_sec',dtv)
+        IF (i_rc /= NF90_NOERR) THEN
+          CALL ipslerr (3,'fliogstc','"timesteps" value', &
+ &                        'not found','in the file')
+        ENDIF
+      ELSE
+        kv  = 1
+        dtv = 1.
+      ENDIF
+      IF (kv > 1) THEN
+        READ (unit=units(1:kv-1),FMT=*) dtn
+      ENDIF
+      t_step = dtn*dtv
+    ELSE IF (it_t == 2) THEN
+      i_rc = NF90_GET_ATT(f_e,NF90_GLOBAL,'delta_tstep_sec',t_step)
+    ELSE
+      t_step = 1.
+    ENDIF
+  ENDIF
+!-
+! Extracting the calendar attribute, if needed
+!-
+  IF (PRESENT(t_calendar)) THEN
+    units = ''
+    i_rc = NF90_GET_ATT(f_e,i_v,'calendar',units)
+    IF (i_rc == NF90_NOERR) THEN
+      t_calendar = units
+    ELSE
+      t_calendar = "not found"
+    ENDIF
+  ENDIF
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "<-fliogstc"
+  ENDIF
+!----------------------
+END SUBROUTINE fliogstc
+!===
+SUBROUTINE flioinqv &
+ & (f_i,v_n,l_ex,v_t,nb_dims,len_dims,id_dims, &
+ &  nb_atts,cn_atts,ia_start,ia_count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  LOGICAL,INTENT(OUT) :: l_ex
+  INTEGER,OPTIONAL,INTENT(OUT) :: v_t,nb_dims,nb_atts
+  INTEGER,OPTIONAL,INTENT(OUT),DIMENSION(:) :: len_dims,id_dims
+  CHARACTER(LEN=*),DIMENSION(:),OPTIONAL,INTENT(OUT) :: cn_atts
+  INTEGER,OPTIONAL,INTENT(IN) :: ia_start,ia_count
+!-
+  INTEGER :: f_e,i_v,n_w,i_s,i_w,iws,iwc,i_rc
+  LOGICAL :: l_ok
+  INTEGER,DIMENSION(NF90_MAX_VAR_DIMS) :: dim_ids
+!-
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "->flioinqv ",TRIM(v_n)
+  ENDIF
+!-
+! Retrieve the external file index
+  CALL flio_qvid ('flioinqv',f_i,f_e)
+!-
+  i_v = -1
+  i_rc = NF90_INQ_VARID(f_e,v_n,i_v)
+!-
+  l_ex = ( (i_v >= 0).AND.(i_rc == NF90_NOERR) )
+!-
+  IF (l_ex) THEN
+    IF (PRESENT(v_t)) THEN
+      i_rc = NF90_INQUIRE_VARIABLE(f_e,i_v,xtype=v_t)
+    ENDIF
+    n_w = -1
+    IF (PRESENT(nb_dims).OR.PRESENT(len_dims)) THEN
+      i_rc = NF90_INQUIRE_VARIABLE(f_e,i_v, &
+ &             ndims=n_w,dimids=dim_ids)
+      IF (PRESENT(nb_dims)) THEN
+        nb_dims = n_w
+      ENDIF
+      IF (PRESENT(len_dims)) THEN
+        i_s = SIZE(len_dims)
+        len_dims(:) = -1
+        IF (i_s < n_w) THEN
+          CALL ipslerr (2,'flioinqv', &
+ &         'Only the first dimensions of the variable', &
+ &         TRIM(v_n),'will be returned')
+        ENDIF
+        DO i_w=1,MIN(n_w,i_s)
+          i_rc = NF90_INQUIRE_DIMENSION(f_e,dim_ids(i_w), &
+ &                                      len=len_dims(i_w))
+        ENDDO
+      ENDIF
+      IF (PRESENT(id_dims)) THEN
+        i_s = SIZE(id_dims)
+        id_dims(:) = -1
+        IF (i_s < n_w) THEN
+          CALL ipslerr (2,'flioinqv', &
+ &         'The number of dimensions to retrieve', &
+ &         'is greater than the size of the array,', &
+ &         'only the first dimensions of "' &
+ &           //TRIM(v_n)//'" will be returned')
+        ENDIF
+        i_w = MIN(n_w,i_s)
+        id_dims(1:i_w) = dim_ids(1:i_w)
+      ENDIF
+    ENDIF
+    IF (PRESENT(nb_atts).OR.PRESENT(cn_atts)) THEN
+      i_rc = NF90_INQUIRE_VARIABLE(f_e,i_v,nAtts=n_w)
+      IF (PRESENT(nb_atts)) THEN
+        nb_atts = n_w
+      ENDIF
+      IF (PRESENT(cn_atts)) THEN
+        l_ok = .TRUE.
+        i_s = SIZE(cn_atts)
+        DO i_w=1,i_s
+          cn_atts(i_w)(:) = '?'
+        ENDDO
+        IF (PRESENT(ia_start)) THEN
+          iws = ia_start
+        ELSE
+          iws = 1
+        ENDIF
+        IF (PRESENT(ia_count)) THEN
+          iwc = ia_count
+        ELSE
+          iwc = n_w
+        ENDIF
+        IF (iws > n_w) THEN
+          l_ok = .FALSE.
+          CALL ipslerr (2,'flioinqv', &
+ &         'The start index of requested attributes', &
+ &         'is greater than the number of attributes of', &
+ &         '"'//TRIM(v_n)//'"')
+        ELSE IF (iws < 1) THEN
+          l_ok = .FALSE.
+          CALL ipslerr (2,'flioinqv', &
+ &         'The start index of requested attributes', &
+ &         'is invalid ( < 1 ) for', &
+ &         '"'//TRIM(v_n)//'"')
+        ENDIF
+        IF ((iws+iwc-1) > n_w) THEN
+          CALL ipslerr (2,'flioinqv', &
+ &         'The number of requested attributes', &
+ &         'is greater than the number of attributes of', &
+ &         '"'//TRIM(v_n)//'"')
+        ENDIF
+        IF (iwc > i_s) THEN
+          CALL ipslerr (2,'flioinqv', &
+ &         'The number of attributes to retrieve', &
+ &         'is greater than the size of the array,', &
+ &         'only the first attributes of "' &
+ &           //TRIM(v_n)//'" will be returned')
+        ELSE IF (iwc < 1) THEN
+          l_ok = .FALSE.
+          CALL ipslerr (2,'flioinqv', &
+ &         'The number of requested attributes', &
+ &         'is invalid ( < 1 ) for', &
+ &         '"'//TRIM(v_n)//'"')
+        ENDIF
+        IF (l_ok) THEN
+          DO i_w=1,MIN(iwc,i_s,n_w-iws+1)
+            i_rc = NF90_INQ_ATTNAME(f_e, &
+ &                  i_v,i_w+iws-1,name=cn_atts(i_w))
+          ENDDO
+        ENDIF
+      ENDIF
+    ENDIF
+  ENDIF
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "<-flioinqv"
+  ENDIF
+!----------------------
+END SUBROUTINE flioinqv
+!===
+SUBROUTINE fliogv_i40 (f_i,v_n,v_v,start)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER(KIND=i_4),INTENT(OUT) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start
+!---------------------------------------------------------------------
+  CALL flio_ugv (f_i,v_n,i_40=v_v,start=start)
+!------------------------
+END SUBROUTINE fliogv_i40
+!===
+SUBROUTINE fliogv_i41 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER(KIND=i_4),DIMENSION(:),INTENT(OUT) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_ugv (f_i,v_n,i_41=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliogv_i41
+!===
+SUBROUTINE fliogv_i42 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER(KIND=i_4),DIMENSION(:,:),INTENT(OUT) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_ugv (f_i,v_n,i_42=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliogv_i42
+!===
+SUBROUTINE fliogv_i43 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER(KIND=i_4),DIMENSION(:,:,:),INTENT(OUT) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_ugv (f_i,v_n,i_43=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliogv_i43
+!===
+SUBROUTINE fliogv_i44 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER(KIND=i_4),DIMENSION(:,:,:,:),INTENT(OUT) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_ugv (f_i,v_n,i_44=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliogv_i44
+!===
+SUBROUTINE fliogv_i45 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER(KIND=i_4),DIMENSION(:,:,:,:,:),INTENT(OUT) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_ugv (f_i,v_n,i_45=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliogv_i45
+!===
+SUBROUTINE fliogv_i20 (f_i,v_n,v_v,start)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER(KIND=i_2),INTENT(OUT) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start
+!---------------------------------------------------------------------
+  CALL flio_ugv (f_i,v_n,i_20=v_v,start=start)
+!------------------------
+END SUBROUTINE fliogv_i20
+!===
+SUBROUTINE fliogv_i21 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER(KIND=i_2),DIMENSION(:),INTENT(OUT) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_ugv (f_i,v_n,i_21=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliogv_i21
+!===
+SUBROUTINE fliogv_i22 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER(KIND=i_2),DIMENSION(:,:),INTENT(OUT) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_ugv (f_i,v_n,i_22=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliogv_i22
+!===
+SUBROUTINE fliogv_i23 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER(KIND=i_2),DIMENSION(:,:,:),INTENT(OUT) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_ugv (f_i,v_n,i_23=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliogv_i23
+!===
+SUBROUTINE fliogv_i24 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER(KIND=i_2),DIMENSION(:,:,:,:),INTENT(OUT) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_ugv (f_i,v_n,i_24=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliogv_i24
+!===
+SUBROUTINE fliogv_i25 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER(KIND=i_2),DIMENSION(:,:,:,:,:),INTENT(OUT) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_ugv (f_i,v_n,i_25=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliogv_i25
+!===
+!?INTEGERS of KIND 1 are not supported on all computers
+!?SUBROUTINE fliogv_i10 (f_i,v_n,v_v,start)
+!?!---------------------------------------------------------------------
+!?  IMPLICIT NONE
+!?!-
+!?  INTEGER,INTENT(IN) :: f_i
+!?  CHARACTER(LEN=*),INTENT(IN) :: v_n
+!?  INTEGER(KIND=i_1),INTENT(OUT) :: v_v
+!?  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start
+!?!---------------------------------------------------------------------
+!?  CALL flio_ugv (f_i,v_n,i_10=v_v,start=start)
+!?!------------------------
+!?END SUBROUTINE fliogv_i10
+!?!===
+!?SUBROUTINE fliogv_i11 (f_i,v_n,v_v,start,count)
+!?!---------------------------------------------------------------------
+!?  IMPLICIT NONE
+!?!-
+!?  INTEGER,INTENT(IN) :: f_i
+!?  CHARACTER(LEN=*),INTENT(IN) :: v_n
+!?  INTEGER(KIND=i_1),DIMENSION(:),INTENT(OUT) :: v_v
+!?  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!?!---------------------------------------------------------------------
+!?  CALL flio_ugv (f_i,v_n,i_11=v_v,start=start,count=count)
+!?!------------------------
+!?END SUBROUTINE fliogv_i11
+!?!===
+!?SUBROUTINE fliogv_i12 (f_i,v_n,v_v,start,count)
+!?!---------------------------------------------------------------------
+!?  IMPLICIT NONE
+!?!-
+!?  INTEGER,INTENT(IN) :: f_i
+!?  CHARACTER(LEN=*),INTENT(IN) :: v_n
+!?  INTEGER(KIND=i_1),DIMENSION(:,:),INTENT(OUT) :: v_v
+!?  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!?!---------------------------------------------------------------------
+!?  CALL flio_ugv (f_i,v_n,i_12=v_v,start=start,count=count)
+!?!------------------------
+!?END SUBROUTINE fliogv_i12
+!?!===
+!?SUBROUTINE fliogv_i13 (f_i,v_n,v_v,start,count)
+!?!---------------------------------------------------------------------
+!?  IMPLICIT NONE
+!?!-
+!?  INTEGER,INTENT(IN) :: f_i
+!?  CHARACTER(LEN=*),INTENT(IN) :: v_n
+!?  INTEGER(KIND=i_1),DIMENSION(:,:,:),INTENT(OUT) :: v_v
+!?  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!?!---------------------------------------------------------------------
+!?  CALL flio_ugv (f_i,v_n,i_13=v_v,start=start,count=count)
+!?!------------------------
+!?END SUBROUTINE fliogv_i13
+!?!===
+!?SUBROUTINE fliogv_i14 (f_i,v_n,v_v,start,count)
+!?!---------------------------------------------------------------------
+!?  IMPLICIT NONE
+!?!-
+!?  INTEGER,INTENT(IN) :: f_i
+!?  CHARACTER(LEN=*),INTENT(IN) :: v_n
+!?  INTEGER(KIND=i_1),DIMENSION(:,:,:,:),INTENT(OUT) :: v_v
+!?  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!?!---------------------------------------------------------------------
+!?  CALL flio_ugv (f_i,v_n,i_14=v_v,start=start,count=count)
+!?!------------------------
+!?END SUBROUTINE fliogv_i14
+!?!===
+!?SUBROUTINE fliogv_i15 (f_i,v_n,v_v,start,count)
+!?!---------------------------------------------------------------------
+!?  IMPLICIT NONE
+!?!-
+!?  INTEGER,INTENT(IN) :: f_i
+!?  CHARACTER(LEN=*),INTENT(IN) :: v_n
+!?  INTEGER(KIND=i_1),DIMENSION(:,:,:,:,:),INTENT(OUT) :: v_v
+!?  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!?!---------------------------------------------------------------------
+!?  CALL flio_ugv (f_i,v_n,i_15=v_v,start=start,count=count)
+!?!------------------------
+!?END SUBROUTINE fliogv_i15
+!===
+SUBROUTINE fliogv_r40 (f_i,v_n,v_v,start)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  REAL(KIND=r_4),INTENT(OUT) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start
+!---------------------------------------------------------------------
+  CALL flio_ugv (f_i,v_n,r_40=v_v,start=start)
+!------------------------
+END SUBROUTINE fliogv_r40
+!===
+SUBROUTINE fliogv_r41 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  REAL(KIND=r_4),DIMENSION(:),INTENT(OUT) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_ugv (f_i,v_n,r_41=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliogv_r41
+!===
+SUBROUTINE fliogv_r42 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  REAL(KIND=r_4),DIMENSION(:,:),INTENT(OUT) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_ugv (f_i,v_n,r_42=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliogv_r42
+!===
+SUBROUTINE fliogv_r43 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  REAL(KIND=r_4),DIMENSION(:,:,:),INTENT(OUT) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_ugv (f_i,v_n,r_43=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliogv_r43
+!===
+SUBROUTINE fliogv_r44 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  REAL(KIND=r_4),DIMENSION(:,:,:,:),INTENT(OUT) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_ugv (f_i,v_n,r_44=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliogv_r44
+!===
+SUBROUTINE fliogv_r45 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  REAL(KIND=r_4),DIMENSION(:,:,:,:,:),INTENT(OUT) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_ugv (f_i,v_n,r_45=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliogv_r45
+!===
+SUBROUTINE fliogv_r80 (f_i,v_n,v_v,start)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  REAL(KIND=r_8),INTENT(OUT) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start
+!---------------------------------------------------------------------
+  CALL flio_ugv (f_i,v_n,r_80=v_v,start=start)
+!------------------------
+END SUBROUTINE fliogv_r80
+!===
+SUBROUTINE fliogv_r81 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  REAL(KIND=r_8),DIMENSION(:),INTENT(OUT) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_ugv (f_i,v_n,r_81=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliogv_r81
+!===
+SUBROUTINE fliogv_r82 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  REAL(KIND=r_8),DIMENSION(:,:),INTENT(OUT) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_ugv (f_i,v_n,r_82=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliogv_r82
+!===
+SUBROUTINE fliogv_r83 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  REAL(KIND=r_8),DIMENSION(:,:,:),INTENT(OUT) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_ugv (f_i,v_n,r_83=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliogv_r83
+!===
+SUBROUTINE fliogv_r84 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  REAL(KIND=r_8),DIMENSION(:,:,:,:),INTENT(OUT) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_ugv (f_i,v_n,r_84=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliogv_r84
+!===
+SUBROUTINE fliogv_r85 (f_i,v_n,v_v,start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  REAL(KIND=r_8),DIMENSION(:,:,:,:,:),INTENT(OUT) :: v_v
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(IN) :: start,count
+!---------------------------------------------------------------------
+  CALL flio_ugv (f_i,v_n,r_85=v_v,start=start,count=count)
+!------------------------
+END SUBROUTINE fliogv_r85
+!===
+SUBROUTINE flio_ugv &
+ & (f_i,v_n, &
+ &  i_40,i_41,i_42,i_43,i_44,i_45, &
+ &  i_20,i_21,i_22,i_23,i_24,i_25, &
+!? &  i_10,i_11,i_12,i_13,i_14,i_15, &
+ &  r_40,r_41,r_42,r_43,r_44,r_45, &
+ &  r_80,r_81,r_82,r_83,r_84,r_85, &
+ &  start,count)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n
+  INTEGER(KIND=i_4),INTENT(OUT),OPTIONAL :: i_40
+  INTEGER(KIND=i_4),DIMENSION(:),INTENT(OUT),OPTIONAL :: i_41
+  INTEGER(KIND=i_4),DIMENSION(:,:),INTENT(OUT),OPTIONAL :: i_42
+  INTEGER(KIND=i_4),DIMENSION(:,:,:),INTENT(OUT),OPTIONAL :: i_43
+  INTEGER(KIND=i_4),DIMENSION(:,:,:,:),INTENT(OUT),OPTIONAL :: i_44
+  INTEGER(KIND=i_4),DIMENSION(:,:,:,:,:),INTENT(OUT),OPTIONAL :: i_45
+  INTEGER(KIND=i_2),INTENT(OUT),OPTIONAL :: i_20
+  INTEGER(KIND=i_2),DIMENSION(:),INTENT(OUT),OPTIONAL :: i_21
+  INTEGER(KIND=i_2),DIMENSION(:,:),INTENT(OUT),OPTIONAL :: i_22
+  INTEGER(KIND=i_2),DIMENSION(:,:,:),INTENT(OUT),OPTIONAL :: i_23
+  INTEGER(KIND=i_2),DIMENSION(:,:,:,:),INTENT(OUT),OPTIONAL :: i_24
+  INTEGER(KIND=i_2),DIMENSION(:,:,:,:,:),INTENT(OUT),OPTIONAL :: i_25
+!?INTEGERS of KIND 1 are not supported on all computers
+!?INTEGER(KIND=i_1),INTENT(OUT),OPTIONAL :: i_10
+!?INTEGER(KIND=i_1),DIMENSION(:),INTENT(OUT),OPTIONAL :: i_11
+!?INTEGER(KIND=i_1),DIMENSION(:,:),INTENT(OUT),OPTIONAL :: i_12
+!?INTEGER(KIND=i_1),DIMENSION(:,:,:),INTENT(OUT),OPTIONAL :: i_13
+!?INTEGER(KIND=i_1),DIMENSION(:,:,:,:),INTENT(OUT),OPTIONAL :: i_14
+!?INTEGER(KIND=i_1),DIMENSION(:,:,:,:,:),INTENT(OUT),OPTIONAL :: i_15
+  REAL(KIND=r_4),INTENT(OUT),OPTIONAL :: r_40
+  REAL(KIND=r_4),DIMENSION(:),INTENT(OUT),OPTIONAL :: r_41
+  REAL(KIND=r_4),DIMENSION(:,:),INTENT(OUT),OPTIONAL :: r_42
+  REAL(KIND=r_4),DIMENSION(:,:,:),INTENT(OUT),OPTIONAL :: r_43
+  REAL(KIND=r_4),DIMENSION(:,:,:,:),INTENT(OUT),OPTIONAL :: r_44
+  REAL(KIND=r_4),DIMENSION(:,:,:,:,:),INTENT(OUT),OPTIONAL :: r_45
+  REAL(KIND=r_8),INTENT(OUT),OPTIONAL :: r_80
+  REAL(KIND=r_8),DIMENSION(:),INTENT(OUT),OPTIONAL :: r_81
+  REAL(KIND=r_8),DIMENSION(:,:),INTENT(OUT),OPTIONAL :: r_82
+  REAL(KIND=r_8),DIMENSION(:,:,:),INTENT(OUT),OPTIONAL :: r_83
+  REAL(KIND=r_8),DIMENSION(:,:,:,:),INTENT(OUT),OPTIONAL :: r_84
+  REAL(KIND=r_8),DIMENSION(:,:,:,:,:),INTENT(OUT),OPTIONAL :: r_85
+  INTEGER,DIMENSION(:),INTENT(IN),OPTIONAL :: start,count
+!-
+  INTEGER :: f_e,i_v,i_rc
+  CHARACTER(LEN=5) :: cvr_d
+!-
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (l_dbg) THEN
+    IF      (PRESENT(i_40)) THEN; cvr_d = "I1 0D";
+    ELSE IF (PRESENT(i_41)) THEN; cvr_d = "I1 1D";
+    ELSE IF (PRESENT(i_42)) THEN; cvr_d = "I1 2D";
+    ELSE IF (PRESENT(i_43)) THEN; cvr_d = "I1 3D";
+    ELSE IF (PRESENT(i_44)) THEN; cvr_d = "I1 4D";
+    ELSE IF (PRESENT(i_45)) THEN; cvr_d = "I1 5D";
+    ELSE IF (PRESENT(i_20)) THEN; cvr_d = "I2 0D";
+    ELSE IF (PRESENT(i_21)) THEN; cvr_d = "I2 1D";
+    ELSE IF (PRESENT(i_22)) THEN; cvr_d = "I2 2D";
+    ELSE IF (PRESENT(i_23)) THEN; cvr_d = "I2 3D";
+    ELSE IF (PRESENT(i_24)) THEN; cvr_d = "I2 4D";
+    ELSE IF (PRESENT(i_25)) THEN; cvr_d = "I2 5D";
+!?  ELSE IF (PRESENT(i_10)) THEN; cvr_d = "I4 0D";
+!?  ELSE IF (PRESENT(i_11)) THEN; cvr_d = "I4 1D";
+!?  ELSE IF (PRESENT(i_12)) THEN; cvr_d = "I4 2D";
+!?  ELSE IF (PRESENT(i_13)) THEN; cvr_d = "I4 3D";
+!?  ELSE IF (PRESENT(i_14)) THEN; cvr_d = "I4 4D";
+!?  ELSE IF (PRESENT(i_15)) THEN; cvr_d = "I4 5D";
+    ELSE IF (PRESENT(r_40)) THEN; cvr_d = "R4 0D";
+    ELSE IF (PRESENT(r_41)) THEN; cvr_d = "R4 1D";
+    ELSE IF (PRESENT(r_42)) THEN; cvr_d = "R4 2D";
+    ELSE IF (PRESENT(r_43)) THEN; cvr_d = "R4 3D";
+    ELSE IF (PRESENT(r_44)) THEN; cvr_d = "R4 4D";
+    ELSE IF (PRESENT(r_45)) THEN; cvr_d = "R4 5D";
+    ELSE IF (PRESENT(r_80)) THEN; cvr_d = "R8 0D";
+    ELSE IF (PRESENT(r_81)) THEN; cvr_d = "R8 1D";
+    ELSE IF (PRESENT(r_82)) THEN; cvr_d = "R8 2D";
+    ELSE IF (PRESENT(r_83)) THEN; cvr_d = "R8 3D";
+    ELSE IF (PRESENT(r_84)) THEN; cvr_d = "R8 4D";
+    ELSE IF (PRESENT(r_85)) THEN; cvr_d = "R8 5D";
+    ENDIF
+    WRITE(ipslout,*) "->fliogetv ",TRIM(v_n)," ",TRIM(cvr_d)
+  ENDIF
+!-
+! Retrieve the external file index
+  CALL flio_qvid ('fliogetv',f_i,f_e)
+!-
+! Ensuring data mode
+!-
+  CALL flio_hdm (f_i,f_e,.FALSE.)
+!-
+  i_rc = NF90_INQ_VARID(f_e,v_n,i_v)
+  IF (i_rc == NF90_NOERR) THEN
+    IF      (PRESENT(i_40)) THEN
+      i_rc = NF90_GET_VAR(f_e,i_v,i_40,start=start)
+    ELSE IF (PRESENT(i_41)) THEN
+      i_rc = NF90_GET_VAR(f_e,i_v,i_41,start=start,count=count)
+    ELSE IF (PRESENT(i_42)) THEN
+      i_rc = NF90_GET_VAR(f_e,i_v,i_42,start=start,count=count)
+    ELSE IF (PRESENT(i_43)) THEN
+      i_rc = NF90_GET_VAR(f_e,i_v,i_43,start=start,count=count)
+    ELSE IF (PRESENT(i_44)) THEN
+      i_rc = NF90_GET_VAR(f_e,i_v,i_44,start=start,count=count)
+    ELSE IF (PRESENT(i_45)) THEN
+      i_rc = NF90_GET_VAR(f_e,i_v,i_45,start=start,count=count)
+    ELSE IF (PRESENT(i_20)) THEN
+      i_rc = NF90_GET_VAR(f_e,i_v,i_20,start=start)
+    ELSE IF (PRESENT(i_21)) THEN
+      i_rc = NF90_GET_VAR(f_e,i_v,i_21,start=start,count=count)
+    ELSE IF (PRESENT(i_22)) THEN
+      i_rc = NF90_GET_VAR(f_e,i_v,i_22,start=start,count=count)
+    ELSE IF (PRESENT(i_23)) THEN
+      i_rc = NF90_GET_VAR(f_e,i_v,i_23,start=start,count=count)
+    ELSE IF (PRESENT(i_24)) THEN
+      i_rc = NF90_GET_VAR(f_e,i_v,i_24,start=start,count=count)
+    ELSE IF (PRESENT(i_25)) THEN
+      i_rc = NF90_GET_VAR(f_e,i_v,i_25,start=start,count=count)
+!?  ELSE IF (PRESENT(i_10)) THEN
+!?    i_rc = NF90_GET_VAR(f_e,i_v,i_10,start=start)
+!?  ELSE IF (PRESENT(i_11)) THEN
+!?    i_rc = NF90_GET_VAR(f_e,i_v,i_11,start=start,count=count)
+!?  ELSE IF (PRESENT(i_12)) THEN
+!?    i_rc = NF90_GET_VAR(f_e,i_v,i_12,start=start,count=count)
+!?  ELSE IF (PRESENT(i_13)) THEN
+!?    i_rc = NF90_GET_VAR(f_e,i_v,i_13,start=start,count=count)
+!?  ELSE IF (PRESENT(i_14)) THEN
+!?    i_rc = NF90_GET_VAR(f_e,i_v,i_14,start=start,count=count)
+!?  ELSE IF (PRESENT(i_15)) THEN
+!?    i_rc = NF90_GET_VAR(f_e,i_v,i_15,start=start,count=count)
+    ELSE IF (PRESENT(r_40)) THEN
+      i_rc = NF90_GET_VAR(f_e,i_v,r_40,start=start)
+    ELSE IF (PRESENT(r_41)) THEN
+      i_rc = NF90_GET_VAR(f_e,i_v,r_41,start=start,count=count)
+    ELSE IF (PRESENT(r_42)) THEN
+      i_rc = NF90_GET_VAR(f_e,i_v,r_42,start=start,count=count)
+    ELSE IF (PRESENT(r_43)) THEN
+      i_rc = NF90_GET_VAR(f_e,i_v,r_43,start=start,count=count)
+    ELSE IF (PRESENT(r_44)) THEN
+      i_rc = NF90_GET_VAR(f_e,i_v,r_44,start=start,count=count)
+    ELSE IF (PRESENT(r_45)) THEN
+      i_rc = NF90_GET_VAR(f_e,i_v,r_45,start=start,count=count)
+    ELSE IF (PRESENT(r_80)) THEN
+      i_rc = NF90_GET_VAR(f_e,i_v,r_80,start=start)
+    ELSE IF (PRESENT(r_81)) THEN
+      i_rc = NF90_GET_VAR(f_e,i_v,r_81,start=start,count=count)
+    ELSE IF (PRESENT(r_82)) THEN
+      i_rc = NF90_GET_VAR(f_e,i_v,r_82,start=start,count=count)
+    ELSE IF (PRESENT(r_83)) THEN
+      i_rc = NF90_GET_VAR(f_e,i_v,r_83,start=start,count=count)
+    ELSE IF (PRESENT(r_84)) THEN
+      i_rc = NF90_GET_VAR(f_e,i_v,r_84,start=start,count=count)
+    ELSE IF (PRESENT(r_85)) THEN
+      i_rc = NF90_GET_VAR(f_e,i_v,r_85,start=start,count=count)
+    ENDIF
+    IF (i_rc /= NF90_NOERR) THEN
+      CALL ipslerr (3,'fliogetv', &
+ &      'Variable '//TRIM(v_n)//' not get','Error :', &
+ &      TRIM(NF90_STRERROR(i_rc)))
+    ENDIF
+  ELSE
+    CALL ipslerr (3,'fliogetv','Variable',TRIM(v_n),'not found')
+  ENDIF
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "<-fliogetv"
+  ENDIF
+!----------------------
+END SUBROUTINE flio_ugv
+!===
+SUBROUTINE flioinqa (f_i,v_n,a_n,l_ex,a_t,a_l)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n,a_n
+  LOGICAL,INTENT(OUT) :: l_ex
+  INTEGER,OPTIONAL,INTENT(OUT) :: a_t,a_l
+!-
+  INTEGER :: i_rc,f_e,i_v,t_ea,l_ea
+!-
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "->flioinqa ",TRIM(v_n),"-",TRIM(a_n)
+  ENDIF
+!-
+! Retrieve the external file index
+  CALL flio_qvid ('flioinqa',f_i,f_e)
+!-
+  IF (TRIM(v_n) == '?') THEN
+    i_v = NF90_GLOBAL
+  ELSE
+    i_rc = NF90_INQ_VARID(f_e,v_n,i_v)
+    IF (i_rc /= NF90_NOERR) THEN
+      CALL ipslerr (3,'flioinqa', &
+       'Variable :',TRIM(v_n),'not found')
+    ENDIF
+  ENDIF
+!-
+  i_rc = NF90_INQUIRE_ATTRIBUTE(f_e,i_v,a_n,xtype=t_ea,len=l_ea)
+!-
+  l_ex = (i_rc == NF90_NOERR)
+!-
+  IF (l_ex) THEN
+    IF (PRESENT(a_t)) THEN
+      a_t = t_ea
+    ENDIF
+    IF (PRESENT(a_l)) THEN
+      a_l = l_ea
+    ENDIF
+  ENDIF
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "<-flioinqa"
+  ENDIF
+!----------------------
+END SUBROUTINE flioinqa
+!===
+SUBROUTINE flioga_r4_0d (f_i,v_n,a_n,a_v)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n,a_n
+  REAL(KIND=4),INTENT(OUT) :: a_v
+!---------------------------------------------------------------------
+  CALL flio_uga (f_i,v_n,a_n,avr_4_0=a_v)
+!---------------------------
+END SUBROUTINE flioga_r4_0d
+!===
+SUBROUTINE flioga_r4_1d (f_i,v_n,a_n,a_v)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n,a_n
+  REAL(KIND=4),DIMENSION(:),INTENT(OUT) :: a_v
+!---------------------------------------------------------------------
+  CALL flio_uga (f_i,v_n,a_n,avr_4_1=a_v)
+!--------------------------
+END SUBROUTINE flioga_r4_1d
+!===
+SUBROUTINE flioga_r8_0d (f_i,v_n,a_n,a_v)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n,a_n
+  REAL(KIND=8),INTENT(OUT) :: a_v
+!---------------------------------------------------------------------
+  CALL flio_uga (f_i,v_n,a_n,avr_8_0=a_v)
+!---------------------------
+END SUBROUTINE flioga_r8_0d
+!===
+SUBROUTINE flioga_r8_1d (f_i,v_n,a_n,a_v)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n,a_n
+  REAL(KIND=8),DIMENSION(:),INTENT(OUT) :: a_v
+!---------------------------------------------------------------------
+  CALL flio_uga (f_i,v_n,a_n,avr_8_1=a_v)
+!--------------------------
+END SUBROUTINE flioga_r8_1d
+!===
+SUBROUTINE flioga_i4_0d (f_i,v_n,a_n,a_v)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n,a_n
+  INTEGER(KIND=4),INTENT(OUT) :: a_v
+!---------------------------------------------------------------------
+  CALL flio_uga (f_i,v_n,a_n,avi_4_0=a_v)
+!---------------------------
+END SUBROUTINE flioga_i4_0d
+!===
+SUBROUTINE flioga_i4_1d (f_i,v_n,a_n,a_v)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n,a_n
+  INTEGER(KIND=4),DIMENSION(:),INTENT(OUT) :: a_v
+!---------------------------------------------------------------------
+  CALL flio_uga (f_i,v_n,a_n,avi_4_1=a_v)
+!--------------------------
+END SUBROUTINE flioga_i4_1d
+!===
+SUBROUTINE flioga_tx_0d (f_i,v_n,a_n,a_v)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n,a_n
+  CHARACTER(LEN=*),INTENT(OUT) :: a_v
+!---------------------------------------------------------------------
+  CALL flio_uga (f_i,v_n,a_n,avtx=a_v)
+!---------------------------
+END SUBROUTINE flioga_tx_0d
+!===
+SUBROUTINE flio_uga &
+ & (f_i,v_n,a_n, &
+ &  avr_4_0,avr_4_1,avr_8_0,avr_8_1,avi_4_0,avi_4_1,avtx)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n,a_n
+  REAL(KIND=4),OPTIONAL,INTENT(OUT) :: avr_4_0
+  REAL(KIND=4),DIMENSION(:),OPTIONAL,INTENT(OUT) :: avr_4_1
+  REAL(KIND=8),OPTIONAL,INTENT(OUT) :: avr_8_0
+  REAL(KIND=8),DIMENSION(:),OPTIONAL,INTENT(OUT) :: avr_8_1
+  INTEGER(KIND=4),OPTIONAL,INTENT(OUT) :: avi_4_0
+  INTEGER(KIND=4),DIMENSION(:),OPTIONAL,INTENT(OUT) :: avi_4_1
+  CHARACTER(LEN=*),OPTIONAL,INTENT(OUT) :: avtx
+!-
+  INTEGER :: f_e,l_ua,i_v,t_ea,l_ea,i_rc
+!-
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "->fliogeta ",TRIM(v_n)," ",TRIM(a_n)
+  ENDIF
+!-
+! Retrieve the external file index
+  CALL flio_qvid ('fliogeta',f_i,f_e)
+!-
+  IF (TRIM(v_n) == '?') THEN
+    i_v = NF90_GLOBAL
+  ELSE
+    i_rc = NF90_INQ_VARID(f_e,v_n,i_v)
+    IF (i_rc /= NF90_NOERR) THEN
+      CALL ipslerr (3,'fliogeta', &
+       'Variable :',TRIM(v_n),'not found')
+    ENDIF
+  ENDIF
+!-
+  i_rc = NF90_INQUIRE_ATTRIBUTE(f_e,i_v,a_n,xtype=t_ea,len=l_ea)
+  IF (i_rc /= NF90_NOERR) THEN
+    CALL ipslerr (3,'fliogeta', &
+ &   'Attribute :',TRIM(a_n),'not found')
+  ENDIF
+!-
+  IF ( (.NOT.PRESENT(avtx).AND.(t_ea == NF90_CHAR)) &
+ &      .OR.(PRESENT(avtx).AND.(t_ea /= NF90_CHAR)) ) THEN
+    CALL ipslerr (3,'fliogeta', &
+ &   'The external type of the attribute :',TRIM(a_n), &
+ &   'is not compatible with the type of the argument')
+  ENDIF
+!-
+  IF      (PRESENT(avr_4_1)) THEN
+    l_ua = SIZE(avr_4_1)
+  ELSE IF (PRESENT(avr_8_1)) THEN
+    l_ua = SIZE(avr_8_1)
+  ELSE IF (PRESENT(avi_4_1)) THEN
+    l_ua = SIZE(avi_4_1)
+  ELSE IF (PRESENT(avtx)) THEN
+    l_ua = LEN(avtx)
+  ELSE
+    l_ua = 1
+  ENDIF
+!-
+  IF (l_ua < l_ea) THEN
+    CALL ipslerr (3,'fliogeta', &
+     'Insufficient size of the argument', &
+ &   'to receive the values of the attribute :',TRIM(a_n))
+  ENDIF
+!-
+  IF      (PRESENT(avr_4_0)) THEN
+    i_rc = NF90_GET_ATT(f_e,i_v,a_n,avr_4_0)
+  ELSE IF (PRESENT(avr_4_1)) THEN
+    i_rc = NF90_GET_ATT(f_e,i_v,a_n,avr_4_1(1:l_ea))
+  ELSE IF (PRESENT(avr_8_0)) THEN
+    i_rc = NF90_GET_ATT(f_e,i_v,a_n,avr_8_0)
+  ELSE IF (PRESENT(avr_8_1)) THEN
+    i_rc = NF90_GET_ATT(f_e,i_v,a_n,avr_8_1(1:l_ea))
+  ELSE IF (PRESENT(avi_4_0)) THEN
+    i_rc = NF90_GET_ATT(f_e,i_v,a_n,avi_4_0)
+  ELSE IF (PRESENT(avi_4_1)) THEN
+    i_rc = NF90_GET_ATT(f_e,i_v,a_n,avi_4_1(1:l_ea))
+  ELSE IF (PRESENT(avtx)) THEN
+    i_rc = NF90_GET_ATT(f_e,i_v,a_n,avtx)
+  ENDIF
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "<-fliogeta"
+  ENDIF
+!----------------------
+END SUBROUTINE flio_uga
+!===
+SUBROUTINE fliorenv (f_i,v_o_n,v_n_n)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_o_n,v_n_n
+!-
+  INTEGER :: f_e,i_v,i_rc
+!-
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) &
+ &    "->fliorenv ",TRIM(v_o_n),"->",TRIM(v_n_n)
+  ENDIF
+!-
+! Retrieve the external file index
+  CALL flio_qvid ('fliorenv',f_i,f_e)
+!-
+  i_rc = NF90_INQ_VARID(f_e,v_o_n,i_v)
+  IF (i_rc /= NF90_NOERR) THEN
+    CALL ipslerr (2,'fliorenv', &
+     'Variable :',TRIM(v_o_n),'not found')
+  ELSE
+    CALL flio_hdm (f_i,f_e,.TRUE.)
+    i_rc = NF90_RENAME_VAR(f_e,i_v,v_n_n)
+    IF (i_rc /= NF90_NOERR) THEN
+      CALL ipslerr (2,'fliorenv', &
+       'Variable :',TRIM(v_o_n),'can not be renamed')
+    ENDIF
+  ENDIF
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "<-fliorenv"
+  ENDIF
+!----------------------
+END SUBROUTINE fliorenv
+!===
+SUBROUTINE fliorena (f_i,v_n,a_o_n,a_n_n)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n,a_o_n,a_n_n
+!-
+  INTEGER :: f_e,i_v,i_a,i_rc
+!-
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) &
+ &    "->fliorena ",TRIM(v_n),"-",TRIM(a_o_n),"->",TRIM(a_n_n)
+  ENDIF
+!-
+! Retrieve the external file index
+  CALL flio_qvid ('fliorena',f_i,f_e)
+!-
+  IF (TRIM(v_n) == '?') THEN
+    i_v = NF90_GLOBAL
+  ELSE
+    i_rc = NF90_INQ_VARID(f_e,v_n,i_v)
+    IF (i_rc /= NF90_NOERR) THEN
+      CALL ipslerr (3,'fliorena', &
+       'Variable :',TRIM(v_n),'not found')
+    ENDIF
+  ENDIF
+!-
+  i_rc = NF90_INQUIRE_ATTRIBUTE(f_e,i_v,a_o_n,attnum=i_a)
+  IF (i_rc /= NF90_NOERR) THEN
+    CALL ipslerr (2,'fliorena', &
+     'Attribute :',TRIM(a_o_n),'not found')
+  ELSE
+    CALL flio_hdm (f_i,f_e,.TRUE.)
+    i_rc = NF90_RENAME_ATT(f_e,i_v,a_o_n,a_n_n)
+    IF (i_rc /= NF90_NOERR) THEN
+      CALL ipslerr (2,'fliorena', &
+       'Attribute :',TRIM(a_o_n),'can not be renamed')
+    ENDIF
+  ENDIF
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "<-fliorena"
+  ENDIF
+!----------------------
+END SUBROUTINE fliorena
+!===
+SUBROUTINE fliodela (f_i,v_n,a_n)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: v_n,a_n
+!-
+  INTEGER :: f_e,i_v,i_a,i_rc
+!-
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "->fliodela ",TRIM(v_n),"-",TRIM(a_n)
+  ENDIF
+!-
+! Retrieve the external file index
+  CALL flio_qvid ('fliodela',f_i,f_e)
+!-
+  IF (TRIM(v_n) == '?') THEN
+    i_v = NF90_GLOBAL
+  ELSE
+    i_rc = NF90_INQ_VARID(f_e,v_n,i_v)
+    IF (i_rc /= NF90_NOERR) THEN
+      CALL ipslerr (3,'fliodela', &
+ &     'Variable :',TRIM(v_n),'not found')
+    ENDIF
+  ENDIF
+!-
+  i_rc = NF90_INQUIRE_ATTRIBUTE(f_e,i_v,a_n,attnum=i_a)
+  IF (i_rc /= NF90_NOERR) THEN
+    CALL ipslerr (2,'fliodela', &
+ &   'Attribute :',TRIM(a_n),'not found')
+  ELSE
+    IF (i_v == NF90_GLOBAL) THEN
+      nw_na(f_i) = nw_na(f_i)-1
+    ENDIF
+    CALL flio_hdm (f_i,f_e,.TRUE.)
+    i_rc = NF90_DEL_ATT(f_e,i_v,a_n)
+  ENDIF
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "<-fliodela"
+  ENDIF
+!----------------------
+END SUBROUTINE fliodela
+!===
+SUBROUTINE fliocpya (f_i_i,v_n_i,a_n,f_i_o,v_n_o)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i_i,f_i_o
+  CHARACTER(LEN=*),INTENT(IN) :: v_n_i,a_n,v_n_o
+!-
+  INTEGER :: f_e_i,f_e_o,i_v_i,i_v_o,i_a,i_rc
+!-
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "->fliocpya - file",f_i_i,"-",TRIM(v_n_i),"-",TRIM(a_n)
+    WRITE(ipslout,*) "  copied to file ",f_i_o,"-",TRIM(v_n_o)
+  ENDIF
+!-
+! Retrieve the external file index
+  CALL flio_qvid ('fliocpya',f_i_i,f_e_i)
+  CALL flio_qvid ('fliocpya',f_i_o,f_e_o)
+!-
+  IF (TRIM(v_n_i) == '?') THEN
+    i_v_i = NF90_GLOBAL
+  ELSE
+    i_rc = NF90_INQ_VARID(f_e_i,v_n_i,i_v_i)
+    IF (i_rc /= NF90_NOERR) THEN
+      CALL ipslerr (3,'fliocpya', &
+ &     'Variable :',TRIM(v_n_i),'not found')
+    ENDIF
+  ENDIF
+!-
+  IF (TRIM(v_n_o) == '?') THEN
+    i_v_o = NF90_GLOBAL
+  ELSE
+    i_rc = NF90_INQ_VARID(f_e_o,v_n_o,i_v_o)
+    IF (i_rc /= NF90_NOERR) THEN
+      CALL ipslerr (3,'fliocpya', &
+ &     'Variable :',TRIM(v_n_o),'not found')
+    ENDIF
+  ENDIF
+!-
+  i_rc = NF90_INQUIRE_ATTRIBUTE(f_e_i,i_v_i,a_n,attnum=i_a)
+  IF (i_rc /= NF90_NOERR) THEN
+    CALL ipslerr (3,'fliocpya', &
+     'Attribute :',TRIM(a_n),'not found')
+  ELSE
+    i_rc = NF90_INQUIRE_ATTRIBUTE(f_e_o,i_v_o,a_n,attnum=i_a)
+    IF ( (i_v_o == NF90_GLOBAL).AND.(i_rc /= NF90_NOERR) ) THEN
+      nw_na(f_i_o) = nw_na(f_i_o)+1
+    ENDIF
+    CALL flio_hdm (f_i_o,f_e_o,.TRUE.)
+    i_rc = NF90_COPY_ATT(f_e_i,i_v_i,a_n,f_e_o,i_v_o)
+    IF (i_rc /= NF90_NOERR) THEN
+      CALL ipslerr (3,'fliocpya', &
+ &      'Attribute '//TRIM(a_n)//' not copied','Error :', &
+ &      TRIM(NF90_STRERROR(i_rc)))
+    ENDIF
+  ENDIF
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "<-fliocpya"
+  ENDIF
+!----------------------
+END SUBROUTINE fliocpya
+!===
+SUBROUTINE flioqstc (f_i,c_type,l_ex,c_name)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i
+  CHARACTER(LEN=*),INTENT(IN) :: c_type
+  LOGICAL,INTENT(OUT) :: l_ex
+  CHARACTER(LEN=*),INTENT(OUT) :: c_name
+!-
+  CHARACTER(LEN=1) :: c_ax
+  INTEGER :: f_e,idc,ndc,i_rc
+!-
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "->flioqstc ",TRIM(c_type)
+  ENDIF
+!-
+! Retrieve the external file index
+  CALL flio_qvid ('flioqstc',f_i,f_e)
+!-
+  c_ax = TRIM(c_type)
+  IF (    (LEN_TRIM(c_type) == 1) &
+ &    .AND.(    (c_ax == 'x').OR.(c_ax == 'y') &
+ &          .OR.(c_ax == 'z').OR.(c_ax == 't')) ) THEN
+    CALL flio_qax (f_i,c_ax,idc,ndc)
+    l_ex = (idc > 0)
+    IF (l_ex) THEN
+      i_rc = NF90_INQUIRE_VARIABLE(f_e,idc,name=c_name)
+    ENDIF
+  ELSE
+    l_ex = .FALSE.
+    CALL ipslerr (2,'flioqstc', &
+ &   'The name of the coordinate,',TRIM(c_type),'is not valid')
+  ENDIF
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "<-flioqstc"
+  ENDIF
+!----------------------
+END SUBROUTINE flioqstc
+!===
+SUBROUTINE fliosync (f_i)
+!---------------------------------------------------------------------
+  INTEGER,INTENT(in),OPTIONAL :: f_i
+!-
+  INTEGER :: i_f,f_e,i_rc,i_s,i_e
+!-
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "->fliosync"
+  ENDIF
+!-
+  IF (PRESENT(f_i)) THEN
+    IF ( (f_i >= 1).AND.(f_i <= nb_fi_mx) ) THEN
+      i_s = f_i
+      i_e = f_i
+    ELSE
+      i_s = 1
+      i_e = 0
+      CALL ipslerr (2,'fliosync', &
+ &     'Invalid file identifier',' ',' ')
+    ENDIF
+  ELSE
+    i_s = 1
+    i_e = nb_fi_mx
+  ENDIF
+!-
+! Ensuring data mode
+!-
+  CALL flio_hdm (f_i,f_e,.FALSE.)
+!-
+  DO i_f=i_s,i_e
+    f_e = nw_id(i_f)
+    IF (f_e > 0) THEN
+      IF (l_dbg) THEN
+        WRITE(ipslout,*) '  fliosync - synchronising file number ',i_f
+      ENDIF
+      i_rc = NF90_SYNC(f_e)
+    ELSE IF (PRESENT(f_i)) THEN
+      CALL ipslerr (2,'fliosync', &
+ &     'Unable to synchronise the file :','probably','not opened')
+    ENDIF
+  ENDDO
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "<-fliosync"
+  ENDIF
+!----------------------
+END SUBROUTINE fliosync
+!===
+SUBROUTINE flioclo (f_i)
+!---------------------------------------------------------------------
+  INTEGER,INTENT(in),OPTIONAL :: f_i
+!-
+  INTEGER :: i_f,f_e,i_rc,i_s,i_e
+!-
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "->flioclo"
+  ENDIF
+!-
+  IF (PRESENT(f_i)) THEN
+    IF ( (f_i >= 1).AND.(f_i <= nb_fi_mx) ) THEN
+      i_s = f_i
+      i_e = f_i
+    ELSE
+      i_s = 1
+      i_e = 0
+      CALL ipslerr (2,'flioclo', &
+ &     'Invalid file identifier',' ',' ')
+    ENDIF
+  ELSE
+    i_s = 1
+    i_e = nb_fi_mx
+  ENDIF
+!-
+  DO i_f=i_s,i_e
+    f_e = nw_id(i_f)
+    IF (f_e > 0) THEN
+      IF (l_dbg) THEN
+        WRITE(ipslout,*) '  flioclo - closing file number ',i_f
+      ENDIF
+      i_rc = NF90_CLOSE(f_e)
+!- error case added : explicit the message and stop with a fatal error
+      IF (i_rc /= NF90_NOERR) THEN
+        CALL ipslerr (3,'flioclo', &
+ &       'Could not close file','Try again and contact your local administrator ', &
+ &       TRIM(NF90_STRERROR(i_rc))//' (Netcdf)')
+      ENDIF
+      nw_id(i_f) = -1
+    ELSE IF (PRESENT(f_i)) THEN
+      CALL ipslerr (2,'flioclo', &
+ &     'Unable to close the file :','probably','not opened')
+    ENDIF
+  ENDDO
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "<-flioclo"
+  ENDIF
+!---------------------
+END SUBROUTINE flioclo
+!===
+SUBROUTINE fliodmpf (f_n)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*),INTENT(IN) :: f_n
+!-
+  INTEGER :: f_e,n_dims,n_vars,n_atts,i_unlm
+  INTEGER :: i_rc,i_n,k_n,t_ea,l_ea
+  INTEGER :: tmp_i
+  REAL    :: tmp_r
+  INTEGER,DIMENSION(:),ALLOCATABLE :: tma_i
+  REAL,DIMENSION(:),ALLOCATABLE    :: tma_r
+  CHARACTER(LEN=256) :: tmp_c
+  INTEGER,DIMENSION(nb_fd_mx) :: n_idim,n_ldim
+  INTEGER,DIMENSION(nb_ax_mx) :: n_ai
+  CHARACTER(LEN=NF90_MAX_NAME),DIMENSION(nb_fd_mx) :: c_ndim
+  INTEGER,DIMENSION(NF90_MAX_VAR_DIMS) :: idimid
+  CHARACTER(LEN=NF90_MAX_NAME) :: c_name
+!---------------------------------------------------------------------
+  i_rc = NF90_OPEN(TRIM(f_n),NF90_NOWRITE,f_e)
+  IF (i_rc /= NF90_NOERR) THEN
+    CALL ipslerr (3,'fliodmpf', &
+ &   'Could not open file :',TRIM(f_n), &
+ &   TRIM(NF90_STRERROR(i_rc))//' (Netcdf)')
+  ENDIF
+!-
+  WRITE (ipslout,*) "---"
+  WRITE (ipslout,*) "--- File '",TRIM(f_n),"'"
+  WRITE (ipslout,*) "---"
+!-
+  CALL flio_inf &
+ &  (f_e,nb_dims=n_dims,nb_vars=n_vars, &
+ &       nb_atts=n_atts,id_unlm=i_unlm, &
+ &       nn_idm=n_idim,nn_ldm=n_ldim,cc_ndm=c_ndim,nn_aid=n_ai)
+!-
+  WRITE (ipslout,*) 'External model identifier   : ',f_e
+  WRITE (ipslout,*) 'Number of dimensions        : ',n_dims
+  WRITE (ipslout,*) 'Number of variables         : ',n_vars
+  WRITE (ipslout,*) 'ID unlimited                : ',i_unlm
+!-
+  WRITE (ipslout,*) "---"
+  WRITE (ipslout,*) 'Presumed axis dimensions identifiers :'
+  IF (n_ai(k_lon) > 0) THEN
+    WRITE (ipslout,*) 'x axis : ',n_ai(k_lon)
+  ELSE
+    WRITE (ipslout,*) 'x axis : NONE'
+  ENDIF
+  IF (n_ai(k_lat) > 0) THEN
+    WRITE (ipslout,*) 'y axis : ',n_ai(k_lat)
+  ELSE
+    WRITE (ipslout,*) 'y axis : NONE'
+  ENDIF
+  IF (n_ai(k_lev) > 0) THEN
+    WRITE (ipslout,*) 'z axis : ',n_ai(k_lev)
+  ELSE
+    WRITE (ipslout,*) 'z axis : NONE'
+  ENDIF
+  IF (n_ai(k_tim) > 0) THEN
+    WRITE (ipslout,*) 't axis : ',n_ai(k_tim)
+  ELSE
+    WRITE (ipslout,*) 't axis : NONE'
+  ENDIF
+!-
+  WRITE (ipslout,*) "---"
+  WRITE (ipslout,*) 'Number of global attributes : ',n_atts
+  DO k_n=1,n_atts
+    i_rc = NF90_INQ_ATTNAME(f_e,NF90_GLOBAL,k_n,c_name)
+    i_rc = NF90_INQUIRE_ATTRIBUTE(f_e,NF90_GLOBAL,c_name, &
+ &                                xtype=t_ea,len=l_ea)
+    IF      (    (t_ea == NF90_INT4).OR.(t_ea == NF90_INT2) &
+             .OR.(t_ea == NF90_INT1) ) THEN
+      IF (l_ea > 1) THEN
+        ALLOCATE(tma_i(l_ea))
+        i_rc = NF90_GET_ATT(f_e,NF90_GLOBAL,c_name,tma_i)
+        WRITE (ipslout,'("    ",A," :",/,(5(1X,I10),:))') &
+ &        TRIM(c_name),tma_i(1:l_ea)
+        DEALLOCATE(tma_i)
+      ELSE
+        i_rc = NF90_GET_ATT(f_e,NF90_GLOBAL,c_name,tmp_i)
+        WRITE(ipslout,*) '   ',TRIM(c_name),' : ',tmp_i
+      ENDIF
+    ELSE IF ( (t_ea == NF90_REAL4).OR.(t_ea == NF90_REAL8) ) THEN
+      IF (l_ea > 1) THEN
+        ALLOCATE(tma_r(l_ea))
+        i_rc = NF90_GET_ATT(f_e,NF90_GLOBAL,c_name,tma_r)
+        WRITE (ipslout,'("    ",A," :",/,(5(1X,1PE11.3),:))') &
+ &        TRIM(c_name),tma_r(1:l_ea)
+        DEALLOCATE(tma_r)
+      ELSE
+        i_rc = NF90_GET_ATT(f_e,NF90_GLOBAL,c_name,tmp_r)
+        WRITE(ipslout,*) '   ',TRIM(c_name),' : ',tmp_r
+      ENDIF
+    ELSE
+      tmp_c = ''
+      i_rc = NF90_GET_ATT(f_e,NF90_GLOBAL,c_name,tmp_c)
+      WRITE(ipslout,*) '   ',TRIM(c_name),' : "',TRIM(tmp_c),'"'
+    ENDIF
+  ENDDO
+!-
+  DO i_n=1,nb_fd_mx
+    IF (n_idim(i_n) > 0) THEN
+      WRITE (ipslout,*) "---"
+      WRITE (ipslout,*) 'Dimension id   : ',n_idim(i_n)
+      WRITE (ipslout,*) 'Dimension name : ',TRIM(c_ndim(i_n))
+      WRITE (ipslout,*) 'Dimension size : ',n_ldim(i_n)
+    ENDIF
+  ENDDO
+!-
+  DO i_n=1,n_vars
+    i_rc = NF90_INQUIRE_VARIABLE(f_e,i_n, &
+ &           name=c_name,ndims=n_dims,dimids=idimid,nAtts=n_atts)
+    WRITE (ipslout,*) "---"
+    WRITE (ipslout,*) "Variable name        : ",TRIM(c_name)
+    WRITE (ipslout,*) "Variable identifier  : ",i_n
+    WRITE (ipslout,*) "Number of dimensions : ",n_dims
+    IF (n_dims > 0) THEN
+      WRITE (ipslout,*) "Dimensions ID's      : ",idimid(1:n_dims)
+    ENDIF
+    WRITE (ipslout,*) "Number of attributes : ",n_atts
+    DO k_n=1,n_atts
+      i_rc = NF90_INQ_ATTNAME(f_e,i_n,k_n,c_name)
+      i_rc = NF90_INQUIRE_ATTRIBUTE(f_e,i_n,c_name, &
+ &                                  xtype=t_ea,len=l_ea)
+      IF      (    (t_ea == NF90_INT4).OR.(t_ea == NF90_INT2) &
+ &             .OR.(t_ea == NF90_INT1) ) THEN
+        IF (l_ea > 1) THEN
+          ALLOCATE(tma_i(l_ea))
+          i_rc = NF90_GET_ATT(f_e,i_n,c_name,tma_i)
+          WRITE (ipslout,'("    ",A," :",/,(5(1X,I10),:))') &
+ &              TRIM(c_name),tma_i(1:l_ea)
+          DEALLOCATE(tma_i)
+        ELSE
+          i_rc = NF90_GET_ATT(f_e,i_n,c_name,tmp_i)
+          WRITE(ipslout,*) '   ',TRIM(c_name),' : ',tmp_i
+        ENDIF
+      ELSE IF ( (t_ea == NF90_REAL4).OR.(t_ea == NF90_REAL8) ) THEN
+        IF (l_ea > 1) THEN
+          ALLOCATE(tma_r(l_ea))
+          i_rc = NF90_GET_ATT(f_e,i_n,c_name,tma_r)
+          WRITE (ipslout,'("    ",A," :",/,(5(1X,1PE11.3),:))') &
+ &          TRIM(c_name),tma_r(1:l_ea)
+          DEALLOCATE(tma_r)
+        ELSE
+          i_rc = NF90_GET_ATT(f_e,i_n,c_name,tmp_r)
+          WRITE(ipslout,*) '   ',TRIM(c_name),' : ',tmp_r
+        ENDIF
+      ELSE
+        tmp_c = ''
+        i_rc = NF90_GET_ATT(f_e,i_n,c_name,tmp_c)
+        WRITE(ipslout,*) '   ',TRIM(c_name),' : "',TRIM(tmp_c),'"'
+      ENDIF
+    ENDDO
+  ENDDO
+  WRITE (ipslout,*) "---"
+!-
+  i_rc = NF90_CLOSE(f_e)
+!----------------------
+END SUBROUTINE fliodmpf
+!===
+SUBROUTINE flio_dom_set &
+ & (dtnb,dnb,did,dsg,dsl,dpf,dpl,dhs,dhe,cdnm,id_dom)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: dtnb,dnb
+  INTEGER,DIMENSION(:),INTENT(IN) :: did,dsg,dsl,dpf,dpl,dhs,dhe
+  CHARACTER(LEN=*),INTENT(IN) :: cdnm
+  INTEGER,INTENT(OUT) :: id_dom
+!-
+  INTEGER :: k_w,i_w,i_s
+  CHARACTER(LEN=l_dns) :: cd_p,cd_w
+!---------------------------------------------------------------------
+  k_w = flio_dom_rid()
+  IF (k_w < 0) THEN
+    CALL ipslerr (3,'flio_dom_set', &
+ &   'too many domains simultaneously defined', &
+ &   'please unset useless domains', &
+ &   'by calling flio_dom_unset')
+  ENDIF
+  id_dom = k_w
+!-
+  d_n_t(k_w) = dtnb
+  d_n_c(k_w) = dnb
+!-
+  i_s = SIZE(did)
+  IF (i_s > dom_max_dims) THEN
+    CALL ipslerr (3,'flio_dom_set', &
+ &   'too many distributed dimensions', &
+ &   'simultaneously defined',' ')
+  ENDIF
+  d_d_n(k_w) = i_s
+  d_d_i(1:i_s,k_w) = did(1:i_s)
+!-
+  i_w = SIZE(dsg)
+  IF (i_w /= i_s) THEN
+    CALL ipslerr (3,'flio_dom_set', &
+ &   'the size of the DOMAIN_size_global array', &
+ &   'is not equal to the size', &
+ &   'of the distributed dimensions array')
+  ENDIF
+  d_s_g(1:i_w,k_w) = dsg(1:i_w)
+!-
+  i_w = SIZE(dsl)
+  IF (i_w /= i_s) THEN
+    CALL ipslerr (3,'flio_dom_set', &
+ &   'the size of the DOMAIN_size_local array', &
+ &   'is not equal to the size', &
+ &   'of the distributed dimensions array')
+  ENDIF
+  d_s_l(1:i_w,k_w) = dsl(1:i_w)
+!-
+  i_w = SIZE(dpf)
+  IF (i_w /= i_s) THEN
+    CALL ipslerr (3,'flio_dom_set', &
+ &   'the size of the DOMAIN_position_first array', &
+ &   'is not equal to the size', &
+ &   'of the distributed dimensions array')
+  ENDIF
+  d_p_f(1:i_w,k_w) = dpf(1:i_w)
+!-
+  i_w = SIZE(dpl)
+  IF (i_w /= i_s) THEN
+    CALL ipslerr (3,'flio_dom_set', &
+ &   'the size of the DOMAIN_position_last array', &
+ &   'is not equal to the size', &
+ &   'of the distributed dimensions array')
+  ENDIF
+  d_p_l(1:i_w,k_w) = dpl(1:i_w)
+!-
+  i_w = SIZE(dhs)
+  IF (i_w /= i_s) THEN
+    CALL ipslerr (3,'flio_dom_set', &
+ &   'the size of the DOMAIN_halo_size_start array', &
+ &   'is not equal to the size', &
+ &   'of the distributed dimensions array')
+  ENDIF
+  d_h_s(1:i_w,k_w) = dhs(1:i_w)
+!-
+  i_w = SIZE(dhe)
+  IF (i_w /= i_s) THEN
+    CALL ipslerr (3,'flio_dom_set', &
+ &   'the size of the DOMAIN_halo_size_end array', &
+ &   'is not equal to the size', &
+ &   'of the distributed dimensions array')
+  ENDIF
+  d_h_e(1:i_w,k_w) = dhe(1:i_w)
+!-
+  cd_p = "unknown"
+  cd_w = cdnm; CALL strlowercase (cd_w)
+  DO i_w=1,n_dns
+    IF (TRIM(cd_w) == TRIM(c_dns(i_w))) THEN
+      cd_p = cd_w; EXIT;
+    ENDIF
+  ENDDO
+  IF (TRIM(cd_p) == "unknown") THEN
+    CALL ipslerr (3,'flio_dom_set', &
+ &   'DOMAIN_type "'//TRIM(cdnm)//'"', &
+ &   'is actually not supported', &
+ &   'please use one of the supported names')
+  ENDIF
+  c_d_t(k_w) = cd_p
+!--------------------------
+END SUBROUTINE flio_dom_set
+!===
+SUBROUTINE flio_dom_unset (id_dom)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN),OPTIONAL :: id_dom
+!-
+  INTEGER :: i_w
+!---------------------------------------------------------------------
+  IF (PRESENT(id_dom)) THEN
+    IF ( (id_dom >= 1).AND.(id_dom <= dom_max_nb) ) THEN
+      IF (d_d_n(id_dom) > 0) THEN
+        d_d_n(id_dom) = -1
+      ELSE
+        CALL ipslerr (2,'flio_dom_unset', &
+ &       'The domain is not set',' ',' ')
+      ENDIF
+    ELSE
+      CALL ipslerr (2,'flio_dom_unset', &
+ &     'Invalid file identifier',' ',' ')
+    ENDIF
+  ELSE
+    DO i_w=1,dom_max_nb
+      d_d_n(id_dom) = -1
+    ENDDO
+  ENDIF
+!----------------------------
+END SUBROUTINE flio_dom_unset
+!===
+SUBROUTINE flio_dom_defset (id_dom)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: id_dom
+!---------------------------------------------------------------------
+  IF ( (id_dom >= 1).AND.(id_dom <= dom_max_nb) ) THEN
+    id_def_dom = id_dom
+  ELSE
+    CALL ipslerr (3,'flio_dom_defset', &
+ &   'Invalid domain identifier',' ',' ')
+  ENDIF
+!-----------------------------
+END SUBROUTINE flio_dom_defset
+!===
+SUBROUTINE flio_dom_defunset ()
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!---------------------------------------------------------------------
+  id_def_dom = FLIO_DOM_NONE
+!-------------------------------
+END SUBROUTINE flio_dom_defunset
+!===
+SUBROUTINE flio_dom_definq (id_dom)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(OUT) :: id_dom
+!---------------------------------------------------------------------
+  id_dom = id_def_dom
+!-----------------------------
+END SUBROUTINE flio_dom_definq
+!===
+!-
+!---------------------------------------------------------------------
+!- Semi-public procedures
+!---------------------------------------------------------------------
+!-
+!===
+SUBROUTINE flio_dom_file (f_n,id_dom)
+!---------------------------------------------------------------------
+!- Update the model file name to include the ".nc" suffix and
+!- the DOMAIN number on which this copy of IOIPSL runs, if needed.
+!- This routine is called by IOIPSL and not by user anyway.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*),INTENT(INOUT) :: f_n
+  INTEGER,OPTIONAL,INTENT(IN) :: id_dom
+!-
+  INTEGER :: il,iw
+  CHARACTER(LEN=4) :: str
+!---------------------------------------------------------------------
+!-
+! Add the ".nc" suffix if needed
+  il = LEN_TRIM(f_n)
+  IF (f_n(il-2:il) /= '.nc') THEN
+    f_n = f_n(1:il)//'.nc'
+  ENDIF
+!-
+! Add the DOMAIN identifier if needed
+  IF (PRESENT(id_dom)) THEN
+    IF (id_dom == FLIO_DOM_DEFAULT) THEN
+      CALL flio_dom_definq (iw)
+    ELSE
+      iw = id_dom
+    ENDIF
+    IF (iw /= FLIO_DOM_NONE) THEN
+      IF ( (id_dom >= 1).AND.(id_dom <= dom_max_nb) ) THEN
+        IF (d_d_n(iw) > 0) THEN
+          WRITE(str,'(I4.4)') d_n_c(iw)
+          il = INDEX(f_n,'.nc')
+          f_n = f_n(1:il-1)//'_'//str//'.nc'
+        ELSE
+          CALL ipslerr (3,'flio_dom_file', &
+ &         'The domain has not been defined', &
+ &         'please call flio_dom_set', &
+ &         'before calling flio_dom_file')
+        ENDIF
+      ELSE
+        CALL ipslerr (3,'flio_dom_file', &
+ &       'Invalid domain identifier',' ',' ')
+      ENDIF
+    ENDIF
+  ENDIF
+!---------------------------
+END SUBROUTINE flio_dom_file
+!===
+SUBROUTINE flio_dom_att (f_e,id_dom)
+!---------------------------------------------------------------------
+!- Add the DOMAIN attributes to the NETCDF file.
+!- This routine is called by IOIPSL and not by user anyway.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(in) :: f_e
+  INTEGER,OPTIONAL,INTENT(IN) :: id_dom
+!-
+  INTEGER :: iw,i_rc,i_n
+  CHARACTER(LEN=15) :: c_ddim
+  INTEGER :: n_idim
+  CHARACTER(LEN=NF90_MAX_NAME) :: c_ndim
+!---------------------------------------------------------------------
+  IF (PRESENT(id_dom)) THEN
+    IF (id_dom == FLIO_DOM_DEFAULT) THEN
+      CALL flio_dom_definq (iw)
+    ELSE
+      iw = id_dom
+    ENDIF
+    IF (iw /= FLIO_DOM_NONE) THEN
+      IF ( (id_dom >= 1).AND.(id_dom <= dom_max_nb) ) THEN
+        IF (d_d_n(iw) > 0) THEN
+          i_rc = NF90_PUT_ATT (f_e,NF90_GLOBAL, &
+ &          'DOMAIN_number_total',d_n_t(iw))
+          i_rc = NF90_PUT_ATT (f_e,NF90_GLOBAL, &
+ &          'DOMAIN_number',d_n_c(iw))
+          i_rc = NF90_PUT_ATT (f_e,NF90_GLOBAL, &
+ &          'DOMAIN_dimensions_ids',d_d_i(1:d_d_n(iw),iw))
+          i_rc = NF90_PUT_ATT (f_e,NF90_GLOBAL, &
+ &          'DOMAIN_size_global',d_s_g(1:d_d_n(iw),iw))
+          i_rc = NF90_PUT_ATT (f_e,NF90_GLOBAL, &
+ &          'DOMAIN_size_local',d_s_l(1:d_d_n(iw),iw))
+          i_rc = NF90_PUT_ATT (f_e,NF90_GLOBAL, &
+ &          'DOMAIN_position_first',d_p_f(1:d_d_n(iw),iw))
+          i_rc = NF90_PUT_ATT (f_e,NF90_GLOBAL, &
+ &          'DOMAIN_position_last',d_p_l(1:d_d_n(iw),iw))
+          i_rc = NF90_PUT_ATT (f_e,NF90_GLOBAL, &
+ &          'DOMAIN_halo_size_start',d_h_s(1:d_d_n(iw),iw))
+          i_rc = NF90_PUT_ATT (f_e,NF90_GLOBAL, &
+ &          'DOMAIN_halo_size_end',d_h_e(1:d_d_n(iw),iw))
+          i_rc = NF90_PUT_ATT (f_e,NF90_GLOBAL, &
+ &          'DOMAIN_type',TRIM(c_d_t(iw)))
+          i_rc = NF90_INQUIRE (f_e,nDimensions=n_idim)
+          DO i_n=1,n_idim
+            i_rc = NF90_INQUIRE_DIMENSION (f_e,i_n,name=c_ndim)
+            WRITE (UNIT=c_ddim,FMT='("DOMAIN_DIM_N",I3.3)') i_n
+            i_rc = NF90_PUT_ATT (f_e,NF90_GLOBAL,c_ddim,TRIM(c_ndim))
+          ENDDO
+        ELSE
+          CALL ipslerr (3,'flio_dom_att', &
+ &         'The domain has not been defined', &
+ &         'please call flio_dom_set', &
+ &         'before calling flio_dom_att')
+        ENDIF
+      ELSE
+        CALL ipslerr (3,'flio_dom_att', &
+ &       'Invalid domain identifier',' ',' ')
+      ENDIF
+    ENDIF
+  ENDIF
+!--------------------------
+END SUBROUTINE flio_dom_att
+!===
+!-
+!---------------------------------------------------------------------
+!- Local procedures
+!---------------------------------------------------------------------
+!-
+!===
+INTEGER FUNCTION flio_rid()
+!---------------------------------------------------------------------
+!- returns a free index in nw_id(:)
+!---------------------------------------------------------------------
+  INTEGER,DIMENSION(1:1) :: nfi
+!-
+  IF (ANY(nw_id < 0)) THEN
+    nfi = MINLOC(nw_id,MASK=nw_id < 0)
+    flio_rid = nfi(1)
+  ELSE
+    flio_rid = -1
+  ENDIF
+!--------------------
+END FUNCTION flio_rid
+!===
+INTEGER FUNCTION flio_dom_rid()
+!---------------------------------------------------------------------
+!- returns a free index in d_d_n(:)
+!---------------------------------------------------------------------
+  INTEGER,DIMENSION(1:1) :: nd
+!---------------------------------------------------------------------
+  IF (ANY(d_d_n < 0)) THEN
+    nd = MINLOC(d_d_n,MASK=d_d_n < 0)
+    flio_dom_rid = nd(1)
+  ELSE
+    flio_dom_rid = -1
+  ENDIF
+!------------------------
+END FUNCTION flio_dom_rid
+!===
+INTEGER FUNCTION flio_qid(iid)
+!---------------------------------------------------------------------
+!- returns the external index associated with the internal index "iid"
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: iid
+!---------------------------------------------------------------------
+  IF ( (iid >= 1).AND.(iid <= nb_fi_mx) ) THEN
+    flio_qid = nw_id(iid)
+  ELSE
+    flio_qid = -1
+  ENDIF
+!--------------------
+END FUNCTION flio_qid
+!===
+SUBROUTINE flio_qvid (cpg,iid,ixd)
+!---------------------------------------------------------------------
+!- This subroutine, called by the procedure "cpg",
+!- validates and returns the external file index "ixd"
+!- associated with the internal file index "iid"
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*),INTENT(IN) :: cpg
+  INTEGER,INTENT(IN)  :: iid
+  INTEGER,INTENT(OUT) :: ixd
+!-
+  CHARACTER(LEN=20) :: c_t
+!---------------------------------------------------------------------
+  ixd = flio_qid(iid)
+  IF (ixd < 0) THEN
+    WRITE (UNIT=c_t,FMT='(I15)') iid
+    CALL ipslerr (3,TRIM(cpg), &
+ &    'Invalid internal file index :',TRIM(ADJUSTL(c_t)),' ')
+  ENDIF
+!-----------------------
+END SUBROUTINE flio_qvid
+!===
+SUBROUTINE flio_hdm (f_i,f_e,lk_hm)
+!---------------------------------------------------------------------
+!- This subroutine handles the "define/data mode" of NETCDF.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_i,f_e
+  LOGICAL,INTENT(IN) :: lk_hm
+!-
+  INTEGER :: i_rc
+!---------------------------------------------------------------------
+  i_rc = NF90_NOERR
+!-
+  IF      ( (.NOT.lw_hm(f_i)).AND.(lk_hm) ) THEN
+    i_rc = NF90_REDEF(f_e)
+    lw_hm(f_i) = .TRUE.
+  ELSE IF ( (lw_hm(f_i)).AND.(.NOT.lk_hm) ) THEN
+    i_rc = NF90_ENDDEF(f_e)
+    lw_hm(f_i) = .FALSE.
+  ENDIF
+!-
+  IF (i_rc /= NF90_NOERR) THEN
+    CALL ipslerr (3,'flio_hdm', &
+ &    'Internal error ','in define/data mode :', &
+ &    TRIM(NF90_STRERROR(i_rc)))
+  ENDIF
+!----------------------
+END SUBROUTINE flio_hdm
+!===
+SUBROUTINE flio_inf (f_e, &
+ & nb_dims,nb_vars,nb_atts,id_unlm,nn_idm,nn_ldm,nn_aid,cc_ndm)
+!---------------------------------------------------------------------
+!- This subroutine allows to get some information concerning
+!- the model file whose the external identifier is "f_e".
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: f_e
+  INTEGER,OPTIONAL,INTENT(OUT) :: nb_dims,nb_vars,nb_atts,id_unlm
+  INTEGER,DIMENSION(:),OPTIONAL,INTENT(OUT) :: nn_idm,nn_ldm,nn_aid
+  CHARACTER(LEN=*),DIMENSION(:),OPTIONAL,INTENT(OUT) :: cc_ndm
+!-
+  INTEGER :: nm_dims,nm_vars,nm_atts,nm_unlm,ml
+  INTEGER :: i_rc,kv
+  CHARACTER(LEN=NF90_MAX_NAME) :: f_d_n
+!-
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "->flio_inf"
+  ENDIF
+!-
+  i_rc = NF90_INQUIRE(f_e,nDimensions=nm_dims,nVariables=nm_vars, &
+ &                    nAttributes=nm_atts,unlimitedDimId=nm_unlm)
+!-
+  IF (PRESENT(nb_dims))  nb_dims = nm_dims;
+  IF (PRESENT(nb_vars))  nb_vars = nm_vars;
+  IF (PRESENT(nb_atts))  nb_atts = nm_atts;
+  IF (PRESENT(id_unlm))  id_unlm = nm_unlm;
+!-
+  IF (PRESENT(nn_idm))  nn_idm(:) =  -1;
+  IF (PRESENT(nn_ldm))  nn_ldm(:) =   0;
+  IF (PRESENT(cc_ndm))  cc_ndm(:) = ' ';
+  IF (PRESENT(nn_aid))  nn_aid(:) =  -1;
+!-
+  DO kv=1,nm_dims
+!---
+    i_rc = NF90_INQUIRE_DIMENSION(f_e,kv,name=f_d_n,len=ml)
+    CALL strlowercase (f_d_n)
+    f_d_n = ADJUSTL(f_d_n)
+!---
+    IF (l_dbg) THEN
+      WRITE(ipslout,*) "  flio_inf ",kv,ml," ",TRIM(f_d_n)
+    ENDIF
+!---
+    IF (PRESENT(nn_idm))  nn_idm(kv)=kv;
+    IF (PRESENT(nn_ldm))  nn_ldm(kv)=ml;
+    IF (PRESENT(cc_ndm))  cc_ndm(kv)=TRIM(f_d_n);
+!---
+    IF      (    (INDEX(f_d_n,'x') == 1)   &
+ &           .OR.(INDEX(f_d_n,'lon') == 1) ) THEN
+      IF (PRESENT(nn_aid)) THEN
+        IF (nn_aid(k_lon) < 0) THEN
+          nn_aid(k_lon)=kv;
+        ENDIF
+      ENDIF
+    ELSE IF (    (INDEX(f_d_n,'y') == 1)   &
+ &           .OR.(INDEX(f_d_n,'lat') == 1) ) THEN
+      IF (PRESENT(nn_aid)) THEN
+        IF (nn_aid(k_lat) < 0) THEN
+          nn_aid(k_lat)=kv;
+        ENDIF
+      ENDIF
+    ELSE IF (    (INDEX(f_d_n,'z') == 1)     &
+ &           .OR.(INDEX(f_d_n,'lev') == 1)   &
+ &           .OR.(INDEX(f_d_n,'plev') == 1)  &
+ &           .OR.(INDEX(f_d_n,'depth') == 1) ) THEN
+      IF (PRESENT(nn_aid)) THEN
+        IF (nn_aid(k_lev) < 0) THEN
+          nn_aid(k_lev)=kv;
+        ENDIF
+      ENDIF
+    ELSE IF (    (TRIM(f_d_n) == 't')         &
+ &           .OR.(TRIM(f_d_n) == 'time')      &
+ &           .OR.(INDEX(f_d_n,'tstep') == 1)  &
+ &           .OR.(INDEX(f_d_n,'time_counter') == 1) ) THEN
+!---- For the time we certainly need to allow for other names
+      IF (PRESENT(nn_aid)) THEN
+        IF (nn_aid(k_tim) < 0) THEN
+          nn_aid(k_tim)=kv;
+        ENDIF
+      ENDIF
+    ENDIF
+!---
+  ENDDO
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "<-flio_inf"
+  ENDIF
+!----------------------
+END SUBROUTINE flio_inf
+!===
+SUBROUTINE flio_qax (f_i,axtype,i_v,nbd)
+!---------------------------------------------------------------------
+!- This subroutine explores the file in order to find
+!- an axis (x/y/z/t) according to a number of rules
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: f_i,i_v,nbd
+  CHARACTER(LEN=*) :: axtype
+!-
+  INTEGER :: kv,k,n_r,l_d,n_d,i_rc,dimnb
+  CHARACTER(LEN=1)  :: c_ax
+  CHARACTER(LEN=18) :: c_sn
+  CHARACTER(LEN=15),DIMENSION(10) :: c_r
+  CHARACTER(LEN=40) :: c_t1,c_t2
+!---------------------------------------------------------------------
+  i_v = -1; nbd = -1;
+!---
+!- Keep the name of the axis
+!---
+  c_ax = TRIM(axtype)
+!-
+! Validate axis type
+!-
+  IF (    (LEN_TRIM(axtype) == 1) &
+ &    .AND.(    (c_ax == 'x').OR.(c_ax == 'y') &
+ &          .OR.(c_ax == 'z').OR.(c_ax == 't')) ) THEN
+!---
+!-- Define the maximum number of dimensions for the coordinate
+!---
+      SELECT CASE (c_ax)
+      CASE('x')
+        l_d = 2
+        c_sn = 'longitude'
+      CASE('y')
+        l_d = 2
+        c_sn = 'latitude'
+      CASE('z')
+        l_d = 1
+        c_sn = 'model_level_number'
+      CASE('t')
+        l_d = 1
+        c_sn = 'time'
+      END SELECT
+!---
+!-- Rule 1 : we look for a variable with one dimension
+!--          and which has the same name as its dimension (NUG)
+!---
+    IF (i_v < 0) THEN
+      SELECT CASE (c_ax)
+      CASE('x')
+        k = nw_ai(k_lon,f_i)
+      CASE('y')
+        k = nw_ai(k_lat,f_i)
+      CASE('z')
+        k = nw_ai(k_lev,f_i)
+      CASE('t')
+        k = nw_ai(k_tim,f_i)
+      END SELECT
+      IF ( (k >= 1).AND.(k <= nb_ax_mx) ) THEN
+        dimnb = nw_di(k,f_i)
+      ELSE
+        dimnb = -1
+      ENDIF
+!-----
+      i_rc = NF90_INQUIRE_DIMENSION(nw_id(f_i),dimnb,name=c_t1)
+      IF (i_rc == NF90_NOERR) THEN
+        CALL strlowercase (c_t1)
+        L_R1: DO kv=1,nw_nv(f_i)
+          i_rc = NF90_INQUIRE_VARIABLE &
+ &                 (nw_id(f_i),kv,name=c_t2,ndims=n_d)
+          IF (n_d == 1) THEN
+            CALL strlowercase (c_t2)
+            IF (TRIM(c_t1) == TRIM(c_t2)) THEN
+              i_v = kv; nbd = n_d;
+              EXIT L_R1
+            ENDIF
+          ENDIF
+        ENDDO L_R1
+      ENDIF
+    ENDIF
+!---
+!-- Rule 2 : we look for a correct "axis" attribute (CF)
+!---
+    IF (i_v < 0) THEN
+      L_R2: DO kv=1,nw_nv(f_i)
+        i_rc = NF90_GET_ATT(nw_id(f_i),kv,'axis',c_t1)
+        IF (i_rc == NF90_NOERR) THEN
+          CALL strlowercase (c_t1)
+          IF (TRIM(c_t1) == c_ax) THEN
+            i_rc = NF90_INQUIRE_VARIABLE(nw_id(f_i),kv,ndims=n_d)
+            IF (n_d <= l_d) THEN
+              i_v = kv; nbd = n_d;
+              EXIT L_R2
+            ENDIF
+          ENDIF
+        ENDIF
+      ENDDO L_R2
+    ENDIF
+!---
+!-- Rule 3 : we look for a correct "standard_name" attribute (CF)
+!---
+    IF (i_v < 0) THEN
+      L_R3: DO kv=1,nw_nv(f_i)
+        i_rc = NF90_GET_ATT(nw_id(f_i),kv,'standard_name',c_t1)
+        IF (i_rc == NF90_NOERR) THEN
+          CALL strlowercase (c_t1)
+          IF (TRIM(c_t1) == TRIM(c_sn)) THEN
+            i_rc = NF90_INQUIRE_VARIABLE(nw_id(f_i),kv,ndims=n_d)
+            IF (n_d <= l_d) THEN
+              i_v = kv; nbd = n_d;
+              EXIT L_R3
+            ENDIF
+          ENDIF
+        ENDIF
+      ENDDO L_R3
+    ENDIF
+!---
+!-- Rule 4 : we look for a specific name (IOIPSL)
+!---
+    IF (i_v < 0) THEN
+      SELECT CASE (c_ax)
+      CASE('x')
+        n_r = 3
+        c_r(1)='nav_lon'; c_r(2)='lon'; c_r(3)='longitude';
+      CASE('y')
+        n_r = 3
+        c_r(1)='nav_lat'; c_r(2)='lat'; c_r(3)='latitude';
+      CASE('z')
+        n_r = 8
+        c_r(1)='depth'; c_r(2)='deptht'; c_r(3)='height';
+        c_r(4)='level'; c_r(5)='lev'; c_r(6)='plev';
+        c_r(7)='sigma_level'; c_r(8)='layer';
+      CASE('t')
+        n_r = 3
+        c_r(1)='time'; c_r(2)='tstep'; c_r(3)='timesteps';
+      END SELECT
+!-----
+      L_R4: DO kv=1,nw_nv(f_i)
+        i_rc = NF90_INQUIRE_VARIABLE &
+ &               (nw_id(f_i),kv,name=c_t1,ndims=n_d)
+        IF (i_rc == NF90_NOERR) THEN
+          CALL strlowercase (c_t1)
+          IF (n_d <= l_d) THEN
+            DO k=1,n_r
+              IF (TRIM(c_t1) == TRIM(c_r(k))) THEN
+                i_v = kv; nbd = n_d;
+                EXIT L_R4
+              ENDIF
+            ENDDO
+          ENDIF
+        ENDIF
+      ENDDO L_R4
+    ENDIF
+!---
+  ENDIF
+!----------------------
+END SUBROUTINE flio_qax
+!-
+!===
+!-
+END MODULE fliocom
Index: /IOIPSL/tags/v2_2_5/src/getincom.f90
===================================================================
--- /IOIPSL/tags/v2_2_5/src/getincom.f90	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/src/getincom.f90	(revision 5058)
@@ -0,0 +1,2208 @@
+MODULE getincom
+!-
+!$Id$
+!-
+! This software is governed by the CeCILL license
+! See IOIPSL/IOIPSL_License_CeCILL.txt
+!---------------------------------------------------------------------
+USE errioipsl, ONLY : ipslerr,ipsldbg,ipslout
+USE stringop, &
+ &   ONLY : nocomma,cmpblank,strlowercase,nocomment, COMMENT_TAG
+!-
+IMPLICIT NONE
+!-
+PRIVATE
+PUBLIC :: getin_name, getin, getin_dump, getin_dump_para
+!-
+!!--------------------------------------------------------------------
+!! The "getin_name" routine allows the user to change the name
+!! of the definition file in which the data will be read.
+!! ("run.def" by default)
+!!
+!!  SUBROUTINE getin_name (file_name)
+!!
+!! OPTIONAL INPUT argument
+!!
+!! (C) file_name :  the name of the file
+!!                  in which the data will be read
+!!--------------------------------------------------------------------
+!-
+!-
+INTERFACE getin
+!!--------------------------------------------------------------------
+!! The "getin" routines get a variable.
+!! We first check if we find it in the database
+!! and if not we get it from the definition file.
+!!
+!! SUBROUTINE getin (targetname,ret_val)
+!!
+!! INPUT
+!!
+!! (C) targetname : Name of the variable
+!!
+!! OUTPUT
+!!
+!! (I/R/C/L) ret_val : scalar, vector or matrix that will contain
+!!                     that will contain the (standard)
+!!                     integer/real/character/logical values
+!!--------------------------------------------------------------------
+  MODULE PROCEDURE getinrs, getinr1d, getinr2d, &
+ &                 getinis, getini1d, getini2d, &
+ &                 getincs, getinc1d, getinc2d, &
+ &                 getinls, getinl1d, getinl2d
+END INTERFACE
+!-
+!!--------------------------------------------------------------------
+!! The "getin_dump" routine will dump the content of the database
+!! into a file which has the same format as the definition file.
+!! The idea is that the user can see which parameters were used
+!! and re-use the file for another run.
+!!
+!!  SUBROUTINE getin_dump (fileprefix)
+!!
+!! OPTIONAL INPUT argument
+!!
+!! (C) fileprefix : allows the user to change the name of the file
+!!                  in which the data will be archived
+!!--------------------------------------------------------------------
+!-
+!!------------------------------------------------------------------------
+!! Parallel version of getin_dump : user must give a fileprefix and a rank.
+!! It may be usefull to verify that only the root proc is reading the input
+!! def files.
+!!
+!!  SUBROUTINE getin_dump_para (fileprefix,rank)
+!!
+!! INPUT argument
+!!
+!! (C) fileprefix : allows the user to change the name of the file
+!!                  in which the data will be archived
+!! (I) rank : the rank of the parallel process (only 0-999 files will be created)
+!!--------------------------------------------------------------------
+!-
+  INTEGER,PARAMETER :: max_files=100
+  CHARACTER(LEN=100),DIMENSION(max_files),SAVE :: filelist
+  INTEGER,SAVE      :: nbfiles
+!-
+  INTEGER,SAVE :: allread=0
+  CHARACTER(LEN=100),SAVE :: def_file = 'run.def'
+!-
+  INTEGER,PARAMETER :: i_txtslab=1000,l_n=30
+  INTEGER,SAVE :: nb_lines,i_txtsize=0
+  CHARACTER(LEN=100),SAVE,ALLOCATABLE,DIMENSION(:) :: fichier
+  CHARACTER(LEN=l_n),SAVE,ALLOCATABLE,DIMENSION(:) :: targetlist
+  INTEGER,SAVE,ALLOCATABLE,DIMENSION(:) :: fromfile,compline
+!-
+  INTEGER,PARAMETER :: n_d_fmt=5,max_msgs=30
+  CHARACTER(LEN=6),SAVE :: c_i_fmt = '(I5.5)'
+!-
+! The data base of parameters
+!-
+  INTEGER,PARAMETER :: memslabs=300
+  INTEGER,PARAMETER :: compress_lim=20
+!-
+  INTEGER,SAVE :: nb_keys=0
+  INTEGER,SAVE :: keymemsize=0
+!-
+! keystr definition
+! name of a key
+!-
+! keystatus definition
+! keystatus = 1 : Value comes from the file defined by 'def_file'
+! keystatus = 2 : Default value is used
+! keystatus = 3 : Some vector elements were taken from default
+  INTEGER,PARAMETER :: nondefault=1, default=2, vectornondefault=3
+!-
+! keytype definition
+! keytype = 1 : Integer
+! keytype = 2 : Real
+! keytype = 3 : Character
+! keytype = 4 : Logical
+!-
+  INTEGER,PARAMETER :: k_i=1, k_r=2, k_c=3, k_l=4
+!-
+! Allow compression for keys (only for integer and real)
+! keycompress < 0 : not compressed
+! keycompress > 0 : number of repeat of the value
+!-
+TYPE :: t_key
+  CHARACTER(LEN=l_n) :: keystr
+  INTEGER :: keystatus, keytype, keycompress, &
+ &           keyfromfile, keymemstart, keymemlen
+END TYPE t_key
+!-
+  TYPE(t_key),SAVE,ALLOCATABLE,DIMENSION(:) :: key_tab
+!-
+  INTEGER,SAVE,ALLOCATABLE,DIMENSION(:) :: i_mem
+  INTEGER,SAVE :: i_memsize=0, i_mempos=0
+  REAL,SAVE,ALLOCATABLE,DIMENSION(:) :: r_mem
+  INTEGER,SAVE :: r_memsize=0, r_mempos=0
+  CHARACTER(LEN=300),SAVE,ALLOCATABLE,DIMENSION(:) :: c_mem
+  INTEGER,SAVE :: c_memsize=0, c_mempos=0
+  LOGICAL,SAVE,ALLOCATABLE,DIMENSION(:) :: l_mem
+  INTEGER,SAVE :: l_memsize=0, l_mempos=0
+!-
+CONTAINS
+!-
+!=== DEFINITION FILE NAME INTERFACE
+!-
+SUBROUTINE getin_name (cname)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*) :: cname
+!---------------------------------------------------------------------
+  IF (allread == 0) THEN
+    def_file = ADJUSTL(cname)
+  ELSE
+    CALL ipslerr (3,'getin_name', &
+ &   'The name of the database file (any_name.def)', &
+ &   'must be changed *before* any attempt','to read the database.')
+  ENDIF
+!------------------------
+END SUBROUTINE getin_name
+!-
+!=== INTEGER INTERFACE
+!-
+SUBROUTINE getinis (targetname,ret_val)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*) :: targetname
+  INTEGER :: ret_val
+!-
+  INTEGER,DIMENSION(1) :: tmp_ret_val
+  INTEGER :: pos,status=0,fileorig, size_of_in
+!---------------------------------------------------------------------
+!-
+! Do we have this targetname in our database ?
+!-
+  CALL get_findkey (1,targetname,pos)
+!-
+  tmp_ret_val(1) = ret_val
+  size_of_in = SIZE(tmp_ret_val)
+  
+!-
+  IF (pos < 0) THEN
+!-- Get the information out of the file
+    CALL get_fil (targetname,status,fileorig,size_of_in,i_val=tmp_ret_val)
+!-- Put the data into the database
+    CALL get_wdb &
+ &   (targetname,status,fileorig,1,i_val=tmp_ret_val)
+  ELSE
+!-- Get the value out of the database
+    CALL get_rdb (pos,1,targetname,i_val=tmp_ret_val)
+  ENDIF
+  ret_val = tmp_ret_val(1)
+!---------------------
+END SUBROUTINE getinis
+!===
+SUBROUTINE getini1d (targetname,ret_val)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*) :: targetname
+  INTEGER,DIMENSION(:) :: ret_val
+!-
+  INTEGER,DIMENSION(:),ALLOCATABLE,SAVE :: tmp_ret_val
+  INTEGER,SAVE :: tmp_ret_size = 0
+  INTEGER :: pos,size_of_in,status=0,fileorig
+!---------------------------------------------------------------------
+!-
+! Do we have this targetname in our database ?
+!-
+  CALL get_findkey (1,targetname,pos)
+!-
+  size_of_in = SIZE(ret_val)
+  IF (.NOT.ALLOCATED(tmp_ret_val)) THEN
+    ALLOCATE (tmp_ret_val(size_of_in))
+  ELSE IF (size_of_in > tmp_ret_size) THEN
+    DEALLOCATE (tmp_ret_val)
+    ALLOCATE (tmp_ret_val(size_of_in))
+    tmp_ret_size = size_of_in
+  ENDIF
+  tmp_ret_val(1:size_of_in) = ret_val(1:size_of_in)
+!-
+  IF (pos < 0) THEN
+!-- Get the information out of the file
+    CALL get_fil (targetname,status,fileorig,size_of_in,i_val=tmp_ret_val)
+!-- Put the data into the database
+    CALL get_wdb &
+ &   (targetname,status,fileorig,size_of_in,i_val=tmp_ret_val)
+  ELSE
+!-- Get the value out of the database
+    CALL get_rdb (pos,size_of_in,targetname,i_val=tmp_ret_val)
+  ENDIF
+  ret_val(1:size_of_in) = tmp_ret_val(1:size_of_in)
+!----------------------
+END SUBROUTINE getini1d
+!===
+SUBROUTINE getini2d (targetname,ret_val)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*) :: targetname
+  INTEGER,DIMENSION(:,:) :: ret_val
+!-
+  INTEGER,DIMENSION(:),ALLOCATABLE,SAVE :: tmp_ret_val
+  INTEGER,SAVE :: tmp_ret_size = 0
+  INTEGER :: pos,size_of_in,size_1,size_2,status=0,fileorig
+  INTEGER :: jl,jj,ji
+!---------------------------------------------------------------------
+!-
+! Do we have this targetname in our database ?
+!-
+  CALL get_findkey (1,targetname,pos)
+!-
+  size_of_in = SIZE(ret_val)
+  size_1 = SIZE(ret_val,1)
+  size_2 = SIZE(ret_val,2)
+  IF (.NOT.ALLOCATED(tmp_ret_val)) THEN
+    ALLOCATE (tmp_ret_val(size_of_in))
+  ELSE IF (size_of_in > tmp_ret_size) THEN
+    DEALLOCATE (tmp_ret_val)
+    ALLOCATE (tmp_ret_val(size_of_in))
+    tmp_ret_size = size_of_in
+  ENDIF
+!-
+  jl=0
+  DO jj=1,size_2
+    DO ji=1,size_1
+      jl=jl+1
+      tmp_ret_val(jl) = ret_val(ji,jj)
+    ENDDO
+  ENDDO
+!-
+  IF (pos < 0) THEN
+!-- Get the information out of the file
+    CALL get_fil (targetname,status,fileorig,size_of_in,i_val=tmp_ret_val)
+!-- Put the data into the database
+    CALL get_wdb &
+ &   (targetname,status,fileorig,size_of_in,i_val=tmp_ret_val)
+  ELSE
+!-- Get the value out of the database
+    CALL get_rdb (pos,size_of_in,targetname,i_val=tmp_ret_val)
+  ENDIF
+!-
+  jl=0
+  DO jj=1,size_2
+    DO ji=1,size_1
+      jl=jl+1
+      ret_val(ji,jj) = tmp_ret_val(jl)
+    ENDDO
+  ENDDO
+!----------------------
+END SUBROUTINE getini2d
+!-
+!=== REAL INTERFACE
+!-
+SUBROUTINE getinrs (targetname,ret_val)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*) :: targetname
+  REAL :: ret_val
+!-
+  REAL,DIMENSION(1) :: tmp_ret_val
+  INTEGER :: pos,status=0,fileorig, size_of_in
+!---------------------------------------------------------------------
+!-
+! Do we have this targetname in our database ?
+!-
+  CALL get_findkey (1,targetname,pos)
+!-
+  tmp_ret_val(1) = ret_val
+  size_of_in = SIZE(tmp_ret_val)
+!-
+  IF (pos < 0) THEN
+!-- Get the information out of the file
+    CALL get_fil (targetname,status,fileorig,size_of_in,r_val=tmp_ret_val)
+!-- Put the data into the database
+    CALL get_wdb &
+ &   (targetname,status,fileorig,1,r_val=tmp_ret_val)
+  ELSE
+!-- Get the value out of the database
+    CALL get_rdb (pos,1,targetname,r_val=tmp_ret_val)
+  ENDIF
+  ret_val = tmp_ret_val(1)
+!---------------------
+END SUBROUTINE getinrs
+!===
+SUBROUTINE getinr1d (targetname,ret_val)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*) :: targetname
+  REAL,DIMENSION(:) :: ret_val
+!-
+  REAL,DIMENSION(:),ALLOCATABLE,SAVE :: tmp_ret_val
+  INTEGER,SAVE :: tmp_ret_size = 0
+  INTEGER :: pos,size_of_in,status=0,fileorig
+!---------------------------------------------------------------------
+!-
+! Do we have this targetname in our database ?
+!-
+  CALL get_findkey (1,targetname,pos)
+!-
+  size_of_in = SIZE(ret_val)
+  IF (.NOT.ALLOCATED(tmp_ret_val)) THEN
+    ALLOCATE (tmp_ret_val(size_of_in))
+  ELSE IF (size_of_in > tmp_ret_size) THEN
+    DEALLOCATE (tmp_ret_val)
+    ALLOCATE (tmp_ret_val(size_of_in))
+    tmp_ret_size = size_of_in
+  ENDIF
+  tmp_ret_val(1:size_of_in) = ret_val(1:size_of_in)
+!-
+  IF (pos < 0) THEN
+!-- Get the information out of the file
+    CALL get_fil (targetname,status,fileorig,size_of_in,r_val=tmp_ret_val)
+!-- Put the data into the database
+    CALL get_wdb &
+ &   (targetname,status,fileorig,size_of_in,r_val=tmp_ret_val)
+  ELSE
+!-- Get the value out of the database
+    CALL get_rdb (pos,size_of_in,targetname,r_val=tmp_ret_val)
+  ENDIF
+  ret_val(1:size_of_in) = tmp_ret_val(1:size_of_in)
+!----------------------
+
+END SUBROUTINE getinr1d
+!===
+SUBROUTINE getinr2d (targetname,ret_val)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*) :: targetname
+  REAL,DIMENSION(:,:) :: ret_val
+!-
+  REAL,DIMENSION(:),ALLOCATABLE,SAVE :: tmp_ret_val
+  INTEGER,SAVE :: tmp_ret_size = 0
+  INTEGER :: pos,size_of_in,size_1,size_2,status=0,fileorig
+  INTEGER :: jl,jj,ji
+!---------------------------------------------------------------------
+!-
+! Do we have this targetname in our database ?
+!-
+  CALL get_findkey (1,targetname,pos)
+!-
+  size_of_in = SIZE(ret_val)
+  size_1 = SIZE(ret_val,1)
+  size_2 = SIZE(ret_val,2)
+  IF (.NOT.ALLOCATED(tmp_ret_val)) THEN
+    ALLOCATE (tmp_ret_val(size_of_in))
+  ELSE IF (size_of_in > tmp_ret_size) THEN
+    DEALLOCATE (tmp_ret_val)
+    ALLOCATE (tmp_ret_val(size_of_in))
+    tmp_ret_size = size_of_in
+  ENDIF
+!-
+  jl=0
+  DO jj=1,size_2
+    DO ji=1,size_1
+      jl=jl+1
+      tmp_ret_val(jl) = ret_val(ji,jj)
+    ENDDO
+  ENDDO
+!-
+  IF (pos < 0) THEN
+!-- Get the information out of the file
+    CALL get_fil (targetname,status,fileorig,size_of_in,r_val=tmp_ret_val)
+!-- Put the data into the database
+    CALL get_wdb &
+ &   (targetname,status,fileorig,size_of_in,r_val=tmp_ret_val)
+  ELSE
+!-- Get the value out of the database
+    CALL get_rdb (pos,size_of_in,targetname,r_val=tmp_ret_val)
+  ENDIF
+!-
+  jl=0
+  DO jj=1,size_2
+    DO ji=1,size_1
+      jl=jl+1
+      ret_val(ji,jj) = tmp_ret_val(jl)
+    ENDDO
+  ENDDO
+!----------------------
+END SUBROUTINE getinr2d
+!-
+!=== CHARACTER INTERFACE
+!-
+SUBROUTINE getincs (targetname,ret_val)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*) :: targetname
+  CHARACTER(LEN=*) :: ret_val
+!-
+  CHARACTER(LEN=:),ALLOCATABLE,DIMENSION(:) :: tmp_ret_val
+  INTEGER :: pos,status=0,fileorig,size_of_in,ier
+!---------------------------------------------------------------------
+!-
+  ALLOCATE(CHARACTER(LEN(ret_val)) :: tmp_ret_val(1), stat=ier)
+  IF (ier /= 0) CALL ipslerr(3, 'getincs', 'Allocation memory problem for', &
+                             'tmp_ret_val' ,'')
+!-
+! Do we have this targetname in our database ?
+!-
+  CALL get_findkey (1,targetname,pos)
+!-
+  tmp_ret_val(1) = ret_val
+  size_of_in = SIZE(tmp_ret_val)
+!-
+  IF (pos < 0) THEN
+!-- Get the information out of the file
+    CALL get_fil (targetname,status,fileorig,size_of_in,c_val=tmp_ret_val)
+!-- Put the data into the database
+    CALL get_wdb &
+ &   (targetname,status,fileorig,1,c_val=tmp_ret_val)
+  ELSE
+!-- Get the value out of the database
+    CALL get_rdb (pos,1,targetname,c_val=tmp_ret_val)
+  ENDIF
+  ret_val = tmp_ret_val(1)
+!--
+  DEALLOCATE(tmp_ret_val)
+!---------------------
+END SUBROUTINE getincs
+!===
+SUBROUTINE getinc1d (targetname,ret_val)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*) :: targetname
+  CHARACTER(LEN=*),DIMENSION(:) :: ret_val
+!-
+  CHARACTER(LEN=100),DIMENSION(:),ALLOCATABLE,SAVE :: tmp_ret_val
+  INTEGER,SAVE :: tmp_ret_size = 0
+  INTEGER :: pos,size_of_in,status=0,fileorig
+!---------------------------------------------------------------------
+!-
+! Do we have this targetname in our database ?
+!-
+  CALL get_findkey (1,targetname,pos)
+!-
+  size_of_in = SIZE(ret_val)
+  IF (.NOT.ALLOCATED(tmp_ret_val)) THEN
+    ALLOCATE (tmp_ret_val(size_of_in))
+  ELSE IF (size_of_in > tmp_ret_size) THEN
+    DEALLOCATE (tmp_ret_val)
+    ALLOCATE (tmp_ret_val(size_of_in))
+    tmp_ret_size = size_of_in
+  ENDIF
+  tmp_ret_val(1:size_of_in) = ret_val(1:size_of_in)
+!-
+  IF (pos < 0) THEN
+!-- Get the information out of the file
+    CALL get_fil (targetname,status,fileorig,size_of_in,c_val=tmp_ret_val)
+!-- Put the data into the database
+    CALL get_wdb &
+ &   (targetname,status,fileorig,size_of_in,c_val=tmp_ret_val)
+  ELSE
+!-- Get the value out of the database
+    CALL get_rdb (pos,size_of_in,targetname,c_val=tmp_ret_val)
+  ENDIF
+  ret_val(1:size_of_in) = tmp_ret_val(1:size_of_in)
+!----------------------
+END SUBROUTINE getinc1d
+!===
+SUBROUTINE getinc2d (targetname,ret_val)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*) :: targetname
+  CHARACTER(LEN=*),DIMENSION(:,:) :: ret_val
+!-
+  CHARACTER(LEN=100),DIMENSION(:),ALLOCATABLE,SAVE :: tmp_ret_val
+  INTEGER,SAVE :: tmp_ret_size = 0
+  INTEGER :: pos,size_of_in,size_1,size_2,status=0,fileorig
+  INTEGER :: jl,jj,ji
+!---------------------------------------------------------------------
+!-
+! Do we have this targetname in our database ?
+!-
+  CALL get_findkey (1,targetname,pos)
+!-
+  size_of_in = SIZE(ret_val)
+  size_1 = SIZE(ret_val,1)
+  size_2 = SIZE(ret_val,2)
+  IF (.NOT.ALLOCATED(tmp_ret_val)) THEN
+    ALLOCATE (tmp_ret_val(size_of_in))
+  ELSE IF (size_of_in > tmp_ret_size) THEN
+    DEALLOCATE (tmp_ret_val)
+    ALLOCATE (tmp_ret_val(size_of_in))
+    tmp_ret_size = size_of_in
+  ENDIF
+!-
+  jl=0
+  DO jj=1,size_2
+    DO ji=1,size_1
+      jl=jl+1
+      tmp_ret_val(jl) = ret_val(ji,jj)
+    ENDDO
+  ENDDO
+!-
+  IF (pos < 0) THEN
+!-- Get the information out of the file
+    CALL get_fil (targetname,status,fileorig,size_of_in,c_val=tmp_ret_val)
+!-- Put the data into the database
+    CALL get_wdb &
+ &   (targetname,status,fileorig,size_of_in,c_val=tmp_ret_val)
+  ELSE
+!-- Get the value out of the database
+    CALL get_rdb (pos,size_of_in,targetname,c_val=tmp_ret_val)
+  ENDIF
+!-
+  jl=0
+  DO jj=1,size_2
+    DO ji=1,size_1
+      jl=jl+1
+      ret_val(ji,jj) = tmp_ret_val(jl)
+    ENDDO
+  ENDDO
+!----------------------
+END SUBROUTINE getinc2d
+!-
+!=== LOGICAL INTERFACE
+!-
+SUBROUTINE getinls (targetname,ret_val)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*) :: targetname
+  LOGICAL :: ret_val
+!-
+  LOGICAL,DIMENSION(1) :: tmp_ret_val
+  INTEGER :: pos,status=0,fileorig,size_of_in
+!---------------------------------------------------------------------
+!-
+! Do we have this targetname in our database ?
+!-
+  CALL get_findkey (1,targetname,pos)
+!-
+  tmp_ret_val(1) = ret_val
+  size_of_in = SIZE(tmp_ret_val)
+!-
+  IF (pos < 0) THEN
+!-- Get the information out of the file
+    CALL get_fil (targetname,status,fileorig,size_of_in,l_val=tmp_ret_val)
+!-- Put the data into the database
+    CALL get_wdb &
+ &   (targetname,status,fileorig,1,l_val=tmp_ret_val)
+  ELSE
+!-- Get the value out of the database
+    CALL get_rdb (pos,1,targetname,l_val=tmp_ret_val)
+  ENDIF
+  ret_val = tmp_ret_val(1)
+!---------------------
+END SUBROUTINE getinls
+!===
+SUBROUTINE getinl1d (targetname,ret_val)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*) :: targetname
+  LOGICAL,DIMENSION(:) :: ret_val
+!-
+  LOGICAL,DIMENSION(:),ALLOCATABLE,SAVE :: tmp_ret_val
+  INTEGER,SAVE :: tmp_ret_size = 0
+  INTEGER :: pos,size_of_in,status=0,fileorig
+!---------------------------------------------------------------------
+!-
+! Do we have this targetname in our database ?
+!-
+  CALL get_findkey (1,targetname,pos)
+!-
+  size_of_in = SIZE(ret_val)
+  IF (.NOT.ALLOCATED(tmp_ret_val)) THEN
+    ALLOCATE (tmp_ret_val(size_of_in))
+  ELSE IF (size_of_in > tmp_ret_size) THEN
+    DEALLOCATE (tmp_ret_val)
+    ALLOCATE (tmp_ret_val(size_of_in))
+    tmp_ret_size = size_of_in
+  ENDIF
+  tmp_ret_val(1:size_of_in) = ret_val(1:size_of_in)
+!-
+  IF (pos < 0) THEN
+!-- Get the information out of the file
+    CALL get_fil (targetname,status,fileorig,size_of_in,l_val=tmp_ret_val)
+!-- Put the data into the database
+    CALL get_wdb &
+ &   (targetname,status,fileorig,size_of_in,l_val=tmp_ret_val)
+  ELSE
+!-- Get the value out of the database
+    CALL get_rdb (pos,size_of_in,targetname,l_val=tmp_ret_val)
+  ENDIF
+  ret_val(1:size_of_in) = tmp_ret_val(1:size_of_in)
+!----------------------
+END SUBROUTINE getinl1d
+!===
+SUBROUTINE getinl2d (targetname,ret_val)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*) :: targetname
+  LOGICAL,DIMENSION(:,:) :: ret_val
+!-
+  LOGICAL,DIMENSION(:),ALLOCATABLE,SAVE :: tmp_ret_val
+  INTEGER,SAVE :: tmp_ret_size = 0
+  INTEGER :: pos,size_of_in,size_1,size_2,status=0,fileorig
+  INTEGER :: jl,jj,ji
+!---------------------------------------------------------------------
+!-
+! Do we have this targetname in our database ?
+!-
+  CALL get_findkey (1,targetname,pos)
+!-
+  size_of_in = SIZE(ret_val)
+  size_1 = SIZE(ret_val,1)
+  size_2 = SIZE(ret_val,2)
+  IF (.NOT.ALLOCATED(tmp_ret_val)) THEN
+    ALLOCATE (tmp_ret_val(size_of_in))
+  ELSE IF (size_of_in > tmp_ret_size) THEN
+    DEALLOCATE (tmp_ret_val)
+    ALLOCATE (tmp_ret_val(size_of_in))
+    tmp_ret_size = size_of_in
+  ENDIF
+!-
+  jl=0
+  DO jj=1,size_2
+    DO ji=1,size_1
+      jl=jl+1
+      tmp_ret_val(jl) = ret_val(ji,jj)
+    ENDDO
+  ENDDO
+!-
+  IF (pos < 0) THEN
+!-- Get the information out of the file
+    CALL get_fil (targetname,status,fileorig,size_of_in,l_val=tmp_ret_val)
+!-- Put the data into the database
+    CALL get_wdb &
+ &   (targetname,status,fileorig,size_of_in,l_val=tmp_ret_val)
+  ELSE
+!-- Get the value out of the database
+    CALL get_rdb (pos,size_of_in,targetname,l_val=tmp_ret_val)
+  ENDIF
+!-
+  jl=0
+  DO jj=1,size_2
+    DO ji=1,size_1
+      jl=jl+1
+      ret_val(ji,jj) = tmp_ret_val(jl)
+    ENDDO
+  ENDDO
+!----------------------
+END SUBROUTINE getinl2d
+!-
+!=== Generic file/database INTERFACE
+!-
+SUBROUTINE get_fil (targetname,status,fileorig,nb_to_ret,i_val,r_val,c_val,l_val)
+!---------------------------------------------------------------------
+!- Subroutine that will extract from the file the values
+!- attributed to the keyword targetname
+!-
+!- (C) targetname    : target for which we will look in the file
+!- (I) status    : tells us from where we obtained the data
+!- (I) fileorig  : index of the file from which the key comes
+!- (I) nb_to_ret : size of output vector
+!- (I) i_val(:)  : INTEGER(nb_to_ret)   values
+!- (R) r_val(:)  : REAL(nb_to_ret)      values
+!- (L) l_val(:)  : LOGICAL(nb_to_ret)   values
+!- (C) c_val(:)  : CHARACTER(nb_to_ret) values
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*) :: targetname
+  INTEGER,INTENT(IN) :: nb_to_ret
+  INTEGER,INTENT(OUT) :: status,fileorig
+  INTEGER,DIMENSION(:),OPTIONAL          :: i_val
+  REAL,DIMENSION(:),OPTIONAL             :: r_val
+  LOGICAL,DIMENSION(:),OPTIONAL          :: l_val
+  CHARACTER(LEN=*),DIMENSION(:),OPTIONAL :: c_val
+!-
+  INTEGER :: k_typ,it,pos,len_str,status_cnt,io_err
+  CHARACTER(LEN=n_d_fmt)  :: cnt
+  CHARACTER(LEN=:), ALLOCATABLE :: str_READ,str_READ_lower
+  CHARACTER(LEN=9)  :: c_vtyp
+  LOGICAL,DIMENSION(:),ALLOCATABLE :: found
+  LOGICAL :: def_beha,compressed
+  CHARACTER(LEN=10) :: c_fmt
+  INTEGER :: i_cmpval
+  REAL    :: r_cmpval
+  INTEGER :: ipos_tr,ipos_fl
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!---------------------------------------------------------------------
+!-
+! Get the type of the argument
+  CALL get_qtyp (k_typ,c_vtyp,i_val,r_val,c_val,l_val)
+  IF ( (k_typ.NE.k_i) .AND. (k_typ.NE.k_r) .AND. (k_typ.NE.k_c) .AND. (k_typ.NE.k_l) ) THEN
+    CALL ipslerr (3,'get_fil', &
+ &   'Internal error','Unknown type of data',' ')
+  ENDIF
+!-
+! Read the file(s)
+  CALL getin_read
+!-
+! Allocate and initialize the memory we need
+  ALLOCATE(found(nb_to_ret))
+  found(:) = .FALSE.
+!-
+! See what we find in the files read
+!---
+!-- We dont know from which file the target could come.
+!-- Thus by default we attribute it to the first file :
+  fileorig = 1
+!-
+  DO it=1,nb_to_ret
+!---
+!-- First try the target as it is
+    CALL get_findkey (2,targetname,pos)
+!---
+!-- Another try
+!---
+    IF (pos < 0) THEN
+      WRITE(UNIT=cnt,FMT=c_i_fmt) it
+      CALL get_findkey (2,TRIM(targetname)//'__'//cnt,pos)
+    ENDIF
+!---
+    IF (pos > 0) THEN
+!-----
+      found(it) = .TRUE.
+      fileorig = fromfile(pos)
+      !
+      IF (l_dbg) THEN
+         WRITE(*,*) &
+              &      'getin_fil : read key ',targetname,' from file ',fileorig,' has type ',k_typ
+      ENDIF
+!-----
+!---- DECODE
+!-----
+      str_READ = ADJUSTL(fichier(pos))
+      str_READ_lower = str_READ
+      CALL strlowercase (str_READ_lower)
+      IF (l_dbg) THEN
+         WRITE(*,*) &
+              &      '            value    ',str_READ_lower
+      ENDIF
+!-----
+      IF (    (TRIM(str_READ_lower) == 'def')     &
+ &        .OR.(TRIM(str_READ_lower) == 'default') ) THEN
+        def_beha = .TRUE.
+      ELSE
+        def_beha = .FALSE.
+        len_str = LEN_TRIM(str_READ)
+        io_err = 0
+        SELECT CASE (k_typ)
+        CASE(k_i)
+          WRITE (UNIT=c_fmt,FMT='("(I",I3.3,")")') len_str
+          READ (UNIT=str_READ(1:len_str), &
+ &              FMT=c_fmt,IOSTAT=io_err) i_val(it)
+        CASE(k_r)
+          READ (UNIT=str_READ(1:len_str), &
+ &              FMT=*,IOSTAT=io_err) r_val(it)
+        CASE(k_c)
+          c_val(it) = str_READ(1:len_str)
+        CASE(k_l)
+          ipos_tr = -1
+          ipos_fl = -1
+          ipos_tr = MAX(INDEX(str_READ_lower,'tru'), &
+ &                      INDEX(str_READ_lower,'y'))
+          ipos_fl = MAX(INDEX(str_READ_lower,'fal'), &
+ &                      INDEX(str_READ_lower,'n'))
+          IF (ipos_tr > 0) THEN
+            l_val(it) = .TRUE.
+          ELSE IF (ipos_fl > 0) THEN
+            l_val(it) = .FALSE.
+          ELSE
+            io_err = 100
+          ENDIF
+        END SELECT
+        IF (io_err /= 0) THEN
+          CALL ipslerr (3,'get_fil', &
+ &         'Target '//TRIM(targetname), &
+ &         'is not of '//TRIM(c_vtyp)//' type',' ')
+        ENDIF
+      ENDIF
+!-----
+      IF ( (k_typ == k_i).OR.(k_typ == k_r) ) THEN
+!-------
+!------ Is this the value of a compressed field ?
+        compressed = (compline(pos) > 0)
+        IF (compressed) THEN
+          IF (compline(pos) /= nb_to_ret) THEN
+            CALL ipslerr (2,'get_fil', &
+ &           'For key '//TRIM(targetname)//' we have a compressed field', &
+ &           'which does not have the right size.', &
+ &           'We will try to fix that.')
+          ENDIF
+          IF      (k_typ == k_i) THEN
+            i_cmpval = i_val(it)
+          ELSE IF (k_typ == k_r) THEN
+            r_cmpval = r_val(it)
+          ENDIF
+        ENDIF
+      ENDIF
+    ELSE
+      found(it) = .FALSE.
+      def_beha = .FALSE.
+      compressed = .FALSE.
+    ENDIF
+  ENDDO
+!-
+  IF ( (k_typ == k_i).OR.(k_typ == k_r) ) THEN
+!---
+!-- If this is a compressed field then we will uncompress it
+    IF (compressed) THEN
+      DO it=1,nb_to_ret
+        IF (.NOT.found(it)) THEN
+          IF      (k_typ == k_i) THEN
+            i_val(it) = i_cmpval
+          ELSE IF (k_typ == k_r) THEN
+            r_val(it) = r_cmpval
+          ENDIF
+          found(it) = .TRUE.
+        ENDIF
+      ENDDO
+    ENDIF
+  ENDIF
+!-
+! Now we set the status for what we found
+  IF (def_beha) THEN
+    status = default
+    CALL ipslerr (1,'USING DEFAULT BEHAVIOUR FOR', &
+ &   TRIM(targetname),' ',' ')
+    WRITE(ipslout,*) 'USING DEFAULT BEHAVIOUR FOR ',TRIM(targetname)
+  ELSE
+    status_cnt = 0
+    DO it=1,nb_to_ret
+      IF (.NOT.found(it)) THEN
+        status_cnt = status_cnt+1
+        IF      (status_cnt <= max_msgs) THEN
+          WRITE (UNIT=ipslout,FMT='(" USING DEFAULTS : ",A)', &
+ &               ADVANCE='NO') TRIM(targetname)
+          IF (nb_to_ret > 1) THEN
+            WRITE (UNIT=ipslout,FMT='("__")',ADVANCE='NO')
+            WRITE (UNIT=ipslout,FMT=c_i_fmt,ADVANCE='NO') it
+          ENDIF
+          SELECT CASE (k_typ)
+          CASE(k_i)
+            WRITE (UNIT=ipslout,FMT=*) "=",i_val(it)
+          CASE(k_r)
+            WRITE (UNIT=ipslout,FMT=*) "=",r_val(it)
+          CASE(k_c)
+            WRITE (UNIT=ipslout,FMT=*) "=",c_val(it)
+          CASE(k_l)
+            WRITE (UNIT=ipslout,FMT=*) "=",l_val(it)
+          END SELECT
+        ELSE IF (status_cnt == max_msgs+1) THEN
+          WRITE (UNIT=ipslout,FMT='(" USING DEFAULTS ... ",A)')
+        ENDIF
+      ENDIF
+    ENDDO
+!---
+    IF (status_cnt == 0) THEN
+      status = nondefault
+    ELSE IF (status_cnt == nb_to_ret) THEN
+      status = default
+    ELSE
+      status = vectornondefault
+    ENDIF
+  ENDIF
+! Deallocate the memory
+  DEALLOCATE(found)
+!---------------------
+END SUBROUTINE get_fil
+!===
+SUBROUTINE get_rdb (pos,size_of_in,targetname,i_val,r_val,c_val,l_val)
+!---------------------------------------------------------------------
+!- Read the required variable in the database
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: pos,size_of_in
+  CHARACTER(LEN=*) :: targetname
+  INTEGER,DIMENSION(:),OPTIONAL          :: i_val
+  REAL,DIMENSION(:),OPTIONAL             :: r_val
+  LOGICAL,DIMENSION(:),OPTIONAL          :: l_val
+  CHARACTER(LEN=*),DIMENSION(:),OPTIONAL :: c_val
+!-
+  INTEGER :: k_typ,k_beg,k_end
+  CHARACTER(LEN=9) :: c_vtyp
+!---------------------------------------------------------------------
+!-
+! Get the type of the argument
+  CALL get_qtyp (k_typ,c_vtyp,i_val,r_val,c_val,l_val)
+  IF (     (k_typ /= k_i).AND.(k_typ /= k_r) &
+ &    .AND.(k_typ /= k_c).AND.(k_typ /= k_l) )THEN
+    CALL ipslerr (3,'get_rdb', &
+ &   'Internal error','Unknown type of data',' ')
+  ENDIF
+!-
+  IF (key_tab(pos)%keytype /= k_typ) THEN
+    CALL ipslerr (3,'get_rdb', &
+ &   'Wrong data type for keyword '//TRIM(targetname), &
+ &   '(NOT '//TRIM(c_vtyp)//')',' ')
+  ENDIF
+!-
+  IF (key_tab(pos)%keycompress > 0) THEN
+    IF (    (key_tab(pos)%keycompress /= size_of_in) &
+ &      .OR.(key_tab(pos)%keymemlen /= 1) ) THEN
+      CALL ipslerr (3,'get_rdb', &
+ &     'Wrong compression length','for keyword '//TRIM(targetname),' ')
+    ELSE
+      SELECT CASE (k_typ)
+      CASE(k_i)
+        i_val(1:size_of_in) = i_mem(key_tab(pos)%keymemstart)
+      CASE(k_r)
+        r_val(1:size_of_in) = r_mem(key_tab(pos)%keymemstart)
+      END SELECT
+    ENDIF
+  ELSE
+    IF (key_tab(pos)%keymemlen /= size_of_in) THEN
+      CALL ipslerr (3,'get_rdb', &
+ &     'Wrong array length','for keyword '//TRIM(targetname),' ')
+    ELSE
+      k_beg = key_tab(pos)%keymemstart
+      k_end = k_beg+key_tab(pos)%keymemlen-1
+      SELECT CASE (k_typ)
+      CASE(k_i)
+        i_val(1:size_of_in) = i_mem(k_beg:k_end)
+      CASE(k_r)
+        r_val(1:size_of_in) = r_mem(k_beg:k_end)
+      CASE(k_c)
+        c_val(1:size_of_in) = c_mem(k_beg:k_end)
+      CASE(k_l)
+        l_val(1:size_of_in) = l_mem(k_beg:k_end)
+      END SELECT
+    ENDIF
+  ENDIF
+!---------------------
+END SUBROUTINE get_rdb
+!===
+SUBROUTINE get_wdb &
+ &  (targetname,status,fileorig,size_of_in, &
+ &   i_val,r_val,c_val,l_val)
+!---------------------------------------------------------------------
+!- Write data into the data base
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*) :: targetname
+  INTEGER :: status,fileorig,size_of_in
+  INTEGER,DIMENSION(:),OPTIONAL          :: i_val
+  REAL,DIMENSION(:),OPTIONAL             :: r_val
+  LOGICAL,DIMENSION(:),OPTIONAL          :: l_val
+  CHARACTER(LEN=*),DIMENSION(:),OPTIONAL :: c_val
+!-
+  INTEGER :: k_typ
+  CHARACTER(LEN=9) :: c_vtyp
+  INTEGER :: k_mempos,k_memsize,k_beg,k_end
+  LOGICAL :: l_cmp
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!---------------------------------------------------------------------
+!-
+! Get the type of the argument
+  CALL get_qtyp (k_typ,c_vtyp,i_val,r_val,c_val,l_val)
+  IF (     (k_typ /= k_i).AND.(k_typ /= k_r) &
+ &    .AND.(k_typ /= k_c).AND.(k_typ /= k_l) )THEN
+    CALL ipslerr (3,'get_wdb', &
+ &   'Internal error','Unknown type of data',' ')
+  ENDIF
+!-
+! First check if we have sufficiant space for the new key
+  IF (nb_keys+1 > keymemsize) THEN
+    CALL getin_allockeys ()
+  ENDIF
+!-
+  SELECT CASE (k_typ)
+  CASE(k_i)
+    k_mempos = i_mempos; k_memsize = i_memsize;
+    l_cmp = (MINVAL(i_val) == MAXVAL(i_val)) &
+ &         .AND.(size_of_in > compress_lim)
+  CASE(k_r)
+    k_mempos = r_mempos; k_memsize = r_memsize;
+    l_cmp = (MINVAL(r_val) == MAXVAL(r_val)) &
+ &         .AND.(size_of_in > compress_lim)
+  CASE(k_c)
+    k_mempos = c_mempos; k_memsize = c_memsize;
+    l_cmp = .FALSE.
+  CASE(k_l)
+    k_mempos = l_mempos; k_memsize = l_memsize;
+    l_cmp = .FALSE.
+  END SELECT
+!-
+! Fill out the items of the data base
+  nb_keys = nb_keys+1
+  key_tab(nb_keys)%keystr = targetname(1:MIN(LEN_TRIM(targetname),l_n))
+  key_tab(nb_keys)%keystatus = status
+  key_tab(nb_keys)%keytype = k_typ
+  key_tab(nb_keys)%keyfromfile = fileorig
+  key_tab(nb_keys)%keymemstart = k_mempos+1
+  IF (l_cmp) THEN
+    key_tab(nb_keys)%keycompress = size_of_in
+    key_tab(nb_keys)%keymemlen = 1
+  ELSE
+    key_tab(nb_keys)%keycompress = -1
+    key_tab(nb_keys)%keymemlen = size_of_in
+  ENDIF
+  IF (l_dbg) THEN
+     WRITE(*,*) &
+ &     "get_wdb : nb_keys ",nb_keys," key_tab keystr   ",key_tab(nb_keys)%keystr,&
+ &                                       ",keystatus   ",key_tab(nb_keys)%keystatus,&
+ &                                       ",keytype     ",key_tab(nb_keys)%keytype,&
+ &                                       ",keycompress ",key_tab(nb_keys)%keycompress,&
+ &                                       ",keyfromfile ",key_tab(nb_keys)%keyfromfile,&
+ &                                       ",keymemstart ",key_tab(nb_keys)%keymemstart
+  ENDIF
+
+!-
+! Before writing the actual size lets see if we have the space
+  IF (key_tab(nb_keys)%keymemstart+key_tab(nb_keys)%keymemlen &
+ &    > k_memsize) THEN
+    CALL getin_allocmem (k_typ,key_tab(nb_keys)%keymemlen)
+  ENDIF
+!-
+  k_beg = key_tab(nb_keys)%keymemstart
+  k_end = k_beg+key_tab(nb_keys)%keymemlen-1
+  SELECT CASE (k_typ)
+  CASE(k_i)
+    i_mem(k_beg:k_end) = i_val(1:key_tab(nb_keys)%keymemlen)
+    i_mempos = k_end
+  CASE(k_r)
+    r_mem(k_beg:k_end) = r_val(1:key_tab(nb_keys)%keymemlen)
+    r_mempos = k_end
+  CASE(k_c)
+    c_mem(k_beg:k_end) = c_val(1:key_tab(nb_keys)%keymemlen)
+    c_mempos = k_end
+  CASE(k_l)
+    l_mem(k_beg:k_end) = l_val(1:key_tab(nb_keys)%keymemlen)
+    l_mempos = k_end
+  END SELECT
+!---------------------
+END SUBROUTINE get_wdb
+!-
+!===
+!-
+SUBROUTINE getin_read
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,SAVE :: current
+!---------------------------------------------------------------------
+  IF (allread == 0) THEN
+!-- Allocate a first set of memory.
+    CALL getin_alloctxt ()
+    CALL getin_allockeys ()
+    CALL getin_allocmem (k_i,0)
+    CALL getin_allocmem (k_r,0)
+    CALL getin_allocmem (k_c,0)
+    CALL getin_allocmem (k_l,0)
+!-- Start with reading the files
+    nbfiles = 1
+    filelist(1) = TRIM(def_file)
+    current = 1
+!--
+    DO WHILE (current <= nbfiles)
+      CALL getin_readdef (current)
+      current = current+1
+    ENDDO
+    allread = 1
+    CALL getin_checkcohe ()
+  ENDIF
+!------------------------
+END SUBROUTINE getin_read
+!-
+!===
+!-
+  SUBROUTINE getin_readdef(current)
+!---------------------------------------------------------------------
+!- This subroutine will read the files and only keep the
+!- the relevant information. The information is kept as it
+!- found in the file. The data will be analysed later.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: current
+!-
+  CHARACTER(LEN=:), ALLOCATABLE :: READ_str, NEW_str
+  CHARACTER(LEN=300) :: last_key,key_str
+  CHARACTER(LEN=n_d_fmt) :: cnt
+  CHARACTER(LEN=10) :: c_fmt
+  INTEGER :: nb_lastkey
+!-
+  INTEGER :: eof,ptn,len_str,i,it,iund,io_err
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!---------------------------------------------------------------------
+  eof = 0
+  ptn = 1
+  nb_lastkey = 0
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) 'getin_readdef : Open file ',TRIM(filelist(current))
+  ENDIF
+!-
+  OPEN (UNIT=22,FILE=filelist(current),STATUS="OLD",IOSTAT=io_err)
+  IF (io_err /= 0) THEN
+    CALL ipslerr (2,'getin_readdef', &
+ &  'Could not open file '//TRIM(filelist(current)),' ',' ')
+    RETURN
+  ENDIF
+!-
+  DO WHILE (eof /= 1)
+!---
+    CALL getin_skipafew (22,READ_str,eof,nb_lastkey)
+    len_str = LEN_TRIM(READ_str)
+    ptn = INDEX(READ_str,'=')
+!---
+    IF (ptn > 0) THEN
+!---- Get the target
+      key_str = TRIM(ADJUSTL(READ_str(1:ptn-1)))
+!---- Make sure that a vector keyword has the right length
+      iund = INDEX(key_str,'__')
+      IF (iund > 0) THEN
+        WRITE (UNIT=c_fmt,FMT='("(I",I3.3,")")') &
+ &        LEN_TRIM(key_str)-iund-1
+        READ(UNIT=key_str(iund+2:LEN_TRIM(key_str)), &
+ &           FMT=c_fmt,IOSTAT=io_err) it
+        IF ( (io_err == 0).AND.(it > 0) ) THEN
+          WRITE(UNIT=cnt,FMT=c_i_fmt) it
+          key_str = key_str(1:iund+1)//cnt
+        ELSE
+          CALL ipslerr (3,'getin_readdef', &
+ &         'A very strange key has just been found :', &
+ &         TRIM(key_str),' ')
+        ENDIF
+      ENDIF
+!---- Prepare the content
+      NEW_str = TRIM(ADJUSTL(READ_str(ptn+1:len_str)))
+      CALL nocomma (NEW_str)
+      CALL cmpblank (NEW_str)
+      NEW_str  = TRIM(ADJUSTL(NEW_str))
+      IF (l_dbg) THEN
+        WRITE(ipslout,*) &
+ &        '--> getin_readdef : ',TRIM(key_str),' :: ',TRIM(NEW_str)
+      ENDIF
+!---- Decypher the content of NEW_str
+!-
+!---- This has to be a new key word, thus :
+      nb_lastkey = 0
+!----
+      CALL getin_decrypt (current,key_str,NEW_str,last_key,nb_lastkey)
+!----
+    ELSE IF (len_str > 0) THEN
+!---- Prepare the key if we have an old one to which
+!---- we will add the line just read
+      IF (nb_lastkey > 0) THEN
+        iund =  INDEX(last_key,'__')
+        IF (iund > 0) THEN
+!-------- We only continue a keyword, thus it is easy
+          key_str = last_key(1:iund-1)
+        ELSE
+          IF (nb_lastkey /= 1) THEN
+            CALL ipslerr (3,'getin_readdef', &
+ &           'We can not have a scalar keyword', &
+ &           'and a vector content',' ')
+          ENDIF
+!-------- The last keyword needs to be transformed into a vector.
+          WRITE(UNIT=cnt,FMT=c_i_fmt) 1
+          targetlist(nb_lines) = &
+ &         last_key(1:MIN(LEN_TRIM(last_key),l_n-n_d_fmt-2))//'__'//cnt
+          key_str = last_key(1:LEN_TRIM(last_key))
+        ENDIF
+      ENDIF
+!---- Prepare the content
+      NEW_str = TRIM(ADJUSTL(READ_str(1:len_str)))
+      CALL getin_decrypt (current,key_str,NEW_str,last_key,nb_lastkey)
+    ELSE
+!---- If we have an empty line then the keyword finishes
+      nb_lastkey = 0
+      IF (l_dbg) THEN
+        WRITE(ipslout,*) 'getin_readdef : Have found an emtpy line '
+      ENDIF
+    ENDIF
+  ENDDO
+!-
+  CLOSE(UNIT=22)
+!-
+  IF (l_dbg) THEN
+    OPEN (UNIT=22,file=TRIM(def_file)//'.test')
+    DO i=1,nb_lines
+      WRITE(UNIT=22,FMT=*) targetlist(i)," : ",fichier(i)
+    ENDDO
+    CLOSE(UNIT=22)
+  ENDIF
+!-
+  IF (l_dbg) THEN
+     WRITE(ipslout,*) "nb_lines ",nb_lines,"nb_keys ",nb_keys
+     WRITE(ipslout,*) "fichier ",fichier(1:nb_lines)
+     WRITE(ipslout,*) "targetlist ",targetlist(1:nb_lines)
+     WRITE(ipslout,*) "fromfile ",fromfile(1:nb_lines)
+     WRITE(ipslout,*) "compline ",compline(1:nb_lines)
+     WRITE(ipslout,*) '<-getin_readdef'
+  ENDIF
+!---------------------------
+END SUBROUTINE getin_readdef
+!-
+!===
+!-
+SUBROUTINE getin_decrypt(current,key_str,NEW_str,last_key,nb_lastkey)
+!---------------------------------------------------------------------
+!- This subroutine is going to decypher the line.
+!- It essentialy checks how many items are included and
+!- it they can be attached to a key.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+! ARGUMENTS
+!-
+  INTEGER :: current,nb_lastkey
+  CHARACTER(LEN=*) :: key_str,NEW_str
+  CHARACTER(LEN=*),INTENT(out) :: last_key
+!-
+! LOCAL
+!-
+  INTEGER :: len_str,blk,nbve,starpos
+  CHARACTER(LEN=:), ALLOCATABLE :: tmp_str,new_key,mult
+  CHARACTER(LEN=n_d_fmt) :: cnt
+  CHARACTER(LEN=10) :: c_fmt
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!---------------------------------------------------------------------
+  len_str = LEN_TRIM(NEW_str)
+  blk = INDEX(NEW_str(1:len_str),' ')
+  tmp_str = NEW_str(1:len_str)
+!-
+! If the key is a new file then we take it up. Else
+! we save the line and go on.
+!-
+  IF (INDEX(key_str,'INCLUDEDEF') > 0) THEN
+    DO WHILE (blk > 0)
+      IF (nbfiles+1 > max_files) THEN
+        CALL ipslerr (3,'getin_decrypt', &
+ &       'Too many files to include',' ',' ')
+      ENDIF
+!-----
+      nbfiles = nbfiles+1
+      filelist(nbfiles) = tmp_str(1:blk)
+!-----
+      tmp_str = TRIM(ADJUSTL(tmp_str(blk+1:LEN_TRIM(tmp_str))))
+      blk = INDEX(tmp_str(1:LEN_TRIM(tmp_str)),' ')
+    ENDDO
+!---
+    IF (nbfiles+1 > max_files) THEN
+      CALL ipslerr (3,'getin_decrypt', &
+ &     'Too many files to include',' ',' ')
+    ENDIF
+!---
+    nbfiles =  nbfiles+1
+    filelist(nbfiles) = TRIM(ADJUSTL(tmp_str))
+!---
+    last_key = 'INCLUDEDEF'
+    nb_lastkey = 1
+  ELSE
+!-
+!-- We are working on a new line of input
+!-
+    IF (nb_lines+1 > i_txtsize) THEN
+      CALL getin_alloctxt ()
+    ENDIF
+    nb_lines = nb_lines+1
+!-
+!-- First we solve the issue of conpressed information. Once
+!-- this is done all line can be handled in the same way.
+!-
+    starpos = INDEX(NEW_str(1:len_str),'*')
+    IF ( (starpos > 0).AND.(tmp_str(1:1) /= '"') &
+ &                    .AND.(tmp_str(1:1) /= "'") ) THEN
+!-----
+      IF (INDEX(key_str(1:LEN_TRIM(key_str)),'__') > 0) THEN
+        CALL ipslerr (3,'getin_decrypt', &
+ &       'We can not have a compressed field of values', &
+ &       'in a vector notation (TARGET__n).', &
+ &       'The key at fault : '//TRIM(key_str))
+      ENDIF
+!-
+!---- Read the multiplied
+!-
+      mult = TRIM(ADJUSTL(NEW_str(1:starpos-1)))
+!---- Construct the new string and its parameters
+      NEW_str = TRIM(ADJUSTL(NEW_str(starpos+1:len_str)))
+      len_str = LEN_TRIM(NEW_str)
+      blk = INDEX(NEW_str(1:len_str),' ')
+      IF (blk > 1) THEN
+        CALL ipslerr (2,'getin_decrypt', &
+ &       'This is a strange behavior','you could report',' ')
+      ENDIF
+      WRITE (UNIT=c_fmt,FMT='("(I",I5.5,")")') LEN_TRIM(mult)
+      READ(UNIT=mult,FMT=c_fmt) compline(nb_lines)
+!---
+    ELSE
+      compline(nb_lines) = -1
+    ENDIF
+!-
+!-- If there is no space wthin the line then the target is a scalar
+!-- or the element of a properly written vector.
+!-- (ie of the type TARGET__00001)
+!-
+    IF (    (blk <= 1) &
+ &      .OR.(tmp_str(1:1) == '"') &
+ &      .OR.(tmp_str(1:1) == "'") ) THEN
+!-
+      IF (nb_lastkey == 0) THEN
+!------ Save info of current keyword as a scalar
+!------ if it is not a continuation
+        targetlist(nb_lines) = key_str(1:MIN(LEN_TRIM(key_str),l_n))
+        last_key = key_str(1:MIN(LEN_TRIM(key_str),l_n))
+        nb_lastkey = 1
+      ELSE
+!------ We are continuing a vector so the keyword needs
+!------ to get the underscores
+        WRITE(UNIT=cnt,FMT=c_i_fmt) nb_lastkey+1
+        targetlist(nb_lines) = &
+ &        key_str(1:MIN(LEN_TRIM(key_str),l_n-n_d_fmt-2))//'__'//cnt
+        last_key = &
+ &        key_str(1:MIN(LEN_TRIM(key_str),l_n-n_d_fmt-2))//'__'//cnt
+        nb_lastkey = nb_lastkey+1
+      ENDIF
+!-----
+      fichier(nb_lines) = NEW_str(1:len_str)
+      fromfile(nb_lines) = current
+    ELSE
+!-
+!---- If there are blanks whithin the line then we are dealing
+!---- with a vector and we need to split it in many entries
+!---- with the TARGET__n notation.
+!----
+!---- Test if the targer is not already a vector target !
+!-
+      IF (INDEX(TRIM(key_str),'__') > 0) THEN
+        CALL ipslerr (3,'getin_decrypt', &
+ &       'We have found a mixed vector notation (TARGET__n).', &
+ &       'The key at fault : '//TRIM(key_str),' ')
+      ENDIF
+!-
+      nbve = nb_lastkey
+      nbve = nbve+1
+      WRITE(UNIT=cnt,FMT=c_i_fmt) nbve
+!-
+      DO WHILE (blk > 0)
+!-
+!------ Save the content of target__nbve
+!-
+        fichier(nb_lines) = tmp_str(1:blk)
+        new_key = &
+ &       key_str(1:MIN(LEN_TRIM(key_str),l_n-n_d_fmt-2))//'__'//cnt
+        targetlist(nb_lines) = new_key(1:MIN(LEN_TRIM(new_key),l_n))
+        fromfile(nb_lines) = current
+!-
+        tmp_str = TRIM(ADJUSTL(tmp_str(blk+1:LEN_TRIM(tmp_str))))
+        blk = INDEX(TRIM(tmp_str),' ')
+!-
+        IF (nb_lines+1 > i_txtsize) THEN
+          CALL getin_alloctxt ()
+        ENDIF
+        nb_lines = nb_lines+1
+        nbve = nbve+1
+        WRITE(UNIT=cnt,FMT=c_i_fmt) nbve
+        compline(nb_lines)=compline(nb_lines-1)
+!-
+      ENDDO
+!-
+!---- Save the content of the last target
+!-
+      fichier(nb_lines) = tmp_str(1:LEN_TRIM(tmp_str))
+      new_key = &
+ &      key_str(1:MIN(LEN_TRIM(key_str),l_n-n_d_fmt-2))//'__'//cnt
+      targetlist(nb_lines) = new_key(1:MIN(LEN_TRIM(new_key),l_n))
+      fromfile(nb_lines) = current
+!-
+      last_key = &
+ &      key_str(1:MIN(LEN_TRIM(key_str),l_n-n_d_fmt-2))//'__'//cnt
+      nb_lastkey = nbve
+!-
+    ENDIF
+!-
+  ENDIF
+
+  IF (l_dbg) THEN
+     WRITE(ipslout,*) "getin_decrypt ->",TRIM(NEW_str), " : "
+     WRITE(ipslout,*) "getin_decrypt ->", nb_lines,&
+          & SIZE(fichier), &
+          & SIZE(fromfile), &
+          & SIZE(filelist)
+     IF (nb_lines > 0) THEN
+        WRITE(ipslout,*) "getin_decrypt ->", &
+          & TRIM(fichier(nb_lines)), &
+          & fromfile(nb_lines), &
+          & TRIM(filelist(fromfile(nb_lines)))
+        WRITE(ipslout,*) "                compline : ",compline(nb_lines)
+        WRITE(ipslout,*) "                targetlist : ",TRIM(targetlist(nb_lines))
+     ENDIF
+     WRITE(ipslout,*) "                last_key : ",last_key
+  ENDIF
+!---------------------------
+END SUBROUTINE getin_decrypt
+!-
+!===
+!-
+SUBROUTINE getin_checkcohe ()
+!---------------------------------------------------------------------
+!- This subroutine checks for redundancies.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: line,n_k,k
+!---------------------------------------------------------------------
+  DO line=1,nb_lines-1
+!-
+    n_k = 0
+    DO k=line+1,nb_lines
+      IF (TRIM(targetlist(line)) == TRIM(targetlist(k))) THEN
+        n_k = k
+        EXIT
+      ENDIF
+    ENDDO
+!---
+!-- IF we have found it we have a problem to solve.
+!---
+    IF (n_k > 0) THEN
+      WRITE(ipslout,*) 'COUNT : ',n_k
+      WRITE(ipslout,*) &
+ &  'getin_checkcohe : Found a problem on key ',TRIM(targetlist(line))
+      WRITE(ipslout,*) &
+ &  'getin_checkcohe : The following values were encoutered :'
+      WRITE(ipslout,*) &
+ &  '                ',TRIM(targetlist(line)),' == ',fichier(line)
+      WRITE(ipslout,*) &
+ &  '                ',TRIM(targetlist(k)),' == ',fichier(k)
+      WRITE(ipslout,*) &
+ &  'getin_checkcohe : We will keep only the last value'
+       CALL ipslerr (2,'getin_checkcohe','Found a problem on key ', &
+ &                     TRIM(targetlist(line)), fichier(line)//" "//fichier(k))
+      targetlist(line) = ' '
+    ENDIF
+  ENDDO
+!-----------------------------
+END SUBROUTINE getin_checkcohe
+!-
+!===
+!-
+SUBROUTINE getin_readline(unitf, out_string, is_eof)
+!---------------------------------------------------------------------
+  USE ISO_FORTRAN_ENV,ONLY : IOSTAT_EOR,IOSTAT_END
+!-
+  IMPLICIT NONE
+!-
+  INTEGER, PARAMETER :: CHARLEN = 100     ! buffer size
+  INTEGER, INTENT(in) :: unitf
+  INTEGER, INTENT(out) :: is_eof
+  CHARACTER(LEN=:),INTENT(out),ALLOCATABLE :: out_string
+!-
+  CHARACTER(LEN=CHARLEN)  :: dummy
+!-
+  CHARACTER(LEN=:), ALLOCATABLE :: buff1  ! buffer
+  INTEGER :: ioerr                  ! error code
+  INTEGER :: readlength                   ! number of chars read from file 
+  LOGICAL :: is_eol, is_first_ite         ! end of line? 
+
+  is_eof = 0
+  is_eol = .FALSE.
+  buff1 = ""
+
+  DO WHILE (.NOT. is_eol)
+!-
+      dummy = ""
+      READ (UNIT=unitf,FMT='(A)', ADVANCE='NO', SIZE=readlength,ERR=9998,END=7778,IOSTAT=ioerr) dummy
+      IF ((ioerr==IOSTAT_EOR).OR.(ioerr==IOSTAT_END)) ioerr = 0
+!-
+      ! keep looping if line is commented
+      dummy = TRIM(ADJUSTL(dummy))
+!-
+      ! is end of line?
+      is_eol = (readlength .LT. CHARLEN)
+!-
+      ! merge with previous buffer
+      buff1 = TRIM(buff1)//TRIM(dummy)
+  ENDDO
+!-
+  out_string=TRIM(buff1)
+!-
+  RETURN
+!-
+9998 CONTINUE
+  CALL ipslerr (3,'getin_readline','Error while reading file',' ',' ')
+!-
+7778 CONTINUE
+  out_string = TRIM(dummy)
+  is_eof = 1
+    
+END SUBROUTINE getin_readline
+!-
+! getin_skipafew: reads  
+!-
+SUBROUTINE getin_skipafew (unit,out_string,is_eof,nb_lastkey)
+!---------------------------------------------------------------------
+  USE ISO_FORTRAN_ENV,ONLY : IOSTAT_EOR,IOSTAT_END
+!-
+  IMPLICIT NONE
+!-
+  INTEGER :: unit,is_eof,nb_lastkey
+  CHARACTER(LEN=:),INTENT(out),ALLOCATABLE :: out_string
+!-
+  CHARACTER(LEN=1) :: first
+  CHARACTER(LEN=:), ALLOCATABLE :: dummy
+!---------------------------------------------------------------------
+  first=COMMENT_TAG
+  is_eof = 0
+  dummy = ""
+!-
+! Loop until a non commented line is found
+  DO WHILE (first == COMMENT_TAG .AND. is_eof == 0)
+!-
+     CALL getin_readline(unit, dummy, is_eof)
+!-
+!    Is first char a comment? #
+     IF (LEN(dummy) > 0) THEN
+         first=dummy(1:1)
+         IF (first == COMMENT_TAG) THEN
+           nb_lastkey = 0
+         ENDIF
+     ENDIF
+!-
+  ENDDO
+!-
+  CALL nocomment(dummy)
+  out_string = TRIM(dummy)
+!-
+  RETURN
+!-
+9998 CONTINUE
+  CALL ipslerr (3,'getin_skipafew','Error while reading file',' ',' ')
+!-
+7778 CONTINUE
+  CALL nocomment(dummy)
+  out_string = TRIM(dummy)
+  is_eof = 1
+!----------------------------
+END SUBROUTINE getin_skipafew
+!-
+!===
+!-
+SUBROUTINE getin_allockeys ()
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  TYPE(t_key),ALLOCATABLE,DIMENSION(:) :: tmp_key_tab
+!-
+  INTEGER :: ier
+  CHARACTER(LEN=20) :: c_tmp
+!---------------------------------------------------------------------
+  IF (keymemsize == 0) THEN
+!---
+!-- Nothing exists in memory arrays and it is easy to do.
+!---
+    WRITE (UNIT=c_tmp,FMT=*) memslabs
+    ALLOCATE(key_tab(memslabs),stat=ier)
+    IF (ier /= 0) THEN
+      CALL ipslerr (3,'getin_allockeys', &
+ &     'Can not allocate key_tab', &
+ &     'to size '//TRIM(ADJUSTL(c_tmp)),' ')
+    ENDIF
+    nb_keys = 0
+    keymemsize = memslabs
+    key_tab(:)%keycompress = -1
+!---
+  ELSE
+!---
+!-- There is something already in the memory,
+!-- we need to transfer and reallocate.
+!---
+    WRITE (UNIT=c_tmp,FMT=*) keymemsize
+    ALLOCATE(tmp_key_tab(keymemsize),stat=ier)
+    IF (ier /= 0) THEN
+      CALL ipslerr (3,'getin_allockeys', &
+ &     'Can not allocate tmp_key_tab', &
+ &     'to size '//TRIM(ADJUSTL(c_tmp)),' ')
+    ENDIF
+    WRITE (UNIT=c_tmp,FMT=*) keymemsize+memslabs
+    tmp_key_tab(1:keymemsize) = key_tab(1:keymemsize)
+    DEALLOCATE(key_tab)
+    ALLOCATE(key_tab(keymemsize+memslabs),stat=ier)
+    IF (ier /= 0) THEN
+      CALL ipslerr (3,'getin_allockeys', &
+ &     'Can not allocate key_tab', &
+ &     'to size '//TRIM(ADJUSTL(c_tmp)),' ')
+    ENDIF
+    key_tab(:)%keycompress = -1
+    key_tab(1:keymemsize) = tmp_key_tab(1:keymemsize)
+    DEALLOCATE(tmp_key_tab)
+    keymemsize = keymemsize+memslabs
+  ENDIF
+!-----------------------------
+END SUBROUTINE getin_allockeys
+!-
+!===
+!-
+SUBROUTINE getin_allocmem (type,len_wanted)
+!---------------------------------------------------------------------
+!- Allocate the memory of the data base for all 4 types of memory
+!- INTEGER / REAL / CHARACTER / LOGICAL
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: type,len_wanted
+!-
+  INTEGER,ALLOCATABLE :: tmp_int(:)
+  REAL,ALLOCATABLE :: tmp_real(:)
+  CHARACTER(LEN=300),ALLOCATABLE :: tmp_char(:)
+  LOGICAL,ALLOCATABLE :: tmp_logic(:)
+  INTEGER :: ier
+  CHARACTER(LEN=20) :: c_tmp
+!---------------------------------------------------------------------
+  SELECT CASE (type)
+  CASE(k_i)
+    IF (i_memsize == 0) THEN
+      ALLOCATE(i_mem(memslabs),stat=ier)
+      IF (ier /= 0) THEN
+        WRITE (UNIT=c_tmp,FMT=*) memslabs
+        CALL ipslerr (3,'getin_allocmem', &
+ &       'Unable to allocate db-memory', &
+ &       'i_mem to size '//TRIM(ADJUSTL(c_tmp)),' ')
+      ENDIF
+      i_memsize=memslabs
+    ELSE
+      ALLOCATE(tmp_int(i_memsize),stat=ier)
+      IF (ier /= 0) THEN
+        WRITE (UNIT=c_tmp,FMT=*) i_memsize
+        CALL ipslerr (3,'getin_allocmem', &
+ &       'Unable to allocate tmp_int', &
+ &       'to size '//TRIM(ADJUSTL(c_tmp)),' ')
+      ENDIF
+      tmp_int(1:i_memsize) = i_mem(1:i_memsize)
+      DEALLOCATE(i_mem)
+      ALLOCATE(i_mem(i_memsize+MAX(memslabs,len_wanted)),stat=ier)
+      IF (ier /= 0) THEN
+        WRITE (UNIT=c_tmp,FMT=*) i_memsize+MAX(memslabs,len_wanted)
+        CALL ipslerr (3,'getin_allocmem', &
+ &       'Unable to re-allocate db-memory', &
+ &       'i_mem to size '//TRIM(ADJUSTL(c_tmp)),' ')
+      ENDIF
+      i_mem(1:i_memsize) = tmp_int(1:i_memsize)
+      i_memsize = i_memsize+MAX(memslabs,len_wanted)
+      DEALLOCATE(tmp_int)
+    ENDIF
+  CASE(k_r)
+    IF (r_memsize == 0) THEN
+      ALLOCATE(r_mem(memslabs),stat=ier)
+      IF (ier /= 0) THEN
+        WRITE (UNIT=c_tmp,FMT=*) memslabs
+        CALL ipslerr (3,'getin_allocmem', &
+ &       'Unable to allocate db-memory', &
+ &       'r_mem to size '//TRIM(ADJUSTL(c_tmp)),' ')
+      ENDIF
+      r_memsize =  memslabs
+    ELSE
+      ALLOCATE(tmp_real(r_memsize),stat=ier)
+      IF (ier /= 0) THEN
+        WRITE (UNIT=c_tmp,FMT=*) r_memsize
+        CALL ipslerr (3,'getin_allocmem', &
+ &       'Unable to allocate tmp_real', &
+ &       'to size '//TRIM(ADJUSTL(c_tmp)),' ')
+      ENDIF
+      tmp_real(1:r_memsize) = r_mem(1:r_memsize)
+      DEALLOCATE(r_mem)
+      ALLOCATE(r_mem(r_memsize+MAX(memslabs,len_wanted)),stat=ier)
+      IF (ier /= 0) THEN
+        WRITE (UNIT=c_tmp,FMT=*) r_memsize+MAX(memslabs,len_wanted)
+        CALL ipslerr (3,'getin_allocmem', &
+ &       'Unable to re-allocate db-memory', &
+ &       'r_mem to size '//TRIM(ADJUSTL(c_tmp)),' ')
+      ENDIF
+      r_mem(1:r_memsize) = tmp_real(1:r_memsize)
+      r_memsize = r_memsize+MAX(memslabs,len_wanted)
+      DEALLOCATE(tmp_real)
+    ENDIF
+  CASE(k_c)
+    IF (c_memsize == 0) THEN
+      ALLOCATE(c_mem(memslabs),stat=ier)
+      IF (ier /= 0) THEN
+        WRITE (UNIT=c_tmp,FMT=*) memslabs
+        CALL ipslerr (3,'getin_allocmem', &
+ &       'Unable to allocate db-memory', &
+ &       'c_mem to size '//TRIM(ADJUSTL(c_tmp)),' ')
+      ENDIF
+      c_memsize = memslabs
+    ELSE
+      ALLOCATE(tmp_char(c_memsize),stat=ier)
+      IF (ier /= 0) THEN
+        WRITE (UNIT=c_tmp,FMT=*) c_memsize
+        CALL ipslerr (3,'getin_allocmem', &
+ &       'Unable to allocate tmp_char', &
+ &       'to size '//TRIM(ADJUSTL(c_tmp)),' ')
+      ENDIF
+      tmp_char(1:c_memsize) = c_mem(1:c_memsize)
+      DEALLOCATE(c_mem)
+      ALLOCATE(c_mem(c_memsize+MAX(memslabs,len_wanted)),stat=ier)
+      IF (ier /= 0) THEN
+        WRITE (UNIT=c_tmp,FMT=*) c_memsize+MAX(memslabs,len_wanted)
+        CALL ipslerr (3,'getin_allocmem', &
+ &       'Unable to re-allocate db-memory', &
+ &       'c_mem to size '//TRIM(ADJUSTL(c_tmp)),' ')
+      ENDIF
+      c_mem(1:c_memsize) = tmp_char(1:c_memsize)
+      c_memsize = c_memsize+MAX(memslabs,len_wanted)
+      DEALLOCATE(tmp_char)
+    ENDIF
+  CASE(k_l)
+    IF (l_memsize == 0) THEN
+      ALLOCATE(l_mem(memslabs),stat=ier)
+      IF (ier /= 0) THEN
+        WRITE (UNIT=c_tmp,FMT=*) memslabs
+        CALL ipslerr (3,'getin_allocmem', &
+ &       'Unable to allocate db-memory', &
+ &       'l_mem to size '//TRIM(ADJUSTL(c_tmp)),' ')
+      ENDIF
+      l_memsize = memslabs
+    ELSE
+      ALLOCATE(tmp_logic(l_memsize),stat=ier)
+      IF (ier /= 0) THEN
+        WRITE (UNIT=c_tmp,FMT=*) l_memsize
+        CALL ipslerr (3,'getin_allocmem', &
+ &       'Unable to allocate tmp_logic', &
+ &       'to size '//TRIM(ADJUSTL(c_tmp)),' ')
+      ENDIF
+      tmp_logic(1:l_memsize) = l_mem(1:l_memsize)
+      DEALLOCATE(l_mem)
+      ALLOCATE(l_mem(l_memsize+MAX(memslabs,len_wanted)),stat=ier)
+      IF (ier /= 0) THEN
+        WRITE (UNIT=c_tmp,FMT=*) l_memsize+MAX(memslabs,len_wanted)
+        CALL ipslerr (3,'getin_allocmem', &
+ &       'Unable to re-allocate db-memory', &
+ &       'l_mem to size '//TRIM(ADJUSTL(c_tmp)),' ')
+      ENDIF
+      l_mem(1:l_memsize) = tmp_logic(1:l_memsize)
+      l_memsize = l_memsize+MAX(memslabs,len_wanted)
+      DEALLOCATE(tmp_logic)
+    ENDIF
+  CASE DEFAULT
+    CALL ipslerr (3,'getin_allocmem','Unknown type of data',' ',' ')
+  END SELECT
+!----------------------------
+END SUBROUTINE getin_allocmem
+!-
+!===
+!-
+SUBROUTINE getin_alloctxt ()
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=300),ALLOCATABLE :: tmp_fic(:)
+  CHARACTER(LEN=l_n),ALLOCATABLE :: tmp_tgl(:)
+  INTEGER,ALLOCATABLE :: tmp_int(:)
+!-
+  INTEGER :: ier
+  CHARACTER(LEN=20) :: c_tmp1,c_tmp2
+!---------------------------------------------------------------------
+  IF (i_txtsize == 0) THEN
+!---
+!-- Nothing exists in memory arrays and it is easy to do.
+!---
+    WRITE (UNIT=c_tmp1,FMT=*) i_txtslab
+    ALLOCATE(fichier(i_txtslab),stat=ier)
+    IF (ier /= 0) THEN
+      CALL ipslerr (3,'getin_alloctxt', &
+ &     'Can not allocate fichier', &
+ &     'to size '//TRIM(ADJUSTL(c_tmp1)),' ')
+    ENDIF
+!---
+    ALLOCATE(targetlist(i_txtslab),stat=ier)
+    IF (ier /= 0) THEN
+      CALL ipslerr (3,'getin_alloctxt', &
+ &     'Can not allocate targetlist', &
+ &     'to size '//TRIM(ADJUSTL(c_tmp1)),' ')
+    ENDIF
+!---
+    ALLOCATE(fromfile(i_txtslab),stat=ier)
+    IF (ier /= 0) THEN
+      CALL ipslerr (3,'getin_alloctxt', &
+ &     'Can not allocate fromfile', &
+ &     'to size '//TRIM(ADJUSTL(c_tmp1)),' ')
+    ENDIF
+!---
+    ALLOCATE(compline(i_txtslab),stat=ier)
+    IF (ier /= 0) THEN
+      CALL ipslerr (3,'getin_alloctxt', &
+ &     'Can not allocate compline', &
+ &     'to size '//TRIM(ADJUSTL(c_tmp1)),' ')
+    ENDIF
+!---
+    nb_lines = 0
+    i_txtsize = i_txtslab
+  ELSE
+!---
+!-- There is something already in the memory,
+!-- we need to transfer and reallocate.
+!---
+    WRITE (UNIT=c_tmp1,FMT=*) i_txtsize
+    WRITE (UNIT=c_tmp2,FMT=*) i_txtsize+i_txtslab
+    ALLOCATE(tmp_fic(i_txtsize),stat=ier)
+    IF (ier /= 0) THEN
+      CALL ipslerr (3,'getin_alloctxt', &
+ &     'Can not allocate tmp_fic', &
+ &     'to size '//TRIM(ADJUSTL(c_tmp1)),' ')
+    ENDIF
+    tmp_fic(1:i_txtsize) = fichier(1:i_txtsize)
+    DEALLOCATE(fichier)
+    ALLOCATE(fichier(i_txtsize+i_txtslab),stat=ier)
+    IF (ier /= 0) THEN
+      CALL ipslerr (3,'getin_alloctxt', &
+ &     'Can not allocate fichier', &
+ &     'to size '//TRIM(ADJUSTL(c_tmp2)),' ')
+    ENDIF
+    fichier(1:i_txtsize) = tmp_fic(1:i_txtsize)
+    DEALLOCATE(tmp_fic)
+!---
+    ALLOCATE(tmp_tgl(i_txtsize),stat=ier)
+    IF (ier /= 0) THEN
+      CALL ipslerr (3,'getin_alloctxt', &
+ &     'Can not allocate tmp_tgl', &
+ &     'to size '//TRIM(ADJUSTL(c_tmp1)),' ')
+    ENDIF
+    tmp_tgl(1:i_txtsize) = targetlist(1:i_txtsize)
+    DEALLOCATE(targetlist)
+    ALLOCATE(targetlist(i_txtsize+i_txtslab),stat=ier)
+    IF (ier /= 0) THEN
+      CALL ipslerr (3,'getin_alloctxt', &
+ &     'Can not allocate targetlist', &
+ &     'to size '//TRIM(ADJUSTL(c_tmp2)),' ')
+    ENDIF
+    targetlist(1:i_txtsize) = tmp_tgl(1:i_txtsize)
+    DEALLOCATE(tmp_tgl)
+!---
+    ALLOCATE(tmp_int(i_txtsize),stat=ier)
+    IF (ier /= 0) THEN
+      CALL ipslerr (3,'getin_alloctxt', &
+ &     'Can not allocate tmp_int', &
+ &     'to size '//TRIM(ADJUSTL(c_tmp1)),' ')
+    ENDIF
+    tmp_int(1:i_txtsize) = fromfile(1:i_txtsize)
+    DEALLOCATE(fromfile)
+    ALLOCATE(fromfile(i_txtsize+i_txtslab),stat=ier)
+    IF (ier /= 0) THEN
+      CALL ipslerr (3,'getin_alloctxt', &
+ &     'Can not allocate fromfile', &
+ &     'to size '//TRIM(ADJUSTL(c_tmp2)),' ')
+    ENDIF
+    fromfile(1:i_txtsize) = tmp_int(1:i_txtsize)
+!---
+    tmp_int(1:i_txtsize) = compline(1:i_txtsize)
+    DEALLOCATE(compline)
+    ALLOCATE(compline(i_txtsize+i_txtslab),stat=ier)
+    IF (ier /= 0) THEN
+      CALL ipslerr (3,'getin_alloctxt', &
+ &     'Can not allocate compline', &
+ &     'to size '//TRIM(ADJUSTL(c_tmp2)),' ')
+    ENDIF
+    compline(1:i_txtsize) = tmp_int(1:i_txtsize)
+    DEALLOCATE(tmp_int)
+!---
+    i_txtsize = i_txtsize+i_txtslab
+  ENDIF
+!----------------------------
+END SUBROUTINE getin_alloctxt
+!-
+!===
+!-
+SUBROUTINE getin_dump (fileprefix)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(*),OPTIONAL :: fileprefix
+!-
+  CHARACTER(LEN=80) :: usedfileprefix
+  INTEGER :: ikey,if,iff,iv
+  CHARACTER(LEN=20) :: c_tmp
+  CHARACTER(LEN=300) :: tmp_str,used_filename
+  INTEGER :: io_err
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!---------------------------------------------------------------------
+  IF (PRESENT(fileprefix)) THEN
+    usedfileprefix = fileprefix(1:MIN(LEN_TRIM(fileprefix),80))
+  ELSE
+    usedfileprefix = "used"
+  ENDIF
+!-
+  DO if=1,nbfiles
+!---
+    used_filename = TRIM(usedfileprefix)//'_'//TRIM(filelist(if))
+    IF (l_dbg) THEN
+      WRITE(ipslout,*) &
+ &      'getin_dump : opens file : ',TRIM(used_filename),' if = ',if
+      WRITE(ipslout,*) 'GETIN_DUMP : NUMBER OF KEYS : ',nb_keys
+    ENDIF
+    OPEN (UNIT=22,FILE=used_filename,iostat=io_err)
+    IF (io_err /= 0) THEN
+       CALL ipslerr (3,'getin_dump', &
+            &   'Could not open file :',TRIM(used_filename), &
+            &   '')
+    ENDIF
+!---
+!-- If this is the first file we need to add the list
+!-- of file which belong to it
+    IF ( (if == 1).AND.(nbfiles > 1) ) THEN
+      WRITE(22,*) '# '
+      WRITE(22,*) '# This file is linked to the following files :'
+      WRITE(22,*) '# '
+      DO iff=2,nbfiles
+        WRITE(22,*) 'INCLUDEDEF = ',TRIM(filelist(iff))
+      ENDDO
+      WRITE(22,*) '# '
+      IF (l_dbg) THEN
+         WRITE(ipslout,*) '# '
+         WRITE(ipslout,*) '# This file is linked to the following files :'
+         WRITE(ipslout,*) '# '
+         DO iff=2,nbfiles
+            WRITE(ipslout,*) 'INCLUDEDEF = ',TRIM(filelist(iff))
+         ENDDO
+         WRITE(ipslout,*) '# '
+      ENDIF
+    ENDIF
+!---
+    DO ikey=1,nb_keys
+!-----
+!---- Is this key from this file ?
+      IF (key_tab(ikey)%keyfromfile == if) THEN
+!-------
+!------ Write some comments
+        WRITE(22,*) '#'
+        SELECT CASE (key_tab(ikey)%keystatus)
+        CASE(nondefault)
+          WRITE(22,*) '# Values of ', &
+ &          TRIM(key_tab(ikey)%keystr),' comes from ',TRIM(def_file)
+        CASE(default)
+          WRITE(22,*) '# Values of ', &
+ &          TRIM(key_tab(ikey)%keystr),' are all defaults.'
+        CASE(vectornondefault)
+          WRITE(22,*) '# Values of ', &
+ &          TRIM(key_tab(ikey)%keystr), &
+ &          ' are a mix of ',TRIM(def_file),' and defaults.'
+        CASE DEFAULT
+          WRITE(22,*) '# Dont know from where the value of ', &
+ &          TRIM(key_tab(ikey)%keystr),' comes.'
+        END SELECT
+        WRITE(22,*) '#'
+        !-
+        IF (l_dbg) THEN
+           WRITE(ipslout,*) '#'
+           WRITE(ipslout,*) '# Status of key ', ikey, ' : ',&
+ &          TRIM(key_tab(ikey)%keystr),key_tab(ikey)%keystatus
+        ENDIF
+!-------
+!------ Write the values
+        SELECT CASE (key_tab(ikey)%keytype)
+        CASE(k_i)
+          IF (key_tab(ikey)%keymemlen == 1) THEN
+            IF (key_tab(ikey)%keycompress < 0) THEN
+              WRITE(22,*) &
+ &              TRIM(key_tab(ikey)%keystr), &
+ &              ' = ',i_mem(key_tab(ikey)%keymemstart)
+            ELSE
+              WRITE(22,*) &
+ &              TRIM(key_tab(ikey)%keystr), &
+ &              ' = ',key_tab(ikey)%keycompress, &
+ &              ' * ',i_mem(key_tab(ikey)%keymemstart)
+            ENDIF
+          ELSE
+            DO iv=0,key_tab(ikey)%keymemlen-1
+              WRITE(UNIT=c_tmp,FMT=c_i_fmt) iv+1
+              WRITE(22,*) &
+ &              TRIM(key_tab(ikey)%keystr), &
+ &              '__',TRIM(ADJUSTL(c_tmp)), &
+ &              ' = ',i_mem(key_tab(ikey)%keymemstart+iv)
+            ENDDO
+          ENDIF
+        CASE(k_r)
+          IF (key_tab(ikey)%keymemlen == 1) THEN
+            IF (key_tab(ikey)%keycompress < 0) THEN
+              WRITE(22,*) &
+ &              TRIM(key_tab(ikey)%keystr), &
+ &              ' = ',r_mem(key_tab(ikey)%keymemstart)
+            ELSE
+              WRITE(22,*) &
+ &              TRIM(key_tab(ikey)%keystr), &
+ &              ' = ',key_tab(ikey)%keycompress, &
+                   & ' * ',r_mem(key_tab(ikey)%keymemstart)
+            ENDIF
+          ELSE
+            DO iv=0,key_tab(ikey)%keymemlen-1
+              WRITE(UNIT=c_tmp,FMT=c_i_fmt) iv+1
+              WRITE(22,*) &
+ &              TRIM(key_tab(ikey)%keystr),'__',TRIM(ADJUSTL(c_tmp)), &
+ &              ' = ',r_mem(key_tab(ikey)%keymemstart+iv)
+            ENDDO
+          ENDIF
+        CASE(k_c)
+          IF (key_tab(ikey)%keymemlen == 1) THEN
+            tmp_str = c_mem(key_tab(ikey)%keymemstart)
+            WRITE(22,*) TRIM(key_tab(ikey)%keystr), &
+ &              ' = ',TRIM(tmp_str)
+          ELSE
+            DO iv=0,key_tab(ikey)%keymemlen-1
+              WRITE(UNIT=c_tmp,FMT=c_i_fmt) iv+1
+              tmp_str = c_mem(key_tab(ikey)%keymemstart+iv)
+              WRITE(22,*) &
+ &              TRIM(key_tab(ikey)%keystr), &
+ &              '__',TRIM(ADJUSTL(c_tmp)), &
+ &              ' = ',TRIM(tmp_str)
+            ENDDO
+          ENDIF
+        CASE(k_l)
+          IF (key_tab(ikey)%keymemlen == 1) THEN
+            IF (l_mem(key_tab(ikey)%keymemstart)) THEN
+              WRITE(22,*) TRIM(key_tab(ikey)%keystr),' = TRUE '
+            ELSE
+              WRITE(22,*) TRIM(key_tab(ikey)%keystr),' = FALSE '
+            ENDIF
+          ELSE
+            DO iv=0,key_tab(ikey)%keymemlen-1
+              WRITE(UNIT=c_tmp,FMT=c_i_fmt) iv+1
+              IF (l_mem(key_tab(ikey)%keymemstart+iv)) THEN
+                WRITE(22,*) TRIM(key_tab(ikey)%keystr),'__', &
+ &                          TRIM(ADJUSTL(c_tmp)),' = TRUE '
+              ELSE
+                WRITE(22,*) TRIM(key_tab(ikey)%keystr),'__', &
+ &                          TRIM(ADJUSTL(c_tmp)),' = FALSE '
+              ENDIF
+            ENDDO
+          ENDIF
+        CASE DEFAULT
+          CALL ipslerr (3,'getin_dump', &
+ &         'Unknown type for variable '//TRIM(key_tab(ikey)%keystr), &
+ &         ' ',' ')
+        END SELECT
+      ENDIF
+    ENDDO
+!-
+    CLOSE(UNIT=22)
+!-
+  ENDDO
+!------------------------
+END SUBROUTINE getin_dump
+!===
+SUBROUTINE getin_dump_para (fileprefix,rank)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(*) :: fileprefix
+  INTEGER,INTENT(IN) :: rank
+!-
+  CHARACTER(LEN=80) :: usedfileprefix
+  LOGICAL :: l_dbg
+  INTEGER :: isize
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!---------------------------------------------------------------------
+  IF ( rank < 1000 ) THEN
+     isize=MIN(LEN_TRIM(fileprefix),75)
+     usedfileprefix = fileprefix(1:isize)
+     usedfileprefix((isize+1):(isize+1))='_'
+     WRITE(usedfileprefix((isize+2):(isize+5)),'(I4.4)') rank
+     IF (l_dbg) &
+          WRITE(ipslout,*) 'Dump getin to file ',usedfileprefix
+     CALL getin_dump(usedfileprefix)
+  ENDIF
+!------------------------
+END SUBROUTINE getin_dump_para
+
+!===
+SUBROUTINE get_qtyp (k_typ,c_vtyp,i_v,r_v,c_v,l_v)
+!---------------------------------------------------------------------
+!- Returns the type of the argument (mutually exclusive)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(OUT) :: k_typ
+  CHARACTER(LEN=*),INTENT(OUT) :: c_vtyp
+  INTEGER,DIMENSION(:),OPTIONAL          :: i_v
+  REAL,DIMENSION(:),OPTIONAL             :: r_v
+  LOGICAL,DIMENSION(:),OPTIONAL          :: l_v
+  CHARACTER(LEN=*),DIMENSION(:),OPTIONAL :: c_v
+!---------------------------------------------------------------------
+  k_typ = 0
+  IF (COUNT((/PRESENT(i_v),PRESENT(r_v),PRESENT(c_v),PRESENT(l_v)/)) &
+ &    /= 1) THEN
+    CALL ipslerr (3,'get_qtyp', &
+ &   'Invalid number of optional arguments','(/= 1)',' ')
+  ENDIF
+!-
+  IF     (PRESENT(i_v)) THEN
+    k_typ = k_i
+    c_vtyp = 'INTEGER'
+  ELSEIF (PRESENT(r_v)) THEN
+    k_typ = k_r
+    c_vtyp = 'REAL'
+  ELSEIF (PRESENT(c_v)) THEN
+    k_typ = k_c
+    c_vtyp = 'CHARACTER'
+  ELSEIF (PRESENT(l_v)) THEN
+    k_typ = k_l
+    c_vtyp = 'LOGICAL'
+  ENDIF
+!----------------------
+END SUBROUTINE get_qtyp
+!===
+SUBROUTINE get_findkey (i_tab,c_key,pos)
+!---------------------------------------------------------------------
+!- This subroutine looks for a key in a table
+!---------------------------------------------------------------------
+!- INPUT
+!-   i_tab  : 1 -> search in key_tab(1:nb_keys)%keystr
+!-            2 -> search in targetlist(1:nb_lines)
+!-   c_key  : Name of the key we are looking for
+!- OUTPUT
+!-   pos    : -1 if key not found, else value in the table
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(in) :: i_tab
+  CHARACTER(LEN=*),INTENT(in) :: c_key
+  INTEGER,INTENT(out) :: pos
+!-
+  INTEGER :: ikey_max,ikey
+  CHARACTER(LEN=l_n) :: c_q_key
+!---------------------------------------------------------------------
+  pos = -1
+  IF     (i_tab == 1) THEN
+    ikey_max = nb_keys
+  ELSEIF (i_tab == 2) THEN
+    ikey_max = nb_lines
+  ELSE
+    ikey_max = 0
+  ENDIF
+  IF ( ikey_max > 0 ) THEN
+    DO ikey=1,ikey_max
+      IF (i_tab == 1) THEN
+        c_q_key = key_tab(ikey)%keystr
+      ELSE
+        c_q_key = targetlist(ikey)
+      ENDIF
+      IF (TRIM(c_q_key) == TRIM(c_key)) THEN
+        pos = ikey
+        EXIT
+      ENDIF
+    ENDDO
+  ENDIF
+!-------------------------
+END SUBROUTINE get_findkey
+!===
+!------------------
+END MODULE getincom
Index: /IOIPSL/tags/v2_2_5/src/histcom.f90
===================================================================
--- /IOIPSL/tags/v2_2_5/src/histcom.f90	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/src/histcom.f90	(revision 5058)
@@ -0,0 +1,2520 @@
+MODULE histcom
+!-
+!$Id$
+!-
+! This software is governed by the CeCILL license
+! See IOIPSL/IOIPSL_License_CeCILL.txt
+!-
+  USE netcdf
+!-
+  USE stringop, ONLY : nocomma,cmpblank,findpos,find_str,strlowercase
+  USE mathelp,  ONLY : mathop,moycum,moycum_index,buildop
+  USE fliocom,  ONLY : flio_dom_file,flio_dom_att
+  USE calendar
+  USE errioipsl, ONLY : ipslerr,ipsldbg,ipslout
+!-
+  IMPLICIT NONE
+!-
+  PRIVATE
+  PUBLIC :: histbeg,histdef,histhori,histvert,histend, &
+ &          histwrite,histclo,histsync,ioconf_modname, histglobal_attr
+!---------------------------------------------------------------------
+!- Some confusing vocabulary in this code !
+!- =========================================
+!-
+!- A REGULAR grid is a grid which is i,j indexes
+!- and thus it is stored in a 2D matrix.
+!- This is opposed to a IRREGULAR grid which is only in a vector
+!- and where we do not know which neighbors we have.
+!- As a consequence we need the bounds for each grid-cell.
+!-
+!- A RECTILINEAR grid is a special case of a regular grid
+!- in which all longitudes for i constant are equal
+!- and all latitudes for j constant.
+!- In other words we do not need the full 2D matrix
+!- to describe the grid, just two vectors.
+!---------------------------------------------------------------------
+!-
+  INTERFACE histbeg
+    MODULE PROCEDURE histb_reg1d,histb_reg2d,histb_irreg
+  END INTERFACE
+!-
+  INTERFACE histhori
+    MODULE PROCEDURE histh_reg1d,histh_reg2d,histh_irreg
+  END INTERFACE
+!-
+  INTERFACE histwrite
+!---------------------------------------------------------------------
+!- The "histwrite" routines will give the data to the I/O system.
+!- It will trigger the operations to be performed,
+!- and the writting to the file if needed
+!-
+!- We test for the work to be done at this time here so that at a
+!- later stage we can call different operation and write subroutine
+!- for the REAL and INTEGER interfaces
+!-
+!- INPUT
+!- idf      : The ID of the file on which this variable is to be,
+!-            written. The variable should have been defined in
+!-            this file before.
+!- pvarname : The short name of the variable
+!- pitau    : Current timestep
+!- pdata    : The variable, I mean the real data !
+!- nbindex  : The number of indexes provided. If it is equal to
+!-            the size of the full field as provided in histdef
+!-            then nothing is done.
+!- nindex   : The indices used to expand the variable (pdata)
+!-            onto the full field.
+!---------------------------------------------------------------------
+!- histwrite - we have to prepare different type of fields :
+!-             real and integer, 1,2 or 3D
+    MODULE PROCEDURE histwrite_r1d,histwrite_r2d,histwrite_r3d
+  END INTERFACE
+!-
+! Fixed parameter
+!-
+  INTEGER,PARAMETER :: nb_files_max=20,nb_var_max=1000, &
+ &                     nb_hax_max=5,nb_zax_max=30,nbopp_max=10
+  REAL,PARAMETER :: missing_val=nf90_fill_real
+  INTEGER,PARAMETER,PUBLIC :: &
+ &  hist_r4=nf90_real4, hist_r8=nf90_real8
+!-
+! Variable derived type
+!-
+TYPE T_D_V
+  INTEGER :: ncvid
+  INTEGER :: nbopp
+  CHARACTER(LEN=20)  :: v_name,unit_name
+  CHARACTER(LEN=256) :: title,std_name
+  CHARACTER(LEN=80)  :: fullop
+  CHARACTER(LEN=7)   :: topp
+  CHARACTER(LEN=7),DIMENSION(nbopp_max) :: sopp
+  REAL,DIMENSION(nbopp_max) :: scal
+!-External type (for R4/R8)
+  INTEGER :: v_typ
+!-Sizes of the associated grid and zommed area
+  INTEGER,DIMENSION(3) :: scsize,zorig,zsize
+!-Sizes for the data as it goes through the various math operations
+  INTEGER,DIMENSION(3) :: datasz_in = -1
+  INTEGER :: datasz_max = -1
+!-
+  INTEGER :: h_axid,z_axid,t_axid
+!-
+  REAL,DIMENSION(2) :: hist_minmax
+  LOGICAL :: hist_calc_rng=.FALSE.,hist_wrt_rng=.FALSE.
+!-Book keeping of the axes
+  INTEGER :: tdimid,tbndid=-1,tax_last
+  LOGICAL :: l_bnd
+  CHARACTER(LEN=40) :: tax_name
+!-
+  REAL :: freq_opp,freq_wrt
+  INTEGER :: &
+ &  last_opp,last_wrt,last_opp_chk,last_wrt_chk,nb_opp,nb_wrt
+!- For future optimization
+  REAL,POINTER,DIMENSION(:) :: t_bf
+!#  REAL,ALLOCATABLE,DIMENSION(:) :: V_1_D
+!#  REAL,ALLOCATABLE,DIMENSION(:,:) :: V_2_D
+!#  REAL,ALLOCATABLE,DIMENSION(:,:,:) :: V_3_D
+END TYPE T_D_V
+!-
+! File derived type
+!-
+TYPE :: T_D_F
+!-NETCDF IDs for file
+  INTEGER :: ncfid=-1
+!-Name of the file
+  CHARACTER(LEN=120) :: name
+!-Time variables
+  INTEGER :: itau0=0
+  REAL :: date0,deltat
+!-Counter of elements (variables, time-horizontal-vertical axis
+  INTEGER :: n_var=0,n_tax=0,n_hax=0,n_zax=0
+!-NETCDF dimension IDs for time-[time_bounds]-longitude-latitude
+  INTEGER :: tid,bid,xid,yid
+!-General definitions in the NETCDF file
+  INTEGER,DIMENSION(2) :: full_size=0,slab_ori,slab_siz
+!-The horizontal axes
+  CHARACTER(LEN=25),DIMENSION(nb_hax_max,2) :: hax_name
+!-The vertical axes
+  INTEGER,DIMENSION(nb_zax_max) :: zax_size,zax_ids
+  CHARACTER(LEN=20),DIMENSION(nb_zax_max) :: zax_name
+!-
+  LOGICAL :: regular=.TRUE.
+!-DOMAIN ID
+  INTEGER :: dom_id_svg=-1
+!-
+  TYPE(T_D_V),DIMENSION(nb_var_max) :: W_V
+END TYPE T_D_F
+!-
+TYPE(T_D_F),DIMENSION(nb_files_max),SAVE :: W_F
+!-
+! A list of functions which require special action
+! (Needs to be updated when functions are added
+!  but they are well located here)
+!-
+  CHARACTER(LEN=30),SAVE :: fuchnbout = 'scatter, fill'
+!- Some configurable variables with locks
+  CHARACTER(LEN=80),SAVE :: model_name='An IPSL model'
+  LOGICAL,SAVE :: lock_modname=.FALSE.
+!-
+!===
+CONTAINS
+!===
+!-
+SUBROUTINE histb_reg1d                 &
+ & (pfilename,pim,plon,pjm,plat,       &
+ &  par_orix,par_szx,par_oriy,par_szy, &
+ &  pitau0,pdate0,pdeltat,phoriid,idf,domain_id,mode)
+!---------------------------------------------------------------------
+!- histbeg for 1D regular horizontal coordinates (see histb_all)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*) :: pfilename
+  INTEGER,INTENT(IN) :: pim,pjm
+  REAL,DIMENSION(pim),INTENT(IN) :: plon
+  REAL,DIMENSION(pjm),INTENT(IN) :: plat
+  INTEGER,INTENT(IN):: par_orix,par_szx,par_oriy,par_szy
+  INTEGER,INTENT(IN) :: pitau0
+  REAL,INTENT(IN) :: pdate0,pdeltat
+  INTEGER,INTENT(OUT) :: idf,phoriid
+  INTEGER,INTENT(IN),OPTIONAL :: domain_id
+  CHARACTER(LEN=*),OPTIONAL,INTENT(IN) :: mode
+!---------------------------------------------------------------------
+  CALL histb_all &
+ & (1,pfilename,pim,pjm,pitau0,pdate0,pdeltat,phoriid,idf, &
+ &  x_1d=plon,y_1d=plat, &
+ &  k_orx=par_orix,k_szx=par_szx,k_ory=par_oriy,k_szy=par_szy, &
+ &  domain_id=domain_id,mode=mode)
+!-------------------------
+END SUBROUTINE histb_reg1d
+!===
+SUBROUTINE histb_reg2d &
+ & (pfilename,pim,plon,pjm,plat,       &
+ &  par_orix,par_szx,par_oriy,par_szy, &
+ &  pitau0,pdate0,pdeltat,phoriid,idf,domain_id,mode)
+!---------------------------------------------------------------------
+!- histbeg for 2D regular horizontal coordinates (see histb_all)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*) :: pfilename
+  INTEGER,INTENT(IN) :: pim,pjm
+  REAL,DIMENSION(pim,pjm),INTENT(IN) :: plon,plat
+  INTEGER,INTENT(IN):: par_orix,par_szx,par_oriy,par_szy
+  INTEGER,INTENT(IN) :: pitau0
+  REAL,INTENT(IN) :: pdate0,pdeltat
+  INTEGER,INTENT(OUT) :: idf,phoriid
+  INTEGER,INTENT(IN),OPTIONAL :: domain_id
+  CHARACTER(LEN=*),OPTIONAL,INTENT(IN) :: mode
+!---------------------------------------------------------------------
+  CALL histb_all &
+ & (2,pfilename,pim,pjm,pitau0,pdate0,pdeltat,phoriid,idf,  &
+ &  x_2d=plon,y_2d=plat, &
+ &  k_orx=par_orix,k_szx=par_szx,k_ory=par_oriy,k_szy=par_szy,    &
+ &  domain_id=domain_id,mode=mode)
+!-------------------------
+END SUBROUTINE histb_reg2d
+!===
+SUBROUTINE histb_irreg &
+ &  (pfilename,pim,plon,plon_bounds,plat,plat_bounds, &
+ &   pitau0,pdate0,pdeltat,phoriid,idf,domain_id,mode)
+!---------------------------------------------------------------------
+!- histbeg for irregular horizontal coordinates (see histb_all)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*) :: pfilename
+  INTEGER,INTENT(IN) :: pim
+  REAL,DIMENSION(pim),INTENT(IN) :: plon,plat
+  REAL,DIMENSION(:,:),INTENT(IN) :: plon_bounds,plat_bounds
+  INTEGER,INTENT(IN) :: pitau0
+  REAL,INTENT(IN) :: pdate0,pdeltat
+  INTEGER,INTENT(OUT) :: idf,phoriid
+  INTEGER,INTENT(IN),OPTIONAL :: domain_id
+  CHARACTER(LEN=*),OPTIONAL,INTENT(IN) :: mode
+!---------------------------------------------------------------------
+  CALL histb_all &
+ & (3,pfilename,pim,pim,pitau0,pdate0,pdeltat,phoriid,idf,  &
+ &  x_1d=plon,y_1d=plat,x_bnds=plon_bounds,y_bnds=plat_bounds, &
+ &  domain_id=domain_id,mode=mode)
+!-------------------------
+END SUBROUTINE histb_irreg
+!===
+SUBROUTINE histb_all &
+ & (k_typ,nc_name,pim,pjm,pitau0,pdate0,pdeltat,phoriid,idf, &
+ &  x_1d,y_1d,x_2d,y_2d,k_orx,k_szx,k_ory,k_szy, &
+ &  x_bnds,y_bnds,domain_id,mode)
+!---------------------------------------------------------------------
+!- General interface for horizontal grids.
+!- This subroutine initializes a netcdf file and returns the ID.
+!- It will set up the geographical space on which the data will be
+!- stored and offers the possibility of seting a zoom.
+!- In the case of irregular grids, all the data comes in as vectors
+!- and for the grid we have the coordinates of the 4 corners.
+!- It also gets the global parameters into the I/O subsystem.
+!-
+!- INPUT
+!-
+!- k_typ    : Type of the grid (1 rectilinear, 2 regular, 3 irregular)
+!- nc_name  : Name of the netcdf file to be created
+!- pim      : Size of arrays in longitude direction
+!- pjm      : Size of arrays in latitude direction (pjm=pim for type 3)
+!-
+!- pitau0   : time step at which the history tape starts
+!- pdate0   : The Julian date at which the itau was equal to 0
+!- pdeltat  : Time step, in seconds, of the counter itau
+!-            used in histwrite for instance
+!-
+!- OUTPUT
+!-
+!- phoriid  : Identifier of the horizontal grid
+!- idf      : Identifier of the file
+!-
+!- Optional INPUT arguments
+!-
+!- For rectilinear or irregular grid
+!- x_1d  : The longitudes
+!- y_1d  : The latitudes
+!- For regular grid
+!- x_2d  : The longitudes
+!- y_2d  : The latitudes
+!- One pair (x_1d,y_1d) or (x_2d,y_2d) must be supplied.
+!-
+!- For regular grid (reg1d or reg2d),
+!- the next 4 arguments allow to define a horizontal zoom
+!- for this file. It is assumed that all variables to come
+!- have the same index space. This can not be assumed for
+!- the z axis and thus we define the zoom in histdef.
+!- k_orx  : Origin of the slab of data within the X axis (pim)
+!- k_szx  : Size of the slab of data in X
+!- k_ory  : Origin of the slab of data within the Y axis (pjm)
+!- k_szy  : Size of the slab of data in Y
+!-
+!- For irregular grid.
+!- x_bnds : The boundaries of the grid in longitude
+!- y_bnds : The boundaries of the grid in latitude
+!-
+!- For all grids.
+!-
+!- domain_id  : Domain identifier
+!-
+!- mode       : String of (case insensitive) blank-separated words
+!-              defining the mode used to create the file.
+!-              Supported keywords : 32, 64
+!-              "32/64" defines the offset mode.
+!-              The default offset mode is 64 bits.
+!-              Keywords "NETCDF4" and "CLASSIC" are reserved
+!-              for future use.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: k_typ
+  CHARACTER(LEN=*),INTENT(IN) :: nc_name
+  INTEGER,INTENT(IN) :: pim,pjm
+  INTEGER,INTENT(IN) :: pitau0
+  REAL,INTENT(IN) :: pdate0,pdeltat
+  INTEGER,INTENT(OUT) :: idf,phoriid
+  REAL,DIMENSION(:),INTENT(IN),OPTIONAL :: x_1d,y_1d
+  REAL,DIMENSION(:,:),INTENT(IN),OPTIONAL :: x_2d,y_2d
+  INTEGER,INTENT(IN),OPTIONAL :: k_orx,k_szx,k_ory,k_szy
+  REAL,DIMENSION(:,:),INTENT(IN),OPTIONAL :: x_bnds,y_bnds
+  INTEGER,INTENT(IN),OPTIONAL :: domain_id
+  CHARACTER(LEN=*),OPTIONAL,INTENT(IN) :: mode
+!-
+  INTEGER :: nfid,iret,m_c
+  CHARACTER(LEN=120) :: file
+  CHARACTER(LEN=30) :: timenow
+  CHARACTER(LEN=11) :: c_nam
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF     (k_typ == 1) THEN
+    c_nam = 'histb_reg1d'
+  ELSEIF (k_typ == 2) THEN
+    c_nam = 'histb_reg2d'
+  ELSEIF (k_typ == 3) THEN
+    c_nam = 'histb_irreg'
+  ELSE
+    CALL ipslerr (3,"histbeg", &
+ &    'Illegal value of k_typ argument','in internal interface','?')
+  ENDIF
+!-
+  IF (l_dbg) WRITE(ipslout,*) c_nam//" 0.0"
+!-
+! Search for a free index
+!-
+  idf = -1
+  DO nfid=1,nb_files_max
+    IF (W_F(nfid)%ncfid < 0) THEN
+      idf = nfid; EXIT;
+    ENDIF
+  ENDDO
+  IF (idf < 0) THEN
+    CALL ipslerr (3,"histbeg", &
+   &  'Table of files too small. You should increase nb_files_max', &
+   &  'in histcom.f90 in order to accomodate all these files',' ')
+  ENDIF
+!-
+! 1.0 Transfering into the common for future use
+!-
+  IF (l_dbg) WRITE(ipslout,*) c_nam//" 1.0"
+!-
+  W_F(idf)%itau0  = pitau0
+  W_F(idf)%date0  = pdate0
+  W_F(idf)%deltat = pdeltat
+!-
+! 2.0 Initializes all variables for this file
+!-
+  IF (l_dbg) WRITE(ipslout,*) c_nam//" 2.0"
+!-
+  W_F(idf)%n_var = 0
+  W_F(idf)%n_tax = 0
+  W_F(idf)%n_hax = 0
+  W_F(idf)%n_zax = 0
+!-
+  IF ( (k_typ == 1).OR.(k_typ == 2) ) THEN
+    W_F(idf)%slab_ori(1:2) = (/ k_orx,k_ory /)
+    W_F(idf)%slab_siz(1:2)  = (/ k_szx,k_szy /)
+  ELSE
+    W_F(idf)%slab_ori(1:2) = (/ 1,1 /)
+    W_F(idf)%slab_siz(1:2) = (/ pim,1 /)
+  ENDIF
+!-
+! 3.0 Opening netcdf file and defining dimensions
+!-
+  IF (l_dbg) WRITE(ipslout,*) c_nam//" 3.0"
+!-
+! Add DOMAIN number and ".nc" suffix in file name if needed
+!-
+  file = nc_name
+  CALL flio_dom_file (file,domain_id)
+!-
+! Check the mode
+!? See fliocom for HDF4 ????????????????????????????????????????????????
+!-
+  IF (PRESENT(mode)) THEN
+    SELECT CASE (TRIM(mode))
+    CASE('32')
+      m_c = NF90_CLOBBER
+    CASE('64')
+      m_c = IOR(NF90_CLOBBER,NF90_64BIT_OFFSET)
+    CASE DEFAULT
+      CALL ipslerr (3,"histbeg", &
+ &      'Invalid argument mode for file :',TRIM(file), &
+ &      'Supported values are 32 or 64')
+    END SELECT
+  ELSE
+    m_c = IOR(NF90_CLOBBER,NF90_64BIT_OFFSET)
+  ENDIF
+!-
+! Create file
+!-
+  iret = NF90_CREATE(file,m_c,nfid)
+!-
+  IF     (k_typ == 1) THEN
+    iret = NF90_DEF_DIM(nfid,'lon',k_szx,W_F(idf)%xid)
+    iret = NF90_DEF_DIM(nfid,'lat',k_szy,W_F(idf)%yid)
+  ELSEIF (k_typ == 2) THEN
+    iret = NF90_DEF_DIM(nfid,'x',k_szx,W_F(idf)%xid)
+    iret = NF90_DEF_DIM(nfid,'y',k_szy,W_F(idf)%yid)
+  ELSEIF (k_typ == 3) THEN
+    iret = NF90_DEF_DIM(nfid,'x',pim,W_F(idf)%xid)
+    W_F(idf)%yid = W_F(idf)%xid
+  ENDIF
+!-
+! 4.0 Declaring the geographical coordinates and other attributes
+!-
+  IF (l_dbg) WRITE(ipslout,*) c_nam//" 4.0"
+!-
+  iret = NF90_PUT_ATT(nfid,NF90_GLOBAL,'Conventions','CF-1.1')
+  iret = NF90_PUT_ATT(nfid,NF90_GLOBAL,'file_name',TRIM(file))
+  iret = NF90_PUT_ATT(nfid,NF90_GLOBAL,'production',TRIM(model_name))
+  lock_modname = .TRUE.
+  CALL ioget_timestamp (timenow)
+  iret = NF90_PUT_ATT(nfid,NF90_GLOBAL,'TimeStamp',TRIM(timenow))
+!-
+! 5.0 Saving some important information on this file in the common
+!-
+  IF (l_dbg) WRITE(ipslout,*) c_nam//" 5.0"
+!-
+  IF (PRESENT(domain_id)) THEN
+    W_F(idf)%dom_id_svg = domain_id
+  ENDIF
+  W_F(idf)%name = TRIM(nc_name)
+  W_F(idf)%ncfid = nfid
+  IF ( (k_typ == 1).OR.(k_typ == 2) ) THEN
+    W_F(idf)%full_size(1:2) = (/ pim,pjm /)
+    W_F(idf)%regular=.TRUE.
+  ELSEIF (k_typ == 3) THEN
+    W_F(idf)%full_size(1:2) = (/ pim,1 /)
+    W_F(idf)%regular=.FALSE.
+  ENDIF
+!-
+! 6.0 storing the geographical coordinates
+!-
+  IF     (k_typ == 1) THEN
+    CALL histh_all &
+ &   (k_typ,idf,pim,pjm,' ','Default grid',phoriid, &
+ &    x_1d=x_1d,y_1d=y_1d)
+  ELSEIF (k_typ == 2) THEN
+    CALL histh_all &
+ &   (k_typ,idf,pim,pjm,' ','Default grid',phoriid, &
+ &    x_2d=x_2d,y_2d=y_2d)
+  ELSEIF (k_typ == 3) THEN
+    CALL histh_all &
+ &   (k_typ,idf,pim,pim,' ','Default grid',phoriid, &
+ &    x_1d=x_1d,y_1d=y_1d,x_bnds=x_bnds,y_bnds=y_bnds)
+  ENDIF
+!-----------------------
+END SUBROUTINE histb_all
+!===
+SUBROUTINE histglobal_attr &
+ & (idf,string_attr,glob_attr)
+!---------------------------------------------------------------------
+!- Definition of GLOBAL attribute for history files.
+!-
+!- INPUT
+!-
+!- idf     : The id of the file to which the grid should be added
+!- string_attr : name of the global attribute to be added
+!- glob_attr : global_attribute to be added
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: idf
+  CHARACTER(len=*),INTENT(IN) :: string_attr
+  CHARACTER(len=*),INTENT(IN) :: glob_attr
+
+  INTEGER :: iret,nfid
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+
+  nfid = W_F(idf)%ncfid
+  IF (l_dbg) WRITE(ipslout,*) "New global attribute : ",glob_attr
+  iret = NF90_PUT_ATT(nfid,NF90_GLOBAL,string_attr,glob_attr)
+!-----------------------
+END SUBROUTINE histglobal_attr
+!===
+SUBROUTINE histh_reg1d &
+ &  (idf,pim,plon,pjm,plat,phname,phtitle,phid)
+!---------------------------------------------------------------------
+!- histhori for 1d regular grid (see histh_all)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: idf,pim,pjm
+  REAL,INTENT(IN),DIMENSION(:) :: plon,plat
+  CHARACTER(LEN=*),INTENT(IN) :: phname,phtitle
+  INTEGER,INTENT(OUT) :: phid
+!---------------------------------------------------------------------
+  CALL histh_all &
+ & (1,idf,pim,pjm,phname,phtitle,phid,x_1d=plon,y_1d=plat)
+!-------------------------
+END SUBROUTINE histh_reg1d
+!===
+SUBROUTINE histh_reg2d &
+ & (idf,pim,plon,pjm,plat,phname,phtitle,phid)
+!---------------------------------------------------------------------
+!- histhori for 2d regular grid (see histh_all)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: idf,pim,pjm
+  REAL,INTENT(IN),DIMENSION(:,:) :: plon,plat
+  CHARACTER(LEN=*),INTENT(IN) :: phname,phtitle
+  INTEGER,INTENT(OUT) :: phid
+!---------------------------------------------------------------------
+  CALL histh_all &
+ & (2,idf,pim,pjm,phname,phtitle,phid,x_2d=plon,y_2d=plat)
+!-------------------------
+END SUBROUTINE histh_reg2d
+!===
+SUBROUTINE histh_irreg &
+ & (idf,pim,plon,plon_bounds,plat,plat_bounds,phname,phtitle,phid)
+!---------------------------------------------------------------------
+!- histhori for irregular grid (see histh_all)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: idf,pim
+  REAL,DIMENSION(:),INTENT(IN) :: plon,plat
+  REAL,DIMENSION(:,:),INTENT(IN) :: plon_bounds,plat_bounds
+  CHARACTER(LEN=*),INTENT(IN) :: phname,phtitle
+  INTEGER,INTENT(OUT) :: phid
+!---------------------------------------------------------------------
+  CALL histh_all &
+ & (3,idf,pim,pim,phname,phtitle,phid, &
+ &  x_1d=plon,y_1d=plat,x_bnds=plon_bounds,y_bnds=plat_bounds)
+!-------------------------
+END SUBROUTINE histh_irreg
+!===
+SUBROUTINE histh_all &
+ & (k_typ,idf,pim,pjm,phname,phtitle,phid, &
+ &  x_1d,y_1d,x_2d,y_2d,x_bnds,y_bnds)
+!---------------------------------------------------------------------
+!- General interface for horizontal grids.
+!- This subroutine is made to declare a new horizontal grid.
+!- It has to have the same number of points as
+!- the original and thus in this routine we will only
+!- add two variable (longitude and latitude).
+!- Any variable in the file can thus point to this pair
+!- through an attribute. This routine is very usefull
+!- to allow staggered grids.
+!-
+!- INPUT
+!-
+!- k_typ   : Type of the grid (1 rectilinear, 2 regular, 3 irregular)
+!- idf     : The id of the file to which the grid should be added
+!- pim     : Size in the longitude direction
+!- pjm     : Size in the latitude direction (pjm=pim for type 3)
+!- phname  : The name of grid
+!- phtitle : The title of the grid
+!-
+!- OUTPUT
+!-
+!- phid    : Id of the created grid
+!-
+!- Optional INPUT arguments
+!-
+!- For rectilinear or irregular grid
+!- x_1d  : The longitudes
+!- y_1d  : The latitudes
+!- For regular grid
+!- x_2d  : The longitudes
+!- y_2d  : The latitudes
+!- One pair (x_1d,y_1d) or (x_2d,y_2d) must be supplied.
+!-
+!- For irregular grid.
+!- x_bnds : The boundaries of the grid in longitude
+!- y_bnds : The boundaries of the grid in latitude
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: k_typ
+  INTEGER,INTENT(IN) :: idf,pim,pjm
+  CHARACTER(LEN=*),INTENT(IN) :: phname,phtitle
+  INTEGER,INTENT(OUT) :: phid
+  REAL,DIMENSION(:),INTENT(IN),OPTIONAL :: x_1d,y_1d
+  REAL,DIMENSION(:,:),INTENT(IN),OPTIONAL :: x_2d,y_2d
+  REAL,DIMENSION(:,:),INTENT(IN),OPTIONAL :: x_bnds,y_bnds
+!-
+  CHARACTER(LEN=25) :: lon_name,lat_name
+  CHARACTER(LEN=30) :: lonbound_name,latbound_name
+  INTEGER :: i_s,i_e
+  INTEGER,DIMENSION(2) :: dims,dims_b
+  INTEGER :: nbbounds
+  INTEGER :: nlonidb,nlatidb,twoid
+  LOGICAL :: transp = .FALSE.
+  REAL,ALLOCATABLE,DIMENSION(:,:) :: bounds_trans
+  REAL :: wmn,wmx
+  INTEGER :: nlonid,nlatid
+  INTEGER :: o_x,o_y,s_x,s_y
+  INTEGER :: iret,nfid
+  CHARACTER(LEN=11) :: c_nam
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF     (k_typ == 1) THEN
+    c_nam = 'histh_reg1d'
+  ELSEIF (k_typ == 2) THEN
+    c_nam = 'histh_reg2d'
+  ELSEIF (k_typ == 3) THEN
+    c_nam = 'histh_irreg'
+  ELSE
+    CALL ipslerr (3,"histhori", &
+ &    'Illegal value of k_typ argument','in internal interface','?')
+  ENDIF
+!-
+! 1.0 Check that all fits in the buffers
+!-
+  IF (    (pim /= W_F(idf)%full_size(1)) &
+ &    .OR.(W_F(idf)%regular.AND.(pjm /= W_F(idf)%full_size(2)))  &
+ &    .OR.(.NOT.W_F(idf)%regular.AND.(W_F(idf)%full_size(2) /= 1)) ) THEN
+    CALL ipslerr (3,"histhori", &
+ &   'The new horizontal grid does not have the same size', &
+ &   'as the one provided to histbeg. This is not yet ', &
+ &   'possible in the hist package.')
+  ENDIF
+!-
+! 1.1 Create all the variables needed
+!-
+  IF (l_dbg) WRITE(ipslout,*) c_nam//" 1.0"
+!-
+  nfid = W_F(idf)%ncfid
+!-
+  IF (k_typ == 3) THEN
+    IF     (SIZE(x_bnds,DIM=1) == pim) THEN
+      nbbounds = SIZE(x_bnds,DIM=2)
+      transp = .TRUE.
+    ELSEIF (SIZE(x_bnds,DIM=2) == pim) THEN
+      nbbounds = SIZE(x_bnds,DIM=1)
+      transp = .FALSE.
+    ELSE
+      CALL ipslerr (3,"histhori", &
+ &     'The boundary variable does not have any axis corresponding', &
+ &     'to the size of the longitude or latitude variable','.')
+    ENDIF
+    ALLOCATE(bounds_trans(nbbounds,pim))
+    iret = NF90_DEF_DIM(nfid,'nbnd',nbbounds,twoid)
+    dims_b(1:2) = (/ twoid,W_F(idf)%xid /)
+  ENDIF
+!-
+  dims(1:2) = (/ W_F(idf)%xid,W_F(idf)%yid /)
+!-
+  IF     (k_typ == 1) THEN
+    IF (W_F(idf)%n_hax == 0) THEN
+      lon_name = 'lon'
+      lat_name = 'lat'
+    ELSE
+      lon_name = 'lon_'//TRIM(phname)
+      lat_name = 'lat_'//TRIM(phname)
+    ENDIF
+  ELSEIF (k_typ == 2) THEN
+    IF (W_F(idf)%n_hax == 0) THEN
+      lon_name = 'nav_lon'
+      lat_name = 'nav_lat'
+    ELSE
+      lon_name = 'nav_lon_'//TRIM(phname)
+      lat_name = 'nav_lat_'//TRIM(phname)
+    ENDIF
+  ELSEIF (k_typ == 3) THEN
+    IF (W_F(idf)%n_hax == 0) THEN
+      lon_name = 'nav_lon'
+      lat_name = 'nav_lat'
+    ELSE
+      lon_name = 'nav_lon_'//TRIM(phname)
+      lat_name = 'nav_lat_'//TRIM(phname)
+    ENDIF
+    lonbound_name = TRIM(lon_name)//'_bounds'
+    latbound_name = TRIM(lat_name)//'_bounds'
+  ENDIF
+!-
+! 1.2 Save the informations
+!-
+  phid = W_F(idf)%n_hax+1
+  W_F(idf)%n_hax = phid
+  W_F(idf)%hax_name(phid,1:2) = (/ lon_name,lat_name /)
+!-
+! 2.0 Longitude
+!-
+  IF (l_dbg) WRITE(ipslout,*) c_nam//" 2.0"
+!-
+  i_s = 1;
+  IF ( (k_typ == 1).OR.(k_typ == 3) ) THEN
+    i_e = 1; wmn = MINVAL(x_1d); wmx = MAXVAL(x_1d);
+  ELSEIF (k_typ == 2) THEN
+    i_e = 2; wmn = MINVAL(x_2d); wmx = MAXVAL(x_2d);
+  ENDIF
+  iret = NF90_DEF_VAR(nfid,lon_name,NF90_REAL4,dims(i_s:i_e),nlonid)
+  IF (k_typ == 1) THEN
+    iret = NF90_PUT_ATT(nfid,nlonid,'axis',"X")
+  ENDIF
+  iret = NF90_PUT_ATT(nfid,nlonid,'standard_name',"longitude")
+  iret = NF90_PUT_ATT(nfid,nlonid,'units',"degrees_east")
+  iret = NF90_PUT_ATT(nfid,nlonid,'valid_min',REAL(wmn,KIND=4))
+  iret = NF90_PUT_ATT(nfid,nlonid,'valid_max',REAL(wmx,KIND=4))
+  iret = NF90_PUT_ATT(nfid,nlonid,'long_name',"Longitude")
+  iret = NF90_PUT_ATT(nfid,nlonid,'nav_model',TRIM(phtitle))
+!-
+  IF (k_typ == 3) THEN
+!---
+!-- 2.1 Longitude bounds
+!---
+    iret = NF90_PUT_ATT(nfid,nlonid,'bounds',TRIM(lonbound_name))
+    iret = NF90_DEF_VAR(nfid,lonbound_name,NF90_REAL4,dims_b(1:2),nlonidb)
+    iret = NF90_PUT_ATT(nfid,nlonidb,'long_name', &
+ &          'Boundaries for coordinate variable '//TRIM(lon_name))
+  ENDIF
+!-
+! 3.0 Latitude
+!-
+  IF (l_dbg) WRITE(ipslout,*) c_nam//" 3.0"
+!-
+  i_e = 2;
+  IF ( (k_typ == 1).OR.(k_typ == 3) ) THEN
+    i_s = 2; wmn = MINVAL(y_1d); wmx = MAXVAL(y_1d);
+  ELSEIF (k_typ == 2) THEN
+    i_s = 1; wmn = MINVAL(y_2d); wmx = MAXVAL(y_2d);
+  ENDIF
+  iret = NF90_DEF_VAR(nfid,lat_name,NF90_REAL4,dims(i_s:i_e),nlatid)
+  IF (k_typ == 1) THEN
+    iret = NF90_PUT_ATT(nfid,nlatid,'axis',"Y")
+  ENDIF
+!-
+  iret = NF90_PUT_ATT(nfid,nlatid,'standard_name',"latitude")
+  iret = NF90_PUT_ATT(nfid,nlatid,'units',"degrees_north")
+  iret = NF90_PUT_ATT(nfid,nlatid,'valid_min',REAL(wmn,KIND=4))
+  iret = NF90_PUT_ATT(nfid,nlatid,'valid_max',REAL(wmx,KIND=4))
+  iret = NF90_PUT_ATT(nfid,nlatid,'long_name',"Latitude")
+  iret = NF90_PUT_ATT(nfid,nlatid,'nav_model',TRIM(phtitle))
+!-
+  IF (k_typ == 3) THEN
+!---
+!-- 3.1 Latitude bounds
+!---
+    iret = NF90_PUT_ATT(nfid,nlatid,'bounds',TRIM(latbound_name))
+    iret = NF90_DEF_VAR(nfid,latbound_name,NF90_REAL4,dims_b(1:2),nlatidb)
+    iret = NF90_PUT_ATT(nfid,nlatidb,'long_name', &
+ &          'Boundaries for coordinate variable '//TRIM(lat_name))
+  ENDIF
+!-
+  iret = NF90_ENDDEF(nfid)
+!-
+! 4.0 storing the geographical coordinates
+!-
+  IF (l_dbg) WRITE(ipslout,*) c_nam//" 4.0"
+!-
+  IF ( (k_typ == 1).OR.(k_typ == 2) ) THEN
+    o_x = W_F(idf)%slab_ori(1)
+    o_y = W_F(idf)%slab_ori(2)
+    s_x = W_F(idf)%slab_siz(1)
+    s_y = W_F(idf)%slab_siz(2)
+!---
+!-- Transfer the longitude and  the latitude
+!---
+    IF     (k_typ == 1) THEN
+      iret = NF90_PUT_VAR(nfid,nlonid,x_1d(o_x:o_x+s_x-1))
+      iret = NF90_PUT_VAR(nfid,nlatid,y_1d(o_y:o_y+s_y-1))
+    ELSEIF (k_typ == 2) THEN
+      iret = NF90_PUT_VAR(nfid,nlonid, &
+ &            x_2d(o_x:o_x+s_x-1,o_y:o_y+s_y-1))
+      iret = NF90_PUT_VAR(nfid,nlatid, &
+ &            y_2d(o_x:o_x+s_x-1,o_y:o_y+s_y-1))
+    ENDIF
+  ELSEIF (k_typ == 3) THEN
+!---
+!-- Transfer the longitude and the longitude bounds
+!---
+    iret = NF90_PUT_VAR(nfid,nlonid,x_1d(1:pim))
+!---
+    IF (transp) THEN
+      bounds_trans = TRANSPOSE(x_bnds)
+    ELSE
+      bounds_trans = x_bnds
+    ENDIF
+    iret = NF90_PUT_VAR(nfid,nlonidb,bounds_trans(1:nbbounds,1:pim))
+!---
+!-- Transfer the latitude and the latitude bounds
+!---
+    iret = NF90_PUT_VAR(nfid,nlatid,y_1d(1:pim))
+!---
+    IF (transp) THEN
+      bounds_trans = TRANSPOSE(y_bnds)
+    ELSE
+      bounds_trans = y_bnds
+    ENDIF
+    iret = NF90_PUT_VAR(nfid,nlatidb,bounds_trans(1:nbbounds,1:pim))
+!---
+    DEALLOCATE(bounds_trans)
+  ENDIF
+!-
+  iret = NF90_REDEF(nfid)
+!-----------------------
+END SUBROUTINE histh_all
+!===
+SUBROUTINE histvert (idf,pzaxname,pzaxtitle,pzaxunit, &
+ &                   pzsize,pzvalues,pzaxid,pdirect)
+!---------------------------------------------------------------------
+!- This subroutine defines a vertical axis and returns it s id.
+!- It gives the user the possibility to the user to define many
+!- different vertical axes. For each variable defined with histdef a
+!- vertical axis can be specified with by it s ID.
+!-
+!- INPUT
+!-
+!- idf      : ID of the file the variable should be archived in
+!- pzaxname : Name of the vertical axis
+!- pzaxtitle: title of the vertical axis
+!- pzaxunit : Units of the vertical axis (no units if blank string)
+!- pzsize   : size of the vertical axis
+!- pzvalues : Coordinate values of the vetical axis
+!-
+!- pdirect  : is an optional argument which allows to specify the
+!-            the positive direction of the axis : up or down.
+!- OUTPUT
+!-
+!- pzaxid   : Returns the ID of the axis.
+!-            Note that this is not the netCDF ID !
+!-
+!- VERSION
+!-
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: idf,pzsize
+  CHARACTER(LEN=*),INTENT(IN) :: pzaxname,pzaxunit,pzaxtitle
+  REAL,INTENT(IN) :: pzvalues(pzsize)
+  INTEGER,INTENT(OUT) :: pzaxid
+  CHARACTER(LEN=*),INTENT(IN),OPTIONAL :: pdirect
+!-
+  INTEGER :: pos,iv,zdimid,zaxid_tmp
+  CHARACTER(LEN=70) :: str71
+  CHARACTER(LEN=20) :: direction
+  INTEGER :: iret,leng,nfid
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+! 1.0 Verifications :
+!    Do we have enough space for an extra axis ?
+!    Is the name already in use ?
+!-
+  IF (l_dbg) WRITE(ipslout,*) "histvert : 1.0 Verifications", &
+ &                      pzaxname,'---',pzaxunit,'---',pzaxtitle
+!-
+! Direction of the vertical axis. Can we get if from the user.
+!-
+  IF (PRESENT(pdirect)) THEN
+    direction = TRIM(pdirect)
+    CALL strlowercase (direction)
+  ELSE
+    direction = 'unknown'
+  ENDIF
+!-
+! Check the consistency of the attribute
+!-
+  IF (     PRESENT(pdirect)    &
+ &    .AND.(direction /= 'up') &
+ &    .AND.(direction /= 'down') ) THEN
+    direction = 'unknown'
+    CALL ipslerr (2,"histvert",&
+ & "The specified positive direction for the vertical axis is invalid.",&
+ & "The value must be up or down.","The attribute will not be written.")
+  ENDIF
+!-
+  IF (W_F(idf)%n_zax+1 > nb_zax_max) THEN
+    CALL ipslerr (3,"histvert", &
+   &  'Table of vertical axes too small. You should increase ',&
+   &  'nb_zax_max in histcom.f90 in order to accomodate all ', &
+   &  'these variables ')
+  ENDIF
+!-
+  iv = W_F(idf)%n_zax
+  IF (iv > 1) THEN
+    CALL find_str (W_F(idf)%zax_name(1:iv-1),pzaxname,pos)
+  ELSE
+    pos = 0
+  ENDIF
+!-
+  IF (pos > 0) THEN
+    WRITE(str71,'("Check variable ",A," in file",I3)') &
+ &    TRIM(pzaxname),idf
+    CALL ipslerr (3,"histvert", &
+ &    "Vertical axis already exists",TRIM(str71), &
+ &    "Can also be a wrong file ID in another declaration")
+  ENDIF
+!-
+  iv = W_F(idf)%n_zax+1
+!-
+! 2.0 Add the information to the file
+!-
+  IF (l_dbg) &
+ &  WRITE(ipslout,*) "histvert : 2.0 Add the information to the file"
+!-
+  nfid = W_F(idf)%ncfid
+!-
+  leng = MIN(LEN_TRIM(pzaxname),20)
+  iret = NF90_DEF_DIM (nfid,pzaxname(1:leng),pzsize,zaxid_tmp)
+  iret = NF90_DEF_VAR (nfid,pzaxname(1:leng),NF90_REAL4, &
+ &                     zaxid_tmp,zdimid)
+  iret = NF90_PUT_ATT (nfid,zdimid,'axis',"Z")
+  iret = NF90_PUT_ATT (nfid,zdimid,'standard_name',"model_level_number")
+  leng = MIN(LEN_TRIM(pzaxunit),20)
+  IF (leng > 0) THEN
+    iret = NF90_PUT_ATT (nfid,zdimid,'units',pzaxunit(1:leng))
+  ENDIF
+  IF (direction /= 'unknown') THEN
+    iret = NF90_PUT_ATT (nfid,zdimid,'positive',TRIM(direction))
+  ENDIF
+  iret = NF90_PUT_ATT (nfid,zdimid,'valid_min', &
+ &                     REAL(MINVAL(pzvalues(1:pzsize)),KIND=4))
+  iret = NF90_PUT_ATT (nfid,zdimid,'valid_max', &
+ &                     REAL(MAXVAL(pzvalues(1:pzsize)),KIND=4))
+  leng = MIN(LEN_TRIM(pzaxname),20)
+  iret = NF90_PUT_ATT (nfid,zdimid,'title',pzaxname(1:leng))
+  leng = MIN(LEN_TRIM(pzaxtitle),80)
+  iret = NF90_PUT_ATT (nfid,zdimid,'long_name',pzaxtitle(1:leng))
+!-
+  iret = NF90_ENDDEF (nfid)
+!-
+  iret = NF90_PUT_VAR (nfid,zdimid,pzvalues(1:pzsize))
+!-
+  iret = NF90_REDEF (nfid)
+!-
+!- 3.0 add the information to the common
+!-
+  IF (l_dbg) &
+  &  WRITE(ipslout,*) "histvert : 3.0 add the information to the common"
+!-
+  W_F(idf)%n_zax = iv
+  W_F(idf)%zax_size(iv) = pzsize
+  W_F(idf)%zax_name(iv) = pzaxname
+  W_F(idf)%zax_ids(iv) = zaxid_tmp
+  pzaxid = iv
+!----------------------
+END SUBROUTINE histvert
+!===
+SUBROUTINE histdef &
+ &  (idf,pvarname,ptitle,punit, &
+ &   pxsize,pysize,phoriid,pzsize,par_oriz,par_szz,pzid, &
+ &   xtype,popp,pfreq_opp,pfreq_wrt,var_range,standard_name)
+!---------------------------------------------------------------------
+!- With this subroutine each variable to be archived on the history
+!- tape should be declared.
+!-
+!- It gives the user the choise of operation
+!- to be performed on the variables, the frequency of this operation
+!- and finaly the frequency of the archiving.
+!-
+!- INPUT
+!-
+!- idf      : ID of the file the variable should be archived in
+!- pvarname : Name of the variable, short and easy to remember
+!- ptitle   : Full name of the variable
+!- punit    : Units of the variable (no units if blank string)
+!-
+!- The next 3 arguments give the size of that data
+!- that will be passed to histwrite. The zoom will be
+!- done there with the horizontal information obtained
+!- in histbeg and the vertical information to follow.
+!-
+!- pxsize   : Size in X direction (size of the data that will be
+!-            given to histwrite)
+!- pysize   : Size in Y direction
+!- phoriid  : ID of the horizontal axis
+!-
+!- The next two arguments give the vertical zoom to use.
+!-
+!- pzsize   : Size in Z direction (If 1 then no axis is declared
+!-            for this variable and pzid is not used)
+!- par_oriz : Off set of the zoom
+!- par_szz  : Size of the zoom
+!-
+!- pzid     : ID of the vertical axis to use. It has to have
+!-            the size of the zoom.
+!- xtype    : External netCDF type (hist_r4/hist_r8)
+!- popp     : Operation to be performed. The following options
+!-            exist today :
+!- inst : keeps instantaneous values for writting
+!- ave  : Computes the average from call between writes
+!- pfreq_opp: Frequency of this operation (in seconds)
+!- pfreq_wrt: Frequency at which the variable should be
+!-            written (in seconds)
+!- var_range: Range of the variable.
+!-            If the minimum is greater than the maximum,
+!-            the values will be calculated.
+!-
+!- VERSION
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: idf,pxsize,pysize,pzsize,pzid
+  INTEGER,INTENT(IN) :: par_oriz,par_szz,xtype,phoriid
+  CHARACTER(LEN=*),INTENT(IN) :: pvarname,punit,popp,ptitle
+  REAL,INTENT(IN) :: pfreq_opp,pfreq_wrt
+  REAL,DIMENSION(2),OPTIONAL,INTENT(IN) :: var_range
+  CHARACTER(LEN=*),OPTIONAL,INTENT(IN) :: standard_name
+!-
+  INTEGER :: iv
+  CHARACTER(LEN=70) :: str70,str71,str72
+  CHARACTER(LEN=20) :: tmp_name
+  CHARACTER(LEN=40) :: str40
+  CHARACTER(LEN=10) :: str10
+  CHARACTER(LEN=120) :: ex_topps
+  REAL :: un_an,un_jour,test_fopp,test_fwrt
+  INTEGER :: pos,buff_sz
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  ex_topps = 'ave, inst, t_min, t_max, t_sum, once, never, l_max, l_min'
+!-
+  W_F(idf)%n_var = W_F(idf)%n_var+1
+  iv = W_F(idf)%n_var
+!-
+  IF (iv > nb_var_max) THEN
+    CALL ipslerr (3,"histdef", &
+   &  'Table of variables too small. You should increase nb_var_max',&
+   &  'in histcom.f90 in order to accomodate all these variables', &
+   &  ' ')
+  ENDIF
+!-
+! 1.0 Transfer informations on the variable to the common
+!     and verify that it does not already exist
+!-
+  IF (l_dbg) WRITE(ipslout,*) "histdef : 1.0"
+!-
+  IF (iv > 1) THEN
+    CALL find_str (W_F(idf)%W_V(1:iv-1)%v_name,pvarname,pos)
+  ELSE
+    pos = 0
+  ENDIF
+!-
+  IF (pos > 0) THEN
+    str70 = "Variable already exists"
+    WRITE(str71,'("Check variable  ",a," in file",I3)') &
+ &    TRIM(pvarname),idf
+    str72 = "Can also be a wrong file ID in another declaration"
+    CALL ipslerr (3,"histdef",str70,str71,str72)
+  ENDIF
+!-
+  W_F(idf)%W_V(iv)%v_name = pvarname
+  W_F(idf)%W_V(iv)%title = ptitle
+  W_F(idf)%W_V(iv)%unit_name = punit
+  IF (PRESENT(standard_name)) THEN
+    W_F(idf)%W_V(iv)%std_name = standard_name
+  ELSE
+    W_F(idf)%W_V(iv)%std_name = ptitle
+  ENDIF
+  tmp_name = W_F(idf)%W_V(iv)%v_name
+!-
+! 1.1 decode the operations
+!-
+  W_F(idf)%W_V(iv)%fullop = popp
+  CALL buildop &
+ &  (TRIM(popp),ex_topps,W_F(idf)%W_V(iv)%topp,missing_val, &
+ &   W_F(idf)%W_V(iv)%sopp,W_F(idf)%W_V(iv)%scal, &
+ &   W_F(idf)%W_V(iv)%nbopp)
+!-
+! 1.2 If we have an even number of operations
+!     then we need to add identity
+!-
+  IF ( MOD(W_F(idf)%W_V(iv)%nbopp,2) == 0) THEN
+    W_F(idf)%W_V(iv)%nbopp = W_F(idf)%W_V(iv)%nbopp+1
+    W_F(idf)%W_V(iv)%sopp(W_F(idf)%W_V(iv)%nbopp) = 'ident'
+    W_F(idf)%W_V(iv)%scal(W_F(idf)%W_V(iv)%nbopp) = missing_val
+  ENDIF
+!-
+! 1.3 External type of the variable
+!-
+  IF (xtype == hist_r8) THEN
+    W_F(idf)%W_V(iv)%v_typ = hist_r8
+  ELSE
+    W_F(idf)%W_V(iv)%v_typ = hist_r4
+  ENDIF
+!-
+! 2.0 Put the size of the variable in the common and check
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "histdef : 2.0",idf,iv,W_F(idf)%W_V(iv)%nbopp, &
+ &    W_F(idf)%W_V(iv)%sopp(1:W_F(idf)%W_V(iv)%nbopp), &
+ &    W_F(idf)%W_V(iv)%scal(1:W_F(idf)%W_V(iv)%nbopp)
+  ENDIF
+!-
+  W_F(idf)%W_V(iv)%scsize(1:3) = (/ pxsize,pysize,pzsize /)
+  W_F(idf)%W_V(iv)%zorig(1:3) = &
+ &  (/ W_F(idf)%slab_ori(1),W_F(idf)%slab_ori(2),par_oriz /)
+  W_F(idf)%W_V(iv)%zsize(1:3) = &
+ &  (/ W_F(idf)%slab_siz(1),W_F(idf)%slab_siz(2),par_szz /)
+!-
+! Is the size of the full array the same as that of the coordinates ?
+!-
+  IF (    (pxsize > W_F(idf)%full_size(1)) &
+ &    .OR.(pysize > W_F(idf)%full_size(2)) ) THEN
+!-
+    str70 = "The size of the variable is different "// &
+ &          "from the one of the coordinates"
+    WRITE(str71,'("Size of coordinates :",2I4)') &
+ &   W_F(idf)%full_size(1),W_F(idf)%full_size(2)
+    WRITE(str72,'("Size declared for variable ",a," :",2I4)') &
+ &   TRIM(tmp_name),pxsize,pysize
+    CALL ipslerr (3,"histdef",str70,str71,str72)
+  ENDIF
+!-
+! Is the size of the zoom smaller than the coordinates ?
+!-
+  IF (    (W_F(idf)%full_size(1) < W_F(idf)%slab_siz(1)) &
+ &    .OR.(W_F(idf)%full_size(2) < W_F(idf)%slab_siz(2)) ) THEN
+    str70 = &
+ &   "Size of variable should be greater or equal to those of the zoom"
+    WRITE(str71,'("Size of XY zoom :",2I4)') &
+ &   W_F(idf)%slab_siz(1),W_F(idf)%slab_siz(2)
+    WRITE(str72,'("Size declared for variable ",A," :",2I4)') &
+ &   TRIM(tmp_name),pxsize,pysize
+    CALL ipslerr (3,"histdef",str70,str71,str72)
+  ENDIF
+!-
+! 2.1 We store the horizontal grid information with minimal
+!     and a fall back onto the default grid
+!-
+  IF ( (phoriid > 0).AND.(phoriid <= W_F(idf)%n_hax) ) THEN
+    W_F(idf)%W_V(iv)%h_axid = phoriid
+  ELSE
+    W_F(idf)%W_V(iv)%h_axid = 1
+    CALL ipslerr (2,"histdef", &
+   &  'We use the default grid for variable as an invalide',&
+   &  'ID was provided for variable : ',TRIM(pvarname))
+  ENDIF
+!-
+! 2.2 Check the vertical coordinates if needed
+!-
+  IF (par_szz > 1) THEN
+!-
+!-- Does the vertical coordinate exist ?
+!-
+    IF (pzid > W_F(idf)%n_zax) THEN
+      WRITE(str70, &
+ &    '("The vertical coordinate chosen for variable ",A)') &
+ &     TRIM(tmp_name)
+      str71 = " Does not exist."
+      CALL ipslerr (3,"histdef",str70,str71," ")
+    ENDIF
+!-
+!-- Is the vertical size of the variable equal to that of the axis ?
+!-
+    IF (par_szz /= W_F(idf)%zax_size(pzid)) THEN
+      str70 = "The size of the zoom does not correspond "// &
+ &            "to the size of the chosen vertical axis"
+      WRITE(str71,'("Size of zoom in z :",I4)') par_szz
+      WRITE(str72,'("Size declared for axis ",A," :",I4)') &
+ &     TRIM(W_F(idf)%zax_name(pzid)),W_F(idf)%zax_size(pzid)
+      CALL ipslerr (3,"histdef",str70,str71,str72)
+    ENDIF
+!-
+!-- Is the zoom smaller that the total size of the variable ?
+!-
+    IF (pzsize < par_szz) THEN
+      str70 = "The vertical size of variable "// &
+ &            "is smaller than that of the zoom."
+      WRITE(str71,'("Declared vertical size of data :",I5)') pzsize
+      WRITE(str72,'("Size of zoom for variable ",a," = ",I5)') &
+ &     TRIM(tmp_name),par_szz
+      CALL ipslerr (3,"histdef",str70,str71,str72)
+    ENDIF
+    W_F(idf)%W_V(iv)%z_axid = pzid
+  ELSE
+    W_F(idf)%W_V(iv)%z_axid = -99
+  ENDIF
+!-
+! 3.0 We get the size of the arrays histwrite will get
+!     and eventually allocate the time_buffer
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "histdef : 3.0"
+  ENDIF
+!-
+  buff_sz = W_F(idf)%W_V(iv)%zsize(1) &
+ &         *W_F(idf)%W_V(iv)%zsize(2) &
+ &         *W_F(idf)%W_V(iv)%zsize(3)
+!-
+  IF (     (TRIM(W_F(idf)%W_V(iv)%topp) /= "inst") &
+ &    .AND.(TRIM(W_F(idf)%W_V(iv)%topp) /= "once") &
+ &    .AND.(TRIM(W_F(idf)%W_V(iv)%topp) /= "never") )THEN
+    ALLOCATE(W_F(idf)%W_V(iv)%t_bf(buff_sz))
+    W_F(idf)%W_V(iv)%t_bf(:) = missing_val
+    IF (l_dbg) THEN
+      WRITE(ipslout,*) "histdef : 3.0 allocating time_buffer for", &
+ &      " idf = ",idf," iv = ",iv," size = ",buff_sz
+    ENDIF
+  ENDIF
+!-
+! 4.0 Transfer the frequency of the operations and check
+!     for validity. We have to pay attention to negative values
+!     of the frequency which indicate monthly time-steps.
+!     The strategy is to bring it back to seconds for the tests
+!-
+  IF (l_dbg) WRITE(ipslout,*) "histdef : 4.0"
+!-
+  W_F(idf)%W_V(iv)%freq_opp = pfreq_opp
+  W_F(idf)%W_V(iv)%freq_wrt = pfreq_wrt
+!-
+  CALL ioget_calendar(un_an,un_jour)
+  IF (pfreq_opp < 0) THEN
+    CALL ioget_calendar(un_an)
+    test_fopp = pfreq_opp*(-1.)*un_an/12.*un_jour
+  ELSE
+    test_fopp = pfreq_opp
+  ENDIF
+  IF (pfreq_wrt < 0) THEN
+    CALL ioget_calendar(un_an)
+    test_fwrt = pfreq_wrt*(-1.)*un_an/12.*un_jour
+  ELSE
+    test_fwrt = pfreq_wrt
+  ENDIF
+!-
+! 4.1 Frequency of operations and output should be larger than deltat !
+!-
+  IF (test_fopp < W_F(idf)%deltat) THEN
+    str70 = 'Frequency of operations should be larger than deltat'
+    WRITE(str71,'("It is not the case for variable ",a," :",F10.4)') &
+ &   TRIM(tmp_name),pfreq_opp
+    str72 = "PATCH : frequency set to deltat"
+!-
+    CALL ipslerr (2,"histdef",str70,str71,str72)
+!-
+    W_F(idf)%W_V(iv)%freq_opp = W_F(idf)%deltat
+  ENDIF
+!-
+  IF (test_fwrt < W_F(idf)%deltat) THEN
+    str70 = 'Frequency of output should be larger than deltat'
+    WRITE(str71,'("It is not the case for variable ",a," :",F10.4)') &
+ &   TRIM(tmp_name),pfreq_wrt
+    str72 = "PATCH : frequency set to deltat"
+!-
+    CALL ipslerr (2,"histdef",str70,str71,str72)
+!-
+    W_F(idf)%W_V(iv)%freq_wrt = W_F(idf)%deltat
+  ENDIF
+!-
+! 4.2 First the existence of the operation is tested and then
+!     its compaticility with the choice of frequencies
+!-
+  IF (TRIM(W_F(idf)%W_V(iv)%topp) == "inst") THEN
+    IF (test_fopp /= test_fwrt) THEN
+      str70 = 'For instantaneous output the frequency '// &
+ &            'of operations and output'
+      WRITE(str71, &
+ &     '("should be the same, this was not case for variable ",a)') &
+ &      TRIM(tmp_name)
+      str72 = "PATCH : The smalest frequency of both is used"
+      CALL ipslerr (2,"histdef",str70,str71,str72)
+      IF (test_fopp < test_fwrt) THEN
+        W_F(idf)%W_V(iv)%freq_opp = pfreq_opp
+        W_F(idf)%W_V(iv)%freq_wrt = pfreq_opp
+      ELSE
+        W_F(idf)%W_V(iv)%freq_opp = pfreq_wrt
+        W_F(idf)%W_V(iv)%freq_wrt = pfreq_wrt
+      ENDIF
+    ENDIF
+  ELSE IF (INDEX(ex_topps,TRIM(W_F(idf)%W_V(iv)%topp)) > 0) THEN
+    IF (test_fopp > test_fwrt) THEN
+      str70 = 'For averages the frequency of operations '// &
+ &            'should be smaller or equal'
+      WRITE(str71, &
+ &     '("to that of output. It is not the case for variable ",a)') &
+ &     TRIM(tmp_name)
+      str72 = 'PATCH : The output frequency is used for both'
+      CALL ipslerr (2,"histdef",str70,str71,str72)
+      W_F(idf)%W_V(iv)%freq_opp = pfreq_wrt
+    ENDIF
+  ELSE
+    WRITE (str70,'("Operation on variable ",A," is unknown")') &
+ &   TRIM(tmp_name)
+    WRITE (str71,'("operation requested is :",A)') &
+ &   W_F(idf)%W_V(iv)%topp
+    WRITE (str72,'("File ID :",I3)') idf
+    CALL ipslerr (3,"histdef",str70,str71,str72)
+  ENDIF
+!-
+! 5.0 Initialize other variables of the common
+!-
+  IF (l_dbg) WRITE(ipslout,*) "histdef : 5.0"
+!-
+  W_F(idf)%W_V(iv)%hist_wrt_rng = (PRESENT(var_range))
+  IF (W_F(idf)%W_V(iv)%hist_wrt_rng) THEN
+    W_F(idf)%W_V(iv)%hist_calc_rng = (var_range(1) > var_range(2))
+    IF (W_F(idf)%W_V(iv)%hist_calc_rng) THEN
+      W_F(idf)%W_V(iv)%hist_minmax(1:2) = &
+ &      (/ ABS(missing_val),-ABS(missing_val) /)
+    ELSE
+      W_F(idf)%W_V(iv)%hist_minmax(1:2) = var_range(1:2)
+    ENDIF
+  ENDIF
+!-
+! - freq_opp(idf,iv)/2./deltat(idf)
+  W_F(idf)%W_V(iv)%last_opp = W_F(idf)%itau0
+! - freq_wrt(idf,iv)/2./deltat(idf)
+  W_F(idf)%W_V(iv)%last_wrt = W_F(idf)%itau0
+! - freq_opp(idf,iv)/2./deltat(idf)
+  W_F(idf)%W_V(iv)%last_opp_chk = W_F(idf)%itau0
+! - freq_wrt(idf,iv)/2./deltat(idf)
+  W_F(idf)%W_V(iv)%last_wrt_chk = W_F(idf)%itau0
+  W_F(idf)%W_V(iv)%nb_opp = 0
+  W_F(idf)%W_V(iv)%nb_wrt = 0
+!-
+! 6.0 Get the time axis for this variable
+!-
+  IF (l_dbg) WRITE(ipslout,*) "histdef : 6.0"
+!-
+! No time axis for once, l_max, l_min or never operation
+!-
+  IF (     (TRIM(W_F(idf)%W_V(iv)%topp) /= 'once')  &
+ &    .AND.(TRIM(W_F(idf)%W_V(iv)%topp) /= 'never') &
+ &    .AND.(TRIM(W_F(idf)%W_V(iv)%topp) /= 'l_max') &
+ &    .AND.(TRIM(W_F(idf)%W_V(iv)%topp) /= 'l_min') ) THEN
+    IF (TRIM(W_F(idf)%W_V(iv)%topp) == 'inst') THEN
+      str10 = 't_inst_'
+    ELSE
+      str10 = 't_op_'
+    ENDIF
+    IF (W_F(idf)%W_V(iv)%freq_wrt > 0) THEN
+      WRITE (UNIT=str40,FMT='(A,I8.8)') &
+&      TRIM(str10),INT(W_F(idf)%W_V(iv)%freq_wrt)
+    ELSE
+      WRITE (UNIT=str40,FMT='(A,I2.2,"month")') &
+&      TRIM(str10),ABS(INT(W_F(idf)%W_V(iv)%freq_wrt))
+    ENDIF
+    CALL find_str (W_F(idf)%W_V(1:W_F(idf)%n_tax)%tax_name,str40,pos)
+    IF (pos < 0) THEN
+      W_F(idf)%n_tax = W_F(idf)%n_tax+1
+      W_F(idf)%W_V(iv)%l_bnd = &
+ &      (TRIM(W_F(idf)%W_V(iv)%topp) /= 'inst')
+      W_F(idf)%W_V(W_F(idf)%n_tax)%tax_name = str40
+      W_F(idf)%W_V(W_F(idf)%n_tax)%tax_last = 0
+      W_F(idf)%W_V(iv)%t_axid = W_F(idf)%n_tax
+    ELSE
+      W_F(idf)%W_V(iv)%t_axid = pos
+    ENDIF
+  ELSE
+    IF (l_dbg) THEN
+      WRITE(ipslout,*) "histdef : 7.0 ",TRIM(W_F(idf)%W_V(iv)%topp),'----'
+    ENDIF
+    W_F(idf)%W_V(iv)%t_axid = -99
+  ENDIF
+!-
+! 7.0 prepare frequence of writing and operation
+!     for never or once operation
+!-
+  IF (    (TRIM(W_F(idf)%W_V(iv)%topp) == 'once')  &
+ &    .OR.(TRIM(W_F(idf)%W_V(iv)%topp) == 'never') ) THEN
+    W_F(idf)%W_V(iv)%freq_opp = 0.
+    W_F(idf)%W_V(iv)%freq_wrt = 0.
+  ENDIF
+!---------------------
+END SUBROUTINE histdef
+!===
+SUBROUTINE histend (idf)
+!---------------------------------------------------------------------
+!- This subroutine end the decalaration of variables and sets the
+!- time axes in the netcdf file and puts it into the write mode.
+!-
+!- INPUT
+!-
+!- idf : ID of the file to be worked on
+!-
+!- VERSION
+!-
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: idf
+!-
+  INTEGER :: nfid,nvid,iret,ndim,iv,itx,ziv,itax,dim_cnt
+  INTEGER,DIMENSION(4) :: dims
+  INTEGER :: year,month,day,hours,minutes
+  REAL :: sec
+  REAL :: rtime0
+  CHARACTER(LEN=30) :: str30
+  CHARACTER(LEN=35) :: str35
+  CHARACTER(LEN=120) :: assoc
+  CHARACTER(LEN=70) :: str70
+  CHARACTER(LEN=3),DIMENSION(12) :: cal =   &
+ &  (/ 'JAN','FEB','MAR','APR','MAY','JUN', &
+ &     'JUL','AUG','SEP','OCT','NOV','DEC' /)
+  CHARACTER(LEN=7) :: tmp_opp
+  LOGICAL :: l_b
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  nfid = W_F(idf)%ncfid
+!-
+! 1.0 Create the time axes
+!-
+  IF (l_dbg) WRITE(ipslout,*) "histend : 1.0"
+!-
+! 1.1 Define the time dimensions needed for this file
+!-
+  iret = NF90_DEF_DIM (nfid,'time_counter', &
+ &                     NF90_UNLIMITED,W_F(idf)%tid)
+  DO iv=1,W_F(idf)%n_var
+    IF (W_F(idf)%W_V(iv)%l_bnd) THEN
+      iret = NF90_DEF_DIM (nfid,'tbnds',2,W_F(idf)%bid)
+      EXIT
+    ENDIF
+  ENDDO
+!-
+! 1.2 Define all the time axes needed for this file
+!-
+  DO itx=1,W_F(idf)%n_tax
+    dims(1) = W_F(idf)%tid
+    l_b = (INDEX(W_F(idf)%W_V(itx)%tax_name,"t_op_") == 1)
+    IF (itx > 1) THEN
+      str30 = W_F(idf)%W_V(itx)%tax_name
+    ELSE
+      str30 = "time_counter"
+    ENDIF
+    IF (l_b) THEN
+      str35 = TRIM(str30)//'_bnds'
+    ENDIF
+    iret = NF90_DEF_VAR (nfid,TRIM(str30),NF90_REAL8, &
+ &          dims(1),W_F(idf)%W_V(itx)%tdimid)
+    IF (itx <= 1) THEN
+      iret = NF90_PUT_ATT (nfid,W_F(idf)%W_V(itx)%tdimid,'axis',"T")
+    ENDIF
+    iret = NF90_PUT_ATT (nfid,W_F(idf)%W_V(itx)%tdimid, &
+ &          'standard_name',"time")
+!---
+!   To transform the current itau into a real date and take it
+!   as the origin of the file requires the time counter to change.
+!   Thus it is an operation the user has to ask for.
+!   This function should thus only be re-instated
+!   if there is a ioconf routine to control it.
+!---
+!-- rtime0 = itau2date(itau0(idf),date0(idf),deltat(idf))
+    rtime0 = W_F(idf)%date0
+!-
+    CALL ju2ymds(rtime0,year,month,day,sec)
+!---
+!   Catch any error induced by a change in calendar !
+!---
+    IF (year < 0) THEN
+      year = 2000+year
+    ENDIF
+!-
+    hours = INT(sec/(60.*60.))
+    minutes = INT((sec-hours*60.*60.)/60.)
+    sec = sec-(hours*60.*60.+minutes*60.)
+!-
+    WRITE (UNIT=str70, &
+ &   FMT='(A,I4.4,"-",I2.2,"-",I2.2," ",I2.2,":",I2.2,":",I2.2)') &
+ &    'seconds since ',year,month,day,hours,minutes,INT(sec)
+    iret = NF90_PUT_ATT (nfid,W_F(idf)%W_V(itx)%tdimid, &
+ &           'units',TRIM(str70))
+!-
+    CALL ioget_calendar (str30)
+    iret = NF90_PUT_ATT (nfid,W_F(idf)%W_V(itx)%tdimid, &
+ &           'calendar',TRIM(str30))
+!-
+    iret = NF90_PUT_ATT (nfid,W_F(idf)%W_V(itx)%tdimid, &
+ &           'title','Time')
+!-
+    iret = NF90_PUT_ATT (nfid,W_F(idf)%W_V(itx)%tdimid, &
+ &           'long_name','Time axis')
+!-
+    WRITE (UNIT=str70, &
+ &   FMT='(" ",I4.4,"-",A3,"-",I2.2," ",I2.2,":",I2.2,":",I2.2)') &
+ &    year,cal(month),day,hours,minutes,INT(sec)
+    iret = NF90_PUT_ATT (nfid,W_F(idf)%W_V(itx)%tdimid, &
+ &           'time_origin',TRIM(str70))
+!---
+    IF (l_b) THEN
+      iret = NF90_PUT_ATT (nfid,W_F(idf)%W_V(itx)%tdimid, &
+ &             'bounds',TRIM(str35))
+      dims(1:2) = (/ W_F(idf)%bid,W_F(idf)%tid /)
+      iret = NF90_DEF_VAR (nfid,TRIM(str35),NF90_REAL8, &
+ &            dims(1:2),W_F(idf)%W_V(itx)%tbndid)
+    ENDIF
+  ENDDO
+!-
+! 2.0 declare the variables
+!-
+  IF (l_dbg) WRITE(ipslout,*) "histend : 2.0"
+!-
+  DO iv=1,W_F(idf)%n_var
+!---
+    itax = W_F(idf)%W_V(iv)%t_axid
+!---
+    IF (W_F(idf)%regular) THEN
+      dims(1:2) = (/ W_F(idf)%xid,W_F(idf)%yid /)
+      dim_cnt = 2
+    ELSE
+      dims(1) = W_F(idf)%xid
+      dim_cnt = 1
+    ENDIF
+!---
+    tmp_opp = W_F(idf)%W_V(iv)%topp
+    ziv = W_F(idf)%W_V(iv)%z_axid
+!---
+!   2.1 dimension of field
+!---
+    IF ((TRIM(tmp_opp) /= 'never')) THEN
+      IF (     (TRIM(tmp_opp) /= 'once')  &
+     &    .AND.(TRIM(tmp_opp) /= 'l_max') &
+     &    .AND.(TRIM(tmp_opp) /= 'l_min') ) THEN
+        IF (ziv == -99) THEN
+          ndim = dim_cnt+1
+          dims(dim_cnt+1:dim_cnt+2) = (/ W_F(idf)%tid,0 /)
+        ELSE
+          ndim = dim_cnt+2
+          dims(dim_cnt+1:dim_cnt+2) = &
+ &         (/ W_F(idf)%zax_ids(ziv),W_F(idf)%tid /)
+        ENDIF
+      ELSE
+        IF (ziv == -99) THEN
+          ndim = dim_cnt
+          dims(dim_cnt+1:dim_cnt+2) = (/ 0,0 /)
+        ELSE
+          ndim = dim_cnt+1
+          dims(dim_cnt+1:dim_cnt+2) = (/ W_F(idf)%zax_ids(ziv),0 /)
+        ENDIF
+      ENDIF
+!-
+      iret = NF90_DEF_VAR (nfid,TRIM(W_F(idf)%W_V(iv)%v_name), &
+ &             W_F(idf)%W_V(iv)%v_typ,dims(1:ABS(ndim)),nvid)
+!-
+      W_F(idf)%W_V(iv)%ncvid = nvid
+!-
+      IF (LEN_TRIM(W_F(idf)%W_V(iv)%unit_name) > 0) THEN
+        iret = NF90_PUT_ATT (nfid,nvid,'units', &
+ &                           TRIM(W_F(idf)%W_V(iv)%unit_name))
+      ENDIF
+      iret = NF90_PUT_ATT (nfid,nvid,'standard_name', &
+ &                         TRIM(W_F(idf)%W_V(iv)%std_name))
+!-
+      IF (W_F(idf)%W_V(iv)%v_typ == hist_r8) THEN
+        iret = NF90_PUT_ATT (nfid,nvid,'_FillValue',NF90_FILL_REAL8)
+      ELSE
+        iret = NF90_PUT_ATT (nfid,nvid,'_FillValue',NF90_FILL_REAL4)
+      ENDIF
+      IF (W_F(idf)%W_V(iv)%hist_wrt_rng) THEN
+        IF (W_F(idf)%W_V(iv)%v_typ == hist_r8) THEN
+          iret = NF90_PUT_ATT (nfid,nvid,'valid_min', &
+ &                 REAL(W_F(idf)%W_V(iv)%hist_minmax(1),KIND=8))
+          iret = NF90_PUT_ATT (nfid,nvid,'valid_max', &
+ &                 REAL(W_F(idf)%W_V(iv)%hist_minmax(2),KIND=8))
+        ELSE
+          iret = NF90_PUT_ATT (nfid,nvid,'valid_min', &
+ &                 REAL(W_F(idf)%W_V(iv)%hist_minmax(1),KIND=4))
+          iret = NF90_PUT_ATT (nfid,nvid,'valid_max', &
+ &                 REAL(W_F(idf)%W_V(iv)%hist_minmax(2),KIND=4))
+        ENDIF
+      ENDIF
+      iret = NF90_PUT_ATT (nfid,nvid,'long_name', &
+ &                         TRIM(W_F(idf)%W_V(iv)%title))
+      iret = NF90_PUT_ATT (nfid,nvid,'online_operation', &
+ &                         TRIM(W_F(idf)%W_V(iv)%fullop))
+!-
+      SELECT CASE(ndim)
+      CASE(-3,2:4)
+      CASE DEFAULT
+        CALL ipslerr (3,"histend", &
+       &  'less than 2 or more than 4 dimensions are not', &
+       &  'allowed at this stage',' ')
+      END SELECT
+!-
+      assoc=TRIM(W_F(idf)%hax_name(W_F(idf)%W_V(iv)%h_axid,2)) &
+ &   //' '//TRIM(W_F(idf)%hax_name(W_F(idf)%W_V(iv)%h_axid,1))
+!-
+      ziv = W_F(idf)%W_V(iv)%z_axid
+      IF (ziv > 0) THEN
+        str30 = W_F(idf)%zax_name(ziv)
+        assoc = TRIM(str30)//' '//TRIM(assoc)
+      ENDIF
+!-
+      IF (itax > 0) THEN
+        IF (itax > 1) THEN
+          str30 = W_F(idf)%W_V(itax)%tax_name
+        ELSE
+          str30 = "time_counter"
+        ENDIF
+        assoc = TRIM(str30)//' '//TRIM(assoc)
+!-
+        IF (l_dbg) THEN
+          WRITE(ipslout,*) "histend : 2.0.n, freq_opp, freq_wrt", &
+ &          W_F(idf)%W_V(iv)%freq_opp,W_F(idf)%W_V(iv)%freq_wrt
+        ENDIF
+!-
+        iret = NF90_PUT_ATT (nfid,nvid,'interval_operation', &
+ &                           REAL(W_F(idf)%W_V(iv)%freq_opp,KIND=4))
+        iret = NF90_PUT_ATT (nfid,nvid,'interval_write', &
+ &                           REAL(W_F(idf)%W_V(iv)%freq_wrt,KIND=4))
+      ENDIF
+      iret = NF90_PUT_ATT (nfid,nvid,'coordinates',TRIM(assoc))
+    ENDIF
+  ENDDO
+!-
+! 2.2 Add DOMAIN attributes if needed
+!-
+  IF (W_F(idf)%dom_id_svg >= 0) THEN
+    CALL flio_dom_att (nfid,W_F(idf)%dom_id_svg)
+  ENDIF
+!-
+! 3.0 Put the netcdf file into write mode
+!-
+  IF (l_dbg) WRITE(ipslout,*) "histend : 3.0"
+!-
+  iret = NF90_ENDDEF (nfid)
+!-
+! 4.0 Give some informations to the user
+!-
+  IF (l_dbg) WRITE(ipslout,*) "histend : 4.0"
+!-
+  WRITE(str70,'("All variables have been initialized on file :",I3)') idf
+  CALL ipslerr (1,'histend',str70,'',' ')
+!---------------------
+END SUBROUTINE histend
+!===
+SUBROUTINE histwrite_r1d (idf,pvarname,pitau,pdata,nbindex,nindex)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: idf,pitau,nbindex
+  INTEGER,DIMENSION(nbindex),INTENT(IN) :: nindex
+  REAL,DIMENSION(:),INTENT(IN) :: pdata
+  CHARACTER(LEN=*),INTENT(IN) :: pvarname
+!---------------------------------------------------------------------
+  CALL histw_rnd (idf,pvarname,pitau,nbindex,nindex,pdata_1d=pdata)
+!---------------------------
+END SUBROUTINE histwrite_r1d
+!===
+SUBROUTINE histwrite_r2d (idf,pvarname,pitau,pdata,nbindex,nindex)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: idf,pitau,nbindex
+  INTEGER,DIMENSION(nbindex),INTENT(IN) :: nindex
+  REAL,DIMENSION(:,:),INTENT(IN) :: pdata
+  CHARACTER(LEN=*),INTENT(IN) :: pvarname
+!---------------------------------------------------------------------
+  CALL histw_rnd (idf,pvarname,pitau,nbindex,nindex,pdata_2d=pdata)
+!---------------------------
+END SUBROUTINE histwrite_r2d
+!===
+SUBROUTINE histwrite_r3d (idf,pvarname,pitau,pdata,nbindex,nindex)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: idf,pitau,nbindex
+  INTEGER,DIMENSION(nbindex),INTENT(IN) :: nindex
+  REAL,DIMENSION(:,:,:),INTENT(IN) :: pdata
+  CHARACTER(LEN=*),INTENT(IN) :: pvarname
+!---------------------------------------------------------------------
+  CALL histw_rnd (idf,pvarname,pitau,nbindex,nindex,pdata_3d=pdata)
+!---------------------------
+END SUBROUTINE histwrite_r3d
+!===
+SUBROUTINE histw_rnd (idf,pvarname,pitau,nbindex,nindex, &
+  &                   pdata_1d,pdata_2d,pdata_3d)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: idf,pitau,nbindex
+  INTEGER,DIMENSION(nbindex),INTENT(IN) :: nindex
+  CHARACTER(LEN=*),INTENT(IN) :: pvarname
+  REAL,DIMENSION(:),INTENT(IN),OPTIONAL     :: pdata_1d
+  REAL,DIMENSION(:,:),INTENT(IN),OPTIONAL   :: pdata_2d
+  REAL,DIMENSION(:,:,:),INTENT(IN),OPTIONAL :: pdata_3d
+!-
+  LOGICAL :: do_oper,do_write,largebuf,l1d,l2d,l3d
+  INTEGER :: iv,io,nbpt_out
+  INTEGER              :: nbpt_in1
+  INTEGER,DIMENSION(2) :: nbpt_in2
+  INTEGER,DIMENSION(3) :: nbpt_in3
+  REAL,ALLOCATABLE,DIMENSION(:),SAVE :: tbf_1
+  CHARACTER(LEN=7) :: tmp_opp
+  CHARACTER(LEN=13) :: c_nam
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  l1d=PRESENT(pdata_1d); l2d=PRESENT(pdata_2d); l3d=PRESENT(pdata_3d);
+  IF      (l1d) THEN
+    c_nam = 'histwrite_r1d'
+  ELSE IF (l2d) THEN
+    c_nam = 'histwrite_r2d'
+  ELSE IF (l3d) THEN
+    c_nam = 'histwrite_r3d'
+  ENDIF
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "histwrite : ",c_nam
+  ENDIF
+!-
+! 1.0 Try to catch errors like specifying the wrong file ID.
+!     Thanks Marine for showing us what errors users can make !
+!-
+  IF ( (idf < 1).OR.(idf > nb_files_max) ) THEN
+    CALL ipslerr (3,"histwrite", &
+ &    'Illegal file ID in the histwrite of variable',pvarname,' ')
+  ENDIF
+!-
+! 1.1 Find the id of the variable to be written and the real time
+!-
+  CALL histvar_seq (idf,pvarname,iv)
+!-
+! 2.0 do nothing for never operation
+!-
+  tmp_opp = W_F(idf)%W_V(iv)%topp
+!-
+  IF (TRIM(tmp_opp) == "never") THEN
+    W_F(idf)%W_V(iv)%last_opp_chk = -99
+    W_F(idf)%W_V(iv)%last_wrt_chk = -99
+  ENDIF
+!-
+! 3.0 We check if we need to do an operation
+!-
+  IF (W_F(idf)%W_V(iv)%last_opp_chk == pitau) THEN
+    CALL ipslerr (3,"histwrite", &
+ &    'This variable has already been analysed at the present', &
+ &    'time step',TRIM(pvarname))
+  ENDIF
+!-
+  CALL isittime &
+ &  (pitau,W_F(idf)%date0,W_F(idf)%deltat, &
+ &   W_F(idf)%W_V(iv)%freq_opp, &
+ &   W_F(idf)%W_V(iv)%last_opp, &
+ &   W_F(idf)%W_V(iv)%last_opp_chk,do_oper)
+!-
+! 4.0 We check if we need to write the data
+!-
+  IF (W_F(idf)%W_V(iv)%last_wrt_chk == pitau) THEN
+    CALL ipslerr (3,"histwrite", &
+ &    'This variable as already been written for the present', &
+ &    'time step',' ')
+  ENDIF
+!-
+  CALL isittime &
+ &  (pitau,W_F(idf)%date0,W_F(idf)%deltat, &
+ &   W_F(idf)%W_V(iv)%freq_wrt, &
+ &   W_F(idf)%W_V(iv)%last_wrt, &
+ &   W_F(idf)%W_V(iv)%last_wrt_chk,do_write)
+!-
+! 5.0 histwrite called
+!-
+  IF (do_oper.OR.do_write) THEN
+!-
+!-- 5.1 Get the sizes of the data we will handle
+!-
+    IF (W_F(idf)%W_V(iv)%datasz_in(1) <= 0) THEN
+!---- There is the risk here that the user has over-sized the array.
+!---- But how can we catch this ?
+!---- In the worst case we will do impossible operations
+!---- on part of the data !
+      W_F(idf)%W_V(iv)%datasz_in(1:3) = -1
+      IF      (l1d) THEN
+        W_F(idf)%W_V(iv)%datasz_in(1) = SIZE(pdata_1d)
+      ELSE IF (l2d) THEN
+        W_F(idf)%W_V(iv)%datasz_in(1) = SIZE(pdata_2d,DIM=1)
+        W_F(idf)%W_V(iv)%datasz_in(2) = SIZE(pdata_2d,DIM=2)
+      ELSE IF (l3d) THEN
+        W_F(idf)%W_V(iv)%datasz_in(1) = SIZE(pdata_3d,DIM=1)
+        W_F(idf)%W_V(iv)%datasz_in(2) = SIZE(pdata_3d,DIM=2)
+        W_F(idf)%W_V(iv)%datasz_in(3) = SIZE(pdata_3d,DIM=3)
+      ENDIF
+    ENDIF
+!-
+!-- 5.2 The maximum size of the data will give the size of the buffer
+!-
+    IF (W_F(idf)%W_V(iv)%datasz_max <= 0) THEN
+      largebuf = .FALSE.
+      DO io=1,W_F(idf)%W_V(iv)%nbopp
+        IF (INDEX(fuchnbout,W_F(idf)%W_V(iv)%sopp(io)) > 0) THEN
+          largebuf = .TRUE.
+        ENDIF
+      ENDDO
+      IF (largebuf) THEN
+        W_F(idf)%W_V(iv)%datasz_max = &
+ &        W_F(idf)%W_V(iv)%scsize(1) &
+ &       *W_F(idf)%W_V(iv)%scsize(2) &
+ &       *W_F(idf)%W_V(iv)%scsize(3)
+      ELSE
+        IF      (l1d) THEN
+          W_F(idf)%W_V(iv)%datasz_max = &
+ &          W_F(idf)%W_V(iv)%datasz_in(1)
+        ELSE IF (l2d) THEN
+          W_F(idf)%W_V(iv)%datasz_max = &
+ &          W_F(idf)%W_V(iv)%datasz_in(1) &
+ &         *W_F(idf)%W_V(iv)%datasz_in(2)
+        ELSE IF (l3d) THEN
+          W_F(idf)%W_V(iv)%datasz_max = &
+ &          W_F(idf)%W_V(iv)%datasz_in(1) &
+ &         *W_F(idf)%W_V(iv)%datasz_in(2) &
+ &         *W_F(idf)%W_V(iv)%datasz_in(3)
+        ENDIF
+      ENDIF
+    ENDIF
+!-
+    IF (.NOT.ALLOCATED(tbf_1)) THEN
+      IF (l_dbg) THEN
+        WRITE(ipslout,*) &
+ &       c_nam//" : allocate tbf_1 for size = ", &
+ &       W_F(idf)%W_V(iv)%datasz_max
+      ENDIF
+      ALLOCATE(tbf_1(W_F(idf)%W_V(iv)%datasz_max))
+    ELSE IF (W_F(idf)%W_V(iv)%datasz_max > SIZE(tbf_1)) THEN
+      IF (l_dbg) THEN
+        WRITE(ipslout,*) &
+ &       c_nam//" : re-allocate tbf_1 for size = ", &
+ &       W_F(idf)%W_V(iv)%datasz_max
+      ENDIF
+      DEALLOCATE(tbf_1)
+      ALLOCATE(tbf_1(W_F(idf)%W_V(iv)%datasz_max))
+    ENDIF
+!-
+!-- We have to do the first operation anyway.
+!-- Thus we do it here and change the ranke
+!-- of the data at the same time. This should speed up things.
+!-
+    nbpt_out = W_F(idf)%W_V(iv)%datasz_max
+    IF      (l1d) THEN
+      nbpt_in1 = W_F(idf)%W_V(iv)%datasz_in(1)
+      CALL mathop (W_F(idf)%W_V(iv)%sopp(1),nbpt_in1,pdata_1d, &
+ &                 missing_val,nbindex,nindex, &
+ &                 W_F(idf)%W_V(iv)%scal(1),nbpt_out,tbf_1)
+    ELSE IF (l2d) THEN
+      nbpt_in2(1:2) = W_F(idf)%W_V(iv)%datasz_in(1:2)
+      CALL mathop (W_F(idf)%W_V(iv)%sopp(1),nbpt_in2,pdata_2d, &
+ &                 missing_val,nbindex,nindex, &
+ &                 W_F(idf)%W_V(iv)%scal(1),nbpt_out,tbf_1)
+    ELSE IF (l3d) THEN
+      nbpt_in3(1:3) = W_F(idf)%W_V(iv)%datasz_in(1:3)
+      CALL mathop (W_F(idf)%W_V(iv)%sopp(1),nbpt_in3,pdata_3d, &
+ &                 missing_val,nbindex,nindex, &
+ &                 W_F(idf)%W_V(iv)%scal(1),nbpt_out,tbf_1)
+    ENDIF
+    CALL histwrite_real (idf,iv,pitau,nbpt_out, &
+ &            tbf_1,nbindex,nindex,do_oper,do_write)
+  ENDIF
+!-
+! 6.0 Manage time steps
+!-
+  IF ((TRIM(tmp_opp) /= "once").AND.(TRIM(tmp_opp) /= "never")) THEN
+    W_F(idf)%W_V(iv)%last_opp_chk = pitau
+    W_F(idf)%W_V(iv)%last_wrt_chk = pitau
+  ELSE
+    W_F(idf)%W_V(iv)%last_opp_chk = -99
+    W_F(idf)%W_V(iv)%last_wrt_chk = -99
+  ENDIF
+!-----------------------
+END SUBROUTINE histw_rnd
+!===
+SUBROUTINE histwrite_real &
+ & (idf,iv,pitau,nbdpt,tbf_1,nbindex,nindex,do_oper,do_write)
+!---------------------------------------------------------------------
+!- This subroutine is internal and does the calculations and writing
+!- if needed. At a later stage it should be split into an operation
+!- and writing subroutines.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: idf,pitau,iv, &
+ &                      nbindex,nindex(nbindex),nbdpt
+  REAL,DIMENSION(:)  :: tbf_1
+  LOGICAL,INTENT(IN) :: do_oper,do_write
+!-
+  INTEGER :: tsz,nfid,nvid,iret,itax,io,nbin,nbout
+  INTEGER :: nx,ny,nz,ky,kz,kt,kc
+  INTEGER,DIMENSION(4) :: corner,edges
+  INTEGER :: itime
+  LOGICAL :: flag
+!-
+  REAL :: rtime
+  REAL,DIMENSION(2) :: t_bnd
+  CHARACTER(LEN=7) :: tmp_opp
+  REAL,ALLOCATABLE,DIMENSION(:),SAVE :: tbf_2
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "histwrite 0.0 : VAR : ",W_F(idf)%W_V(iv)%v_name
+    WRITE(ipslout,*) "histwrite 0.0 : nbindex :",nbindex
+    WRITE(ipslout,*) "histwrite 0.0 : nindex  :",nindex(1:MIN(3,nbindex)),'...'
+  ENDIF
+!-
+! The sizes which can be encoutered
+!-
+  tsz =  W_F(idf)%W_V(iv)%zsize(1) &
+ &      *W_F(idf)%W_V(iv)%zsize(2) &
+ &      *W_F(idf)%W_V(iv)%zsize(3)
+!-
+! 1.0 We allocate and the temporary space needed for operations.
+!     The buffers are only deallocated when more space is needed.
+!     This reduces the umber of allocates but increases memory needs.
+!-
+  IF (.NOT.ALLOCATED(tbf_2)) THEN
+    IF (l_dbg) THEN
+      WRITE(ipslout,*) "histwrite_real 1.1 allocate tbf_2 ",SIZE(tbf_1)
+    ENDIF
+    ALLOCATE(tbf_2(W_F(idf)%W_V(iv)%datasz_max))
+  ELSE IF (W_F(idf)%W_V(iv)%datasz_max > SIZE(tbf_2)) THEN
+    IF (l_dbg) THEN
+      WRITE(ipslout,*) "histwrite_real 1.2 re-allocate tbf_2 : ", &
+     & SIZE(tbf_1)," instead of ",SIZE(tbf_2)
+    ENDIF
+    DEALLOCATE(tbf_2)
+    ALLOCATE(tbf_2(W_F(idf)%W_V(iv)%datasz_max))
+  ENDIF
+!-
+  rtime = pitau*W_F(idf)%deltat
+  tmp_opp = W_F(idf)%W_V(iv)%topp
+!-
+! 3.0 Do the operations or transfer the slab of data into tbf_1
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "histwrite: 3.0",idf
+  ENDIF
+!-
+! 3.1 DO the Operations only if needed
+!-
+  IF (do_oper) THEN
+    nbout = nbdpt
+!-
+!-- 3.4 We continue the sequence of operations
+!--     we started in the interface routine
+!-
+    DO io=2,W_F(idf)%W_V(iv)%nbopp,2
+      nbin = nbout
+      nbout = W_F(idf)%W_V(iv)%datasz_max
+      CALL mathop(W_F(idf)%W_V(iv)%sopp(io),nbin,tbf_1, &
+ &      missing_val,nbindex,nindex,W_F(idf)%W_V(iv)%scal(io), &
+ &      nbout,tbf_2)
+      IF (l_dbg) THEN
+        WRITE(ipslout,*) &
+ &       "histwrite: 3.4a nbout : ",nbin,nbout,W_F(idf)%W_V(iv)%sopp(io)
+      ENDIF
+!-
+      nbin = nbout
+      nbout = W_F(idf)%W_V(iv)%datasz_max
+      CALL mathop(W_F(idf)%W_V(iv)%sopp(io+1),nbin,tbf_2, &
+ &      missing_val,nbindex,nindex,W_F(idf)%W_V(iv)%scal(io+1), &
+ &      nbout,tbf_1)
+      IF (l_dbg) THEN
+        WRITE(ipslout,*) &
+ &     "histwrite: 3.4b nbout : ",nbin,nbout,W_F(idf)%W_V(iv)%sopp(io+1)
+      ENDIF
+    ENDDO
+!-
+!   3.5 Zoom into the data
+!-
+    IF (l_dbg) THEN
+      WRITE(ipslout,*) &
+ &     "histwrite: 3.5 size(tbf_1) : ",SIZE(tbf_1)
+      WRITE(ipslout,*) &
+ &     "histwrite: 3.5 slab in X :", &
+ &     W_F(idf)%W_V(iv)%zorig(1),W_F(idf)%W_V(iv)%zsize(1)
+      WRITE(ipslout,*) &
+ &     "histwrite: 3.5 slab in Y :", &
+ &     W_F(idf)%W_V(iv)%zorig(2),W_F(idf)%W_V(iv)%zsize(2)
+      WRITE(ipslout,*) &
+ &     "histwrite: 3.5 slab in Z :", &
+ &     W_F(idf)%W_V(iv)%zorig(3),W_F(idf)%W_V(iv)%zsize(3)
+      WRITE(ipslout,*) &
+ &     "histwrite: 3.5 slab of input:", &
+ &     W_F(idf)%W_V(iv)%scsize(1), &
+ &     W_F(idf)%W_V(iv)%scsize(2), &
+ &     W_F(idf)%W_V(iv)%scsize(3)
+    ENDIF
+!---
+!-- We have to consider blocks of contiguous data
+!---
+    nx=MAX(W_F(idf)%W_V(iv)%zsize(1),1)
+    ny=MAX(W_F(idf)%W_V(iv)%zsize(2),1)
+    nz=MAX(W_F(idf)%W_V(iv)%zsize(3),1)
+    IF     (     (W_F(idf)%W_V(iv)%zorig(1) == 1) &
+ &          .AND.(   W_F(idf)%W_V(iv)%zsize(1) &
+ &                == W_F(idf)%W_V(iv)%scsize(1)) &
+ &          .AND.(W_F(idf)%W_V(iv)%zorig(2) == 1) &
+ &          .AND.(   W_F(idf)%W_V(iv)%zsize(2) &
+ &                == W_F(idf)%W_V(iv)%scsize(2))) THEN
+      kt = (W_F(idf)%W_V(iv)%zorig(3)-1)*nx*ny
+      tbf_2(1:nx*ny*nz) = tbf_1(kt+1:kt+nx*ny*nz)
+    ELSEIF (     (W_F(idf)%W_V(iv)%zorig(1) == 1) &
+ &          .AND.(   W_F(idf)%W_V(iv)%zsize(1) &
+ &                == W_F(idf)%W_V(iv)%scsize(1))) THEN
+      kc = -nx*ny
+      DO kz=W_F(idf)%W_V(iv)%zorig(3),W_F(idf)%W_V(iv)%zorig(3)+nz-1
+        kc = kc+nx*ny
+        kt = ( (kz-1)*W_F(idf)%W_V(iv)%scsize(2) &
+ &            +W_F(idf)%W_V(iv)%zorig(2)-1)*nx
+        tbf_2(kc+1:kc+nx*ny) = tbf_1(kt+1:kt+nx*ny)
+      ENDDO
+    ELSE
+      kc = -nx
+      DO kz=W_F(idf)%W_V(iv)%zorig(3),W_F(idf)%W_V(iv)%zorig(3)+nz-1
+        DO ky=W_F(idf)%W_V(iv)%zorig(2),W_F(idf)%W_V(iv)%zorig(2)+ny-1
+          kc = kc+nx
+          kt = ((kz-1)*W_F(idf)%W_V(iv)%scsize(2)+ky-1) &
+ &            *W_F(idf)%W_V(iv)%scsize(1) &
+ &            +W_F(idf)%W_V(iv)%zorig(1)-1
+          tbf_2(kc+1:kc+nx) = tbf_1(kt+1:kt+nx)
+        ENDDO
+      ENDDO
+    ENDIF
+!-
+!-- 4.0 Get the min and max of the field
+!-
+    IF (l_dbg) THEN
+      WRITE(ipslout,*) "histwrite: 4.0 tbf_1",idf,iv, &
+ &      TRIM(tmp_opp),' ---- ',LEN_TRIM(tmp_opp),nbindex
+    ENDIF
+!-
+    IF (W_F(idf)%W_V(iv)%hist_calc_rng) THEN
+      W_F(idf)%W_V(iv)%hist_minmax(1) = &
+ &      MIN(W_F(idf)%W_V(iv)%hist_minmax(1), &
+ &      MINVAL(tbf_2(1:tsz),MASK=tbf_2(1:tsz) /= missing_val))
+      W_F(idf)%W_V(iv)%hist_minmax(2) = &
+ &      MAX(W_F(idf)%W_V(iv)%hist_minmax(2), &
+ &      MAXVAL(tbf_2(1:tsz),MASK=tbf_2(1:tsz) /= missing_val))
+    ENDIF
+!-
+!-- 5.0 Do the operations if needed. In the case of instantaneous
+!--     output we do not transfer to the time_buffer.
+!-
+    IF (l_dbg) THEN
+      WRITE(ipslout,*) "histwrite: 5.0 idf : ",idf," iv : ",iv," tsz : ",tsz
+    ENDIF
+!-
+    IF (     (TRIM(tmp_opp) /= "inst") &
+ &      .AND.(TRIM(tmp_opp) /= "once") ) THEN
+
+!-
+!------ 5.1 Check if scatter operation is performed
+!-      
+       flag = .FALSE. 
+       DO io = 1, nbopp_max
+          IF ( INDEX(TRIM(W_F(idf)%W_V(iv)%sopp(io)),'scatter') > 0 ) THEN 
+             flag = .TRUE. 
+          END IF
+       END DO
+
+       IF ( flag ) THEN
+!-
+!------ 5.2  Enter moycum_index only if a scatter operation is performed
+!-         
+          IF (l_dbg) &
+               & WRITE(ipslout,*) "histwrite: 5.2 moycum_index",nbindex,nx,ny,nz
+          CALL moycum_index(tmp_opp, W_F(idf)%W_V(iv)%t_bf, &
+ &             tbf_2, W_F(idf)%W_V(iv)%nb_opp, nbindex, nindex)
+       ELSE
+!-
+!------ 5.3  Enter moycum otherwise
+!-
+          IF (l_dbg) &
+               & WRITE(ipslout,*) "histwrite: 5.3 moycum",nbindex,nx,ny
+          CALL moycum(tmp_opp,tsz,W_F(idf)%W_V(iv)%t_bf, &
+               &           tbf_2,W_F(idf)%W_V(iv)%nb_opp)
+       END IF
+
+    ENDIF
+!-
+    W_F(idf)%W_V(iv)%last_opp = pitau
+    W_F(idf)%W_V(iv)%nb_opp = W_F(idf)%W_V(iv)%nb_opp+1
+!-
+  ENDIF
+!-
+! 6.0 Write to file if needed
+!-
+  IF (l_dbg) WRITE(ipslout,*) "histwrite: 6.0",idf
+!-
+  IF (do_write) THEN
+!-
+    nfid = W_F(idf)%ncfid
+    nvid = W_F(idf)%W_V(iv)%ncvid
+!-
+!-- 6.1 Do the operations that are needed before writting
+!-
+    IF (l_dbg) WRITE(ipslout,*) "histwrite: 6.1",idf
+!-
+    IF (     (TRIM(tmp_opp) /= "inst") &
+ &      .AND.(TRIM(tmp_opp) /= "once") ) THEN
+      t_bnd(1:2) = (/ W_F(idf)%W_V(iv)%last_wrt*W_F(idf)%deltat,rtime /)
+      rtime = (t_bnd(1)+t_bnd(2))/2.0
+    ENDIF
+!-
+!-- 6.2 Add a value to the time axis of this variable if needed
+!-
+    IF (     (TRIM(tmp_opp) /= "l_max") &
+ &      .AND.(TRIM(tmp_opp) /= "l_min") &
+ &      .AND.(TRIM(tmp_opp) /= "once") ) THEN
+!-
+      IF (l_dbg) WRITE(ipslout,*) "histwrite: 6.2",idf
+!-
+      itax  = W_F(idf)%W_V(iv)%t_axid
+      itime = W_F(idf)%W_V(iv)%nb_wrt+1
+!-
+      IF (W_F(idf)%W_V(itax)%tax_last < itime) THEN
+        iret = NF90_PUT_VAR (nfid,W_F(idf)%W_V(itax)%tdimid, &
+ &               (/ rtime /),start=(/ itime /),count=(/ 1 /))
+        IF (W_F(idf)%W_V(itax)%tbndid > 0) THEN
+          iret = NF90_PUT_VAR (nfid,W_F(idf)%W_V(itax)%tbndid, &
+ &                 t_bnd,start=(/ 1,itime /),count=(/ 2,1 /))
+        ENDIF
+        W_F(idf)%W_V(itax)%tax_last = itime
+      ENDIF
+    ELSE
+      itime=1
+    ENDIF
+!-
+!-- 6.3 Write the data. Only in the case of instantaneous output
+!       we do not write the buffer.
+!-
+    IF (l_dbg) THEN
+      WRITE(ipslout,*) "histwrite: 6.3",idf,nfid,nvid,iv,itime
+    ENDIF
+!-
+    IF (W_F(idf)%W_V(iv)%scsize(3) == 1) THEN
+      IF (W_F(idf)%regular) THEN
+        corner(1:4) = (/ 1,1,itime,0 /)
+        edges(1:4) = (/ W_F(idf)%W_V(iv)%zsize(1), &
+ &                      W_F(idf)%W_V(iv)%zsize(2),1,0 /)
+      ELSE
+        corner(1:4) = (/ 1,itime,0,0 /)
+        edges(1:4) = (/ W_F(idf)%W_V(iv)%zsize(1),1,0,0 /)
+      ENDIF
+    ELSE
+      IF (W_F(idf)%regular) THEN
+        corner(1:4) = (/ 1,1,1,itime /)
+        edges(1:4) = (/ W_F(idf)%W_V(iv)%zsize(1), &
+ &                      W_F(idf)%W_V(iv)%zsize(2), &
+ &                      W_F(idf)%W_V(iv)%zsize(3),1 /)
+      ELSE
+        corner(1:4) = (/ 1,1,itime,0 /)
+        edges(1:4) = (/ W_F(idf)%W_V(iv)%zsize(1), &
+ &                      W_F(idf)%W_V(iv)%zsize(3),1,0 /)
+      ENDIF
+    ENDIF
+!-
+    IF (     (TRIM(tmp_opp) /= "inst") &
+ &      .AND.(TRIM(tmp_opp) /= "once") ) THEN
+      iret = NF90_PUT_VAR (nfid,nvid,W_F(idf)%W_V(iv)%t_bf, &
+ &                         start=corner(1:4),count=edges(1:4))
+    ELSE
+      iret = NF90_PUT_VAR (nfid,nvid,tbf_2, &
+ &                         start=corner(1:4),count=edges(1:4))
+    ENDIF
+!-
+    W_F(idf)%W_V(iv)%last_wrt = pitau
+    W_F(idf)%W_V(iv)%nb_wrt = W_F(idf)%W_V(iv)%nb_wrt+1
+    W_F(idf)%W_V(iv)%nb_opp = 0
+!---
+!   After the write the file can be synchronized so that no data is
+!   lost in case of a crash. This feature gives up on the benefits of
+!   buffering and should only be used in debuging mode. A flag is
+!   needed here to switch to this mode.
+!---
+!   iret = NF90_SYNC (nfid)
+!-
+  ENDIF
+!----------------------------
+END SUBROUTINE histwrite_real
+!===
+SUBROUTINE histvar_seq (idf,pvarname,idv)
+!---------------------------------------------------------------------
+!- This subroutine optimize the search for the variable in the table.
+!- In a first phase it will learn the succession of the variables
+!- called and then it will use the table to guess what comes next.
+!- It is the best solution to avoid lengthy searches through array
+!- vectors.
+!-
+!- ARGUMENTS :
+!-
+!- idf      : id of the file on which we work
+!- pvarname : The name of the variable we are looking for
+!- idv      : The var id we found
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(in)  :: idf
+  CHARACTER(LEN=*),INTENT(IN) :: pvarname
+  INTEGER,INTENT(out) :: idv
+!-
+  LOGICAL,SAVE :: learning(nb_files_max)=.TRUE.
+  INTEGER,SAVE :: overlap(nb_files_max) = -1
+  INTEGER,SAVE :: varseq(nb_files_max,nb_var_max*3)
+  INTEGER,SAVE :: varseq_len(nb_files_max) = 0
+  INTEGER,SAVE :: varseq_pos(nb_files_max)
+  INTEGER,SAVE :: varseq_err(nb_files_max) = 0
+  INTEGER      :: ib,sp,nn,pos
+  CHARACTER(LEN=70) :: str70
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) 'histvar_seq, start of the subroutine :',learning(idf)
+  ENDIF
+!-
+  IF (learning(idf)) THEN
+!-
+!-- 1.0 We compute the length over which we are going
+!--     to check the overlap
+!-
+    IF (overlap(idf) <= 0) THEN
+      IF (W_F(idf)%n_var > 6) THEN
+        overlap(idf) = W_F(idf)%n_var/3*2
+      ELSE
+        overlap(idf) = W_F(idf)%n_var
+      ENDIF
+    ENDIF
+!-
+!-- 1.1 Find the position of this string
+!-
+    CALL find_str (W_F(idf)%W_V(1:W_F(idf)%n_var)%v_name,pvarname,pos)
+    IF (pos > 0) THEN
+      idv = pos
+    ELSE
+      CALL ipslerr (3,"histvar_seq", &
+ &      'The name of the variable you gave has not been declared', &
+ &      'You should use subroutine histdef for declaring variable', &
+ &      TRIM(pvarname))
+    ENDIF
+!-
+!-- 1.2 If we have not given up we store the position
+!--     in the sequence of calls
+!-
+    IF (varseq_err(idf) >= 0) THEN
+      sp = varseq_len(idf)+1
+      IF (sp <= nb_var_max*3) THEN
+        varseq(idf,sp) = idv
+        varseq_len(idf) = sp
+      ELSE
+        CALL ipslerr (2,"histvar_seq",&
+ &       'The learning process has failed and we give up. '// &
+ &       'Either you sequence is',&
+ &       'too complex or I am too dumb. '// &
+ &       'This will only affect the efficiency',&
+ &       'of your code. Thus if you wish to save time'// &
+ &       ' contact the IOIPSL team. ')
+        WRITE(ipslout,*) 'The sequence we have found up to now :'
+        WRITE(ipslout,*) varseq(idf,1:sp-1)
+        varseq_err(idf) = -1
+      ENDIF
+!-
+!---- 1.3 Check if we have found the right overlap
+!-
+      IF (varseq_len(idf) >= overlap(idf)*2) THEN
+!-
+!------ We skip a few variables if needed as they could come
+!------ from the initialisation of the model.
+!-
+        DO ib = 0,sp-overlap(idf)*2
+          IF ( learning(idf) .AND.&
+            & SUM(ABS(varseq(idf,ib+1:ib+overlap(idf)) -&
+            & varseq(idf,sp-overlap(idf)+1:sp))) == 0 ) THEN
+            learning(idf) = .FALSE.
+            varseq_len(idf) = sp-overlap(idf)-ib
+            varseq_pos(idf) = overlap(idf)+ib
+            varseq(idf,1:varseq_len(idf)) = &
+ &            varseq(idf,ib+1:ib+varseq_len(idf))
+          ENDIF
+        ENDDO
+      ENDIF
+    ENDIF
+  ELSE
+!-
+!-- 2.0 Now we know how the calls to histwrite are sequenced
+!--     and we can get a guess at the var ID
+!-
+    nn = varseq_pos(idf)+1
+    IF (nn > varseq_len(idf)) nn = 1
+!-
+    idv = varseq(idf,nn)
+!-
+    IF (TRIM(W_F(idf)%W_V(idv)%v_name) /= TRIM(pvarname)) THEN
+      CALL find_str (W_F(idf)%W_V(1:W_F(idf)%n_var)%v_name,pvarname,pos)
+      IF (pos > 0) THEN
+        idv = pos
+      ELSE
+        CALL ipslerr (3,"histvar_seq", &
+ &  'The name of the variable you gave has not been declared',&
+ &  'You should use subroutine histdef for declaring variable', &
+ &  TRIM(pvarname))
+      ENDIF
+      varseq_err(idf) = varseq_err(idf)+1
+      IF (l_dbg) &
+           WRITE(ipslout,*) "Error history file ",W_F(idf)%name," names : ", &
+ &           TRIM(W_F(idf)%W_V(idv)%v_name),TRIM(pvarname)," id : ",idv
+    ELSE
+!-
+!---- We only keep the new position if we have found the variable
+!---- this way. This way an out of sequence call to histwrite does
+!---- not defeat the process.
+!-
+      varseq_pos(idf) = nn
+    ENDIF
+!-
+    IF (varseq_err(idf) >= 10) THEN
+      WRITE(str70,'("for file ",I3)') idf
+      CALL ipslerr (2,"histvar_seq", &
+ &  'There were 10 errors in the learned sequence of variables',&
+ &  str70,'This looks like a bug, please report it.')
+         varseq_err(idf) = 0
+    ENDIF
+  ENDIF
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) &
+ &   'histvar_seq, end of the subroutine :',TRIM(pvarname),idv
+  ENDIF
+!-------------------------
+END SUBROUTINE histvar_seq
+!===
+SUBROUTINE histsync (idf)
+!---------------------------------------------------------------------
+!- This subroutine will synchronise all
+!- (or one if defined) opened files.
+!-
+!- VERSION
+!-
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+! idf  : optional argument for fileid
+  INTEGER,INTENT(in),OPTIONAL :: idf
+!-
+  INTEGER :: ifile,iret,i_s,i_e
+!-
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "->histsync"
+  ENDIF
+!-
+  IF (PRESENT(idf)) THEN
+    IF ( (idf >= 1).AND.(idf <= nb_files_max) ) THEN
+      IF (W_F(idf)%ncfid > 0) THEN
+        i_s = idf
+        i_e = idf
+      ELSE
+        i_s = 1
+        i_e = 0
+        CALL ipslerr (2,'histsync', &
+ &       'Unable to synchronise the file :','probably','not opened')
+      ENDIF
+    ELSE
+      CALL ipslerr (3,'histsync','Invalid file identifier',' ',' ')
+    ENDIF
+  ELSE
+    i_s = 1
+    i_e = nb_files_max
+  ENDIF
+!-
+  DO ifile=i_s,i_e
+    IF (W_F(ifile)%ncfid > 0) THEN
+      IF (l_dbg) THEN
+        WRITE(ipslout,*) '  histsync - synchronising file number ',ifile
+      ENDIF
+      iret = NF90_SYNC(W_F(ifile)%ncfid)
+    ENDIF
+  ENDDO
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "<-histsync"
+  ENDIF
+!----------------------
+END SUBROUTINE histsync
+!===
+SUBROUTINE histclo (idf)
+!---------------------------------------------------------------------
+!- This subroutine will close all (or one if defined) opened files
+!-
+!- VERSION
+!-
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+! idf  : optional argument for fileid
+  INTEGER,INTENT(in),OPTIONAL :: idf
+!-
+  INTEGER :: ifile,nfid,nvid,iret,iv,i_s,i_e
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "->histclo"
+  ENDIF
+!-
+  IF (PRESENT(idf)) THEN
+    IF ( (idf >= 1).AND.(idf <= nb_files_max) ) THEN
+      IF (W_F(idf)%ncfid > 0) THEN
+        i_s = idf
+        i_e = idf
+      ELSE
+        i_s = 1
+        i_e = 0
+        CALL ipslerr (2,'histclo', &
+ &       'Unable to close the file :','probably','not opened')
+      ENDIF
+    ELSE
+      CALL ipslerr (3,'histclo','Invalid file identifier',' ',' ')
+    ENDIF
+  ELSE
+    i_s = 1
+    i_e = nb_files_max
+  ENDIF
+!-
+  DO ifile=i_s,i_e
+    IF (W_F(ifile)%ncfid > 0) THEN
+      IF (l_dbg) THEN
+        WRITE(ipslout,*) '  histclo - closing specified file number :',ifile
+      ENDIF
+      nfid = W_F(ifile)%ncfid
+      iret = NF90_REDEF(nfid)
+!-----
+!---- 1. Loop on the number of variables to add some final information
+!-----
+      IF (l_dbg) THEN
+        WRITE(ipslout,*) '  Entering loop on vars : ',W_F(ifile)%n_var
+      ENDIF
+      DO iv=1,W_F(ifile)%n_var
+!------ Extrema
+        IF (W_F(ifile)%W_V(iv)%hist_wrt_rng) THEN
+          IF (l_dbg) THEN
+            WRITE(ipslout,*) 'min value for file :',ifile,' var n. :',iv, &
+ &                     ' is : ',W_F(ifile)%W_V(iv)%hist_minmax(1)
+            WRITE(ipslout,*) 'max value for file :',ifile,' var n. :',iv, &
+ &                     ' is : ',W_F(ifile)%W_V(iv)%hist_minmax(2)
+          ENDIF
+          IF (W_F(ifile)%W_V(iv)%hist_calc_rng) THEN
+!---------- Put the min and max values on the file
+            nvid = W_F(ifile)%W_V(iv)%ncvid
+            IF (W_F(ifile)%W_V(iv)%v_typ == hist_r8) THEN
+              iret = NF90_PUT_ATT(nfid,nvid,'valid_min', &
+ &                     REAL(W_F(ifile)%W_V(iv)%hist_minmax(1),KIND=8))
+              iret = NF90_PUT_ATT(nfid,nvid,'valid_max', &
+ &                     REAL(W_F(ifile)%W_V(iv)%hist_minmax(2),KIND=8))
+            ELSE
+              iret = NF90_PUT_ATT(nfid,nvid,'valid_min', &
+ &                     REAL(W_F(ifile)%W_V(iv)%hist_minmax(1),KIND=4))
+              iret = NF90_PUT_ATT(nfid,nvid,'valid_max', &
+ &                     REAL(W_F(ifile)%W_V(iv)%hist_minmax(2),KIND=4))
+            ENDIF
+          ENDIF
+        ENDIF
+!------ Time-Buffers
+        IF (ASSOCIATED(W_F(ifile)%W_V(iv)%t_bf)) THEN
+          DEALLOCATE(W_F(ifile)%W_V(iv)%t_bf)
+        ENDIF
+!------ Reinitialize the sizes
+        W_F(ifile)%W_V(iv)%datasz_in(:) = -1
+        W_F(ifile)%W_V(iv)%datasz_max = -1
+      ENDDO
+!-----
+!---- 2. Close the file
+!-----
+      IF (l_dbg) WRITE(ipslout,*) '  close file :',nfid
+      iret = NF90_CLOSE(nfid)
+      W_F(ifile)%ncfid = -1
+      W_F(ifile)%dom_id_svg = -1
+    ENDIF
+  ENDDO
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) "<-histclo"
+  ENDIF
+!---------------------
+END SUBROUTINE histclo
+!===
+SUBROUTINE ioconf_modname (str)
+!---------------------------------------------------------------------
+!- This subroutine allows to configure the name
+!- of the model written into the file
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*),INTENT(IN) :: str
+!---------------------------------------------------------------------
+  IF (.NOT.lock_modname) THEN
+    model_name = str(1:MIN(LEN_TRIM(str),80))
+    lock_modname = .TRUE.
+  ELSE
+    CALL ipslerr (2,"ioconf_modname", &
+   &  'The model name can only be changed once and only', &
+   &  'before it is used. It is now set to :',model_name)
+  ENDIF
+!----------------------------
+END SUBROUTINE ioconf_modname
+!-
+!===
+!-
+!-----------------
+END MODULE histcom
Index: /IOIPSL/tags/v2_2_5/src/ioipsl.f90
===================================================================
--- /IOIPSL/tags/v2_2_5/src/ioipsl.f90	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/src/ioipsl.f90	(revision 5058)
@@ -0,0 +1,17 @@
+MODULE ioipsl
+!
+!$Id$
+!-
+! This software is governed by the CeCILL license
+! See IOIPSL/IOIPSL_License_CeCILL.txt
+!
+  USE errioipsl 
+  USE stringop
+  USE mathelp    
+  USE getincom
+  USE calendar   
+  USE fliocom    
+  USE flincom    
+  USE histcom    
+  USE restcom
+END MODULE ioipsl
Index: /IOIPSL/tags/v2_2_5/src/mathelp.f90
===================================================================
--- /IOIPSL/tags/v2_2_5/src/mathelp.f90	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/src/mathelp.f90	(revision 5058)
@@ -0,0 +1,3200 @@
+MODULE mathelp
+!-
+!$Id$
+!-
+! This software is governed by the CeCILL license
+! See IOIPSL/IOIPSL_License_CeCILL.txt
+!---------------------------------------------------------------------
+  USE errioipsl,ONLY : ipslerr
+  USE stringop
+!-
+  PRIVATE
+  PUBLIC :: mathop,moycum,moycum_index,buildop
+!-
+  INTERFACE mathop
+    MODULE PROCEDURE mathop_r11,mathop_r21,mathop_r31
+  END INTERFACE
+!-
+!- Variables used to detect and identify the operations
+!-
+  CHARACTER(LEN=80),SAVE :: &
+ &  seps='( ) , + - / * ^', ops = '+ - * / ^', mima = 'min max'
+  CHARACTER(LEN=250),SAVE :: &
+ &  funcs = 'sin cos tan asin acos atan exp log sqrt chs abs '&
+ & //'cels kelv deg rad gather scatter fill coll undef only ident'
+  CHARACTER(LEN=120),SAVE :: &
+ &  indexfu = 'gather, scatter, fill, coll, undef, only'
+!---------------------------------------------------------------------
+CONTAINS
+!===
+SUBROUTINE buildop (c_str,ex_topps,topp,fill_val,opps,scal,nbops)
+!---------------------------------------------------------------------
+!- This subroutine decomposes the input string in the elementary
+!- functions which need to be applied to the vector of data.
+!- This vector is represented by X in the string.
+!- This subroutine is the driver of the decomposition and gets
+!- the time operation but then call decoop for the other operations
+!-
+!- INPUT
+!-
+!- c_str    : String containing the operations
+!- ex_toops : Time operations that can be expected within the string
+!- fill_val :
+!-
+!- OUTPUT
+!-
+!- topp   : Time operation
+!- opps   :
+!- scal   :
+!- nbops  :
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*),INTENT(IN) :: c_str,ex_topps
+  CHARACTER(LEN=*),INTENT(OUT) :: topp
+  CHARACTER(LEN=*),DIMENSION(:),INTENT(OUT) :: opps
+  REAL,INTENT(IN) :: fill_val
+  REAL,DIMENSION(:),INTENT(OUT) :: scal
+  INTEGER,INTENT(OUT) :: nbops
+!-
+  CHARACTER(LEN=LEN(c_str)) :: str,new_str
+  INTEGER :: leng,ind_opb,ind_clb
+!-
+  LOGICAL :: check = .FALSE.
+!---------------------------------------------------------------------
+  IF (check) WRITE(*,*) 'buildop : Some preliminary cleaning'
+!-
+  str = c_str
+  leng = LEN_TRIM(str)
+  IF ( str(1:1) == '(' .AND. str(leng:leng) == ')' ) THEN
+    str = str(2:leng-1)
+    leng = leng-2
+  ENDIF
+!-
+  IF (check) &
+ &  WRITE(*,*) 'buildop : Starting to test the various options'
+!-
+  IF (leng <= 5 .AND. INDEX(ex_topps,str(1:leng)) > 0) THEN
+    IF (check) WRITE(*,*) 'buildop : Time operation only'
+    nbops = 0
+    topp = str(1:leng)
+  ELSE
+    IF (check) THEN
+      WRITE(*,*) 'buildop : Time operation and something else'
+    ENDIF
+!--
+    ind_opb = INDEX(str(1:leng),'(')
+    IF (ind_opb > 0) THEN
+      IF (INDEX(ex_topps,str(1:ind_opb-1)) > 0) THEN
+        IF (check) THEN
+          WRITE(*,'(2a)') &
+ &          ' buildop : Extract time operation from : ',str
+        ENDIF
+        topp = str(1:ind_opb-1)
+        ind_clb = INDEX(str(1:leng),')',BACK=.TRUE.)
+        new_str = str(ind_opb+1:ind_clb-1)
+        IF (check) THEN
+          WRITE(*,'(2a,2I3)') &
+ &          ' buildop : Call decoop ',new_str,ind_opb,ind_clb
+        ENDIF
+        CALL decoop (new_str,fill_val,opps,scal,nbops)
+      ELSE
+        CALL ipslerr(3,'buildop', &
+ &        'time operation does not exist',str(1:ind_opb-1),' ')
+      ENDIF
+    ELSE
+      CALL ipslerr(3,'buildop', &
+ &      'some long operation exists but wihout parenthesis', &
+ &      str(1:leng),' ')
+    ENDIF
+  ENDIF
+!-
+  IF (check) THEN
+    DO leng=1,nbops
+      WRITE(*,*) &
+ &      'buildop : i -- opps, scal : ',leng,opps(leng),scal(leng)
+    ENDDO
+  ENDIF
+!---------------------
+END SUBROUTINE buildop
+!===
+SUBROUTINE decoop (pstr,fill_val,opps,scal,nbops)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*),INTENT(IN) :: pstr
+  REAL,INTENT(IN) :: fill_val
+  CHARACTER(LEN=*),DIMENSION(:),INTENT(OUT) :: opps
+  REAL,DIMENSION(:),INTENT(OUT) :: scal
+  INTEGER,INTENT(OUT) :: nbops
+!-
+  CHARACTER(LEN=1),DIMENSION(2) :: f_char,s_char
+  INTEGER,DIMENSION(2) :: f_pos,s_pos
+  CHARACTER(LEN=20) :: opp_str,scal_str
+  CHARACTER(LEN=LEN(pstr)) :: str
+  INTEGER :: nbsep,nbops_max,xpos,leng,ppos,epos,int_tmp
+  CHARACTER(LEN=3) :: tl,dl
+  CHARACTER(LEN=10) :: fmt
+!-
+  LOGICAL :: check = .FALSE.,prio
+!---------------------------------------------------------------------
+  IF (check) WRITE(*,'(2A)') ' decoop : Incoming string : ',pstr
+!-
+  str = pstr; nbops = 0;
+!-
+  CALL findsep (str,nbsep,f_char,f_pos,s_char,s_pos)
+  IF (check) WRITE(*,*) 'decoop : Out of findsep',nbsep
+!-
+  nbops_max = min(SIZE(opps),SIZE(scal))
+!-
+  DO WHILE (nbsep > 0)
+    IF (nbops >= nbops_max) THEN
+      CALL ipslerr(3,'decoop','Expression too complex',TRIM(str),' ')
+    ENDIF
+!--
+    xpos = INDEX(str,'X')
+    leng = LEN_TRIM(str)
+    nbops = nbops+1
+!--
+    IF (check) THEN
+      WRITE(*,*) 'decoop : str   -> ',TRIM(str)
+      WRITE(*,*) 'decoop : nbops -> ',nbops
+      WRITE(*,*) s_char(1),'-',f_char(1),'|',f_char(2),'-',s_char(2)
+      WRITE(*,*) s_pos(1),'-',f_pos(1),'|',f_pos(2),'-',s_pos(2)
+    ENDIF
+!---
+!-- Start the analysis of the syntax. 3 types of constructs
+!-- are recognized.  They are scanned sequentialy
+!---
+    IF (nbsep == 1) THEN
+      IF (check) WRITE(*,*) 'decoop : Only one operation'
+      IF (INDEX(ops,f_char(1)) > 0) THEN
+!------ Type : scal+X
+        IF (f_char(1) == '-' .OR. f_char(1) == '/') THEN
+          opp_str = f_char(1)//'I'
+        ELSE
+          opp_str = f_char(1)
+        ENDIF
+        scal_str = str(s_pos(1)+1:f_pos(1)-1)
+        str = 'X'
+      ELSE IF (INDEX(ops,f_char(2)) > 0) THEN
+!------ Type : X+scal
+        opp_str = f_char(2)
+        scal_str = str(f_pos(2)+1:s_pos(2)-1)
+        str = 'X'
+      ELSE
+        CALL ipslerr(3,'decoop', &
+ &        'Unknown operations of type X+scal',f_char(1),pstr)
+      ENDIF
+    ELSE
+      IF (check) WRITE(*,*) 'decoop : More complex operation'
+      IF ( f_char(1) == '(' .AND. f_char(2) == ')' ) THEN
+!------ Type : sin(X)
+        opp_str = str(s_pos(1)+1:f_pos(1)-1)
+        scal_str = '?'
+        str = str(1:s_pos(1))//'X'//str(f_pos(2)+1:leng)
+      ELSE IF (    (f_char(1) == '(' .AND. f_char(2) == ',')&
+ &             .OR.(f_char(1) == ',' .AND. f_char(2) == ')')) THEN
+!------ Type : max(X,scal) or max(scal,X)
+        IF (f_char(1) == '(' .AND. s_char(2) == ')') THEN
+!-------- Type : max(X,scal)
+          opp_str = str(f_pos(1)-3:f_pos(1)-1)
+          scal_str = str(f_pos(2)+1:s_pos(2)-1)
+          str = str(1:f_pos(1)-4)//'X'//str(s_pos(2)+1:leng)
+        ELSE IF (f_char(1) == ',' .AND. s_char(1) == '(') THEN
+!-------- Type : max(scal,X)
+          opp_str = str(s_pos(1)-3:s_pos(1)-1)
+          scal_str = str(s_pos(1)+1:f_pos(1)-1)
+          str = str(1:s_pos(1)-4)//'X'//str(f_pos(2)+1:leng)
+        ELSE
+          CALL ipslerr(3,'decoop','Syntax error 1',str,' ')
+        ENDIF
+      ELSE
+        prio = (f_char(2) == '*').OR.(f_char(2) == '^')
+        IF (     (INDEX(ops,f_char(1)) > 0) &
+ &          .AND.(xpos-f_pos(1) == 1).AND.(.NOT.prio) ) THEN
+!-------- Type : ... scal+X ...
+          IF (f_char(1) == '-' .OR. f_char(1) == '/') THEN
+            opp_str = f_char(1)//'I'
+          ELSE
+            opp_str = f_char(1)
+          ENDIF
+          scal_str = str(s_pos(1)+1:f_pos(1)-1)
+          str = str(1:s_pos(1))//'X'//str(f_pos(1)+2:leng)
+        ELSE IF (     (INDEX(ops,f_char(2)) > 0) &
+ &               .AND.(f_pos(2)-xpos == 1) ) THEN
+!-------- Type : ... X+scal ...
+          opp_str = f_char(2)
+          scal_str = str(f_pos(2)+1:s_pos(2)-1)
+          str = str(1:f_pos(2)-2)//'X'//str(s_pos(2):leng)
+        ELSE
+          CALL ipslerr(3,'decoop','Syntax error 2',str,' ')
+        ENDIF
+      ENDIF
+    ENDIF
+!---
+    IF (check) WRITE(*,*) 'decoop : Finished syntax,str = ',TRIM(str)
+!---
+!-- Now that the different components of the operation are identified
+!-- we transform them into what is going to be used in the program
+!---
+    IF (INDEX(scal_str,'?') > 0) THEN
+      IF (INDEX(funcs,opp_str(1:LEN_TRIM(opp_str))) > 0) THEN
+        opps(nbops) = opp_str(1:LEN_TRIM(opp_str))
+        scal(nbops) = fill_val
+      ELSE
+        CALL ipslerr(3,'decoop', &
+ &        'Unknown function',opp_str(1:LEN_TRIM(opp_str)),' ')
+      ENDIF
+    ELSE
+      leng = LEN_TRIM(opp_str)
+      IF (INDEX(mima,opp_str(1:leng)) > 0) THEN
+        opps(nbops) = 'fu'//opp_str(1:leng)
+      ELSE
+        IF (INDEX(opp_str(1:leng),'+') > 0) THEN
+          opps(nbops) = 'add'
+        ELSE IF (INDEX(opp_str(1:leng),'-I') > 0) THEN
+          opps(nbops) = 'subi'
+        ELSE IF (INDEX(opp_str(1:leng),'-') > 0) THEN
+          opps(nbops) = 'sub'
+        ELSE IF (INDEX(opp_str(1:leng),'*') > 0) THEN
+          opps(nbops) = 'mult'
+        ELSE IF (INDEX(opp_str(1:leng),'/') > 0) THEN
+          opps(nbops) = 'div'
+        ELSE IF (INDEX(opp_str(1:leng),'/I') > 0) THEN
+          opps(nbops) = 'divi'
+        ELSE IF (INDEX(opp_str(1:leng),'^') > 0) THEN
+          opps(nbops) = 'power'
+        ELSE
+          CALL ipslerr(3,'decoop', &
+ &          'Unknown operation',opp_str(1:leng),' ')
+        ENDIF
+      ENDIF
+!-----
+      leng = LEN_TRIM(scal_str)
+      ppos = INDEX(scal_str,'.')
+      epos = INDEX(scal_str,'e')
+      IF (epos == 0) epos = INDEX(scal_str,'E')
+!-----
+!---- Try to catch a few errors
+!-----
+      IF (INDEX(ops,scal_str) > 0) THEN
+        CALL ipslerr(3,'decoop', &
+ &        'Strange scalar you have here ',scal_str,pstr)
+      ENDIF
+      IF (epos > 0) THEN
+        WRITE(tl,'(I3.3)') leng
+        WRITE(dl,'(I3.3)') epos-ppos-1
+        fmt='(e'//tl//'.'//dl//')'
+        READ(scal_str,fmt) scal(nbops)
+      ELSE IF (ppos > 0) THEN
+        WRITE(tl,'(I3.3)') leng
+        WRITE(dl,'(I3.3)') leng-ppos
+        fmt='(f'//tl//'.'//dl//')'
+        READ(scal_str,fmt) scal(nbops)
+      ELSE
+        WRITE(tl,'(I3.3)') leng
+        fmt = '(I'//tl//')'
+        READ(scal_str,fmt) int_tmp
+        scal(nbops) = REAL(int_tmp)
+      ENDIF
+    ENDIF
+    IF (check) WRITE(*,*) 'decoop : Finished interpretation'
+    CALL findsep(str,nbsep,f_char,f_pos,s_char,s_pos)
+  ENDDO
+!--------------------
+END SUBROUTINE decoop
+!===
+SUBROUTINE findsep (str,nbsep,f_char,f_pos,s_char,s_pos)
+!---------------------------------------------------------------------
+!- Subroutine finds all separators in a given string
+!- It returns the following information about str :
+!-   f_char : The first separation character
+!-            (1 for before and 2 for after)
+!-   f_pos  : The position of the first separator
+!-   s_char : The second separation character
+!-            (1 for before and 2 for after)
+!-   s_pos  : The position of the second separator
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*),INTENT(INOUT) :: str
+  INTEGER :: nbsep
+  CHARACTER(LEN=1),DIMENSION(2) :: f_char,s_char
+  INTEGER,DIMENSION(2) :: f_pos,s_pos
+!-
+  CHARACTER(LEN=10) :: str_tmp
+  LOGICAL :: f_found,s_found
+  INTEGER :: ind,xpos,leng,i
+!-
+  LOGICAL :: check = .FALSE.
+!---------------------------------------------------------------------
+  IF (check) WRITE(*,*) 'findsep : call cleanstr: ',TRIM(str)
+!-
+  CALL cleanstr(str)
+!-
+  IF (check) WRITE(*,*) 'findsep : out of cleanstr: ',TRIM(str)
+!-
+  xpos = INDEX(str,'X')
+  leng = LEN_TRIM(str)
+!-
+  f_pos(1:2) = (/ 0,leng+1 /)
+  f_char(1:2) = (/ '?','?' /)
+  s_pos(1:2) = (/ 0,leng+1 /)
+  s_char(1:2) = (/ '?','?' /)
+!-
+  nbsep = 0
+!-
+  f_found = .FALSE.
+  s_found = .FALSE.
+  IF (xpos > 1) THEN
+    DO i=xpos-1,1,-1
+      ind = INDEX(seps,str(i:i))
+      IF (ind > 0) THEN
+        IF (.NOT.f_found) THEN
+          f_char(1) = str(i:i)
+          f_pos(1) = i
+          nbsep = nbsep+1
+          f_found = .TRUE.
+        ELSE IF (.NOT.s_found) THEN
+          s_char(1) = str(i:i)
+          s_pos(1) = i
+          nbsep = nbsep+1
+          s_found = .TRUE.
+        ENDIF
+      ENDIF
+    ENDDO
+  ENDIF
+!-
+  f_found = .FALSE.
+  s_found = .FALSE.
+  IF (xpos < leng) THEN
+    DO i=xpos+1,leng
+      ind = INDEX(seps,str(i:i))
+      IF (ind > 0) THEN
+        IF (.NOT.f_found) THEN
+          f_char(2) = str(i:i)
+          f_pos(2) = i
+          nbsep = nbsep+1
+          f_found = .TRUE.
+        ELSE IF (.NOT.s_found) THEN
+          s_char(2) = str(i:i)
+          s_pos(2) = i
+          nbsep = nbsep+1
+          s_found = .TRUE.
+        ENDIF
+      ENDIF
+    ENDDO
+  ENDIF
+!-
+  IF (nbsep > 4) THEN
+    WRITE(str_tmp,'("number :",I3)') nbsep
+    CALL ipslerr(3,'findsep', &
+ &    'How can I find that many separators',str_tmp,TRIM(str))
+  ENDIF
+!-
+  IF (check) WRITE(*,*) 'Finished findsep : ',nbsep,leng
+!---------------------
+END SUBROUTINE findsep
+!===
+SUBROUTINE cleanstr(str)
+!---------------------------------------------------------------------
+!- We clean up the string by taking out the extra () and puting
+!- everything in lower case except for the X describing the variable
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*),INTENT(INOUT) :: str
+!-
+  INTEGER :: ind,leng,ic,it
+  LOGICAL :: check = .FALSE.
+!---------------------------------------------------------------------
+  leng = LEN_TRIM(str)
+  CALL strlowercase(str)
+!-
+  ind = INDEX(str,'x')
+  IF (check) THEN
+    WRITE (*,*) 'cleanstr 1.0 : ind = ',ind, &
+&               ' str = ',str(1:leng),'---'
+  ENDIF
+!-
+! If the character before the x is not a letter then we can assume
+! that it is the variable and promote it to a capital letter
+!-
+  DO WHILE (ind > 0)
+    ic = 0
+    IF (ind > 1) ic = IACHAR(str(ind-1:ind-1))
+    IF (ic < 97 .OR. ic > 122) THEN
+      str(ind:ind) = 'X'
+    ENDIF
+    it = INDEX(str(ind+1:leng),'x')
+    IF (it > 0) THEN
+      ind = ind+it
+    ELSE
+      ind = it
+    ENDIF
+  ENDDO
+!-
+  IF (check) WRITE (*,*) 'cleanstr 2.0 : str = ',str(1:leng),'---'
+!-
+  IF ( str(1:1) == '(' .AND. str(leng:leng) == ')' ) THEN
+    str = str(2:leng-1)
+  ENDIF
+!-
+  IF (check) WRITE (*,*) 'cleanstr 3.0 : str = ',str(1:leng),'---'
+!-
+  leng = LEN_TRIM(str)
+  ind = INDEX(str,'((X))')
+  IF (ind > 0) THEN
+    str=str(1:ind-1)//'(X)'//str(ind+5:leng)//'  '
+  ENDIF
+!-
+  IF (check) WRITE (*,*) 'cleanstr 4.0 : str = ',str(1:leng),'---'
+!-
+  leng = LEN_TRIM(str)
+  ind = INDEX(str,'(X)')
+  IF (ind > 0 .AND. ind+3 < leng) THEN
+    IF (      (INDEX(seps,str(ind-1:ind-1)) > 0) &
+ &      .AND. (INDEX(seps,str(ind+3:ind+3)) > 0) ) THEN
+      str=str(1:ind-1)//'X'//str(ind+3:leng)//'  '
+    ENDIF
+  ENDIF
+!-
+  IF (check) WRITE (*,*) 'cleanstr 5.0 : str = ',str(1:leng),'---'
+!-
+  leng = LEN_TRIM(str)
+  ind = INDEX(str(1:leng),' ')
+  DO WHILE (ind > 0)
+    str=str(1:ind-1)//str(ind+1:leng)//' '
+    leng = LEN_TRIM(str)
+    ind = INDEX(str(1:leng),' ')
+  ENDDO
+!-
+  IF (check) WRITE (*,*) 'cleanstr 6.0 : str = ',str(1:leng),'---'
+!----------------------
+END SUBROUTINE cleanstr
+!===
+!===
+SUBROUTINE mathop_r11 &
+ &  (fun,nb,work_in,miss_val,nb_index,nindex,scal,nb_max,work_out)
+!---------------------------------------------------------------------
+!- This subroutines gives an interface to the various operation
+!- which are allowed. The interface is general enough to allow its use
+!- for other cases.
+!-
+!- INPUT
+!-
+!- fun      : function to be applied to the vector of data
+!- nb       : Length of input vector
+!- work_in  : Input vector of data (REAL)
+!- miss_val : The value of the missing data flag (it has to be a
+!-            maximum value, in f90 : huge( a real ))
+!- nb_index : Length of index vector
+!- nindex   : Vector of indices
+!- scal     : A scalar value for vector/scalar operations
+!- nb_max   : maximum length of output vector
+!-
+!- OUTPUT
+!-
+!- nb_max   : Actual length of output variable
+!- work_out : Output vector after the operation was applied
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=7) :: fun
+  INTEGER :: nb,nb_max,nb_index
+  INTEGER :: nindex(nb_index)
+  REAL :: work_in(nb),scal,miss_val
+  REAL :: work_out(nb_max)
+!-
+  INTEGER :: ierr
+!-
+  INTRINSIC SIN,COS,TAN,ASIN,ACOS,ATAN,EXP,LOG,SQRT,ABS
+!---------------------------------------------------------------------
+  ierr = 0
+!-
+  IF (scal >= miss_val-1.) THEN
+    IF (INDEX(indexfu,fun(1:LEN_TRIM(fun))) == 0) THEN
+      SELECT CASE (fun)
+      CASE('sin')
+        ierr = ma_sin_r11(nb,work_in,nb_max,work_out)
+      CASE('cos')
+        ierr = ma_cos_r11(nb,work_in,nb_max,work_out)
+      CASE('tan')
+        ierr = ma_tan_r11(nb,work_in,nb_max,work_out)
+      CASE('asin')
+        ierr = ma_asin_r11(nb,work_in,nb_max,work_out)
+      CASE('acos')
+        ierr = ma_acos_r11(nb,work_in,nb_max,work_out)
+      CASE('atan')
+        ierr = ma_atan_r11(nb,work_in,nb_max,work_out)
+      CASE('exp')
+        ierr = ma_exp_r11(nb,work_in,nb_max,work_out)
+      CASE('log')
+        ierr = ma_log_r11(nb,work_in,nb_max,work_out)
+      CASE('sqrt')
+        ierr = ma_sqrt_r11(nb,work_in,nb_max,work_out)
+      CASE('chs')
+        ierr = ma_chs_r11(nb,work_in,nb_max,work_out)
+      CASE('abs')
+        ierr = ma_abs_r11(nb,work_in,nb_max,work_out)
+      CASE('cels')
+        ierr = ma_cels_r11(nb,work_in,nb_max,work_out)
+      CASE('kelv')
+        ierr = ma_kelv_r11(nb,work_in,nb_max,work_out)
+      CASE('deg')
+        ierr = ma_deg_r11(nb,work_in,nb_max,work_out)
+      CASE('rad')
+        ierr = ma_rad_r11(nb,work_in,nb_max,work_out)
+      CASE('ident')
+        ierr = ma_ident_r11(nb,work_in,nb_max,work_out)
+      CASE DEFAULT
+        CALL ipslerr(3,"mathop", &
+ &        'scalar variable undefined and no indexing', &
+ &        'but still unknown function',fun)
+      END SELECT
+      IF (ierr > 0) THEN
+        CALL ipslerr(3,"mathop", &
+ &        'Error while executing a simple function',fun,' ')
+      ENDIF
+    ELSE
+      SELECT CASE (fun)
+      CASE('gather')
+        ierr = ma_fugath_r11(nb,work_in,nb_index,nindex, &
+&                            miss_val,nb_max,work_out)
+      CASE('scatter')
+        IF (nb_index > nb) THEN
+          work_out(1:nb_max) = miss_val
+          ierr=1
+        ELSE
+          ierr = ma_fuscat_r11(nb,work_in,nb_index,nindex, &
+&                              miss_val,nb_max,work_out)
+        ENDIF
+      CASE('coll')
+        ierr = ma_fucoll_r11(nb,work_in,nb_index,nindex, &
+&                            miss_val,nb_max,work_out)
+      CASE('fill')
+        ierr = ma_fufill_r11(nb,work_in,nb_index,nindex, &
+&                            miss_val,nb_max,work_out)
+      CASE('undef')
+        ierr = ma_fuundef_r11(nb,work_in,nb_index,nindex, &
+&                            miss_val,nb_max,work_out)
+      CASE('only')
+        ierr = ma_fuonly_r11(nb,work_in,nb_index,nindex, &
+&                            miss_val,nb_max,work_out)
+      CASE DEFAULT
+        CALL ipslerr(3,"mathop", &
+ &        'scalar variable undefined and indexing',&
+ &        'was requested but with unknown function',fun)
+      END SELECT
+      IF (ierr > 0) THEN
+        CALL ipslerr(3,"mathop_r11", &
+ &        'Error while executing an indexing function',fun,' ')
+      ENDIF
+    ENDIF
+  ELSE
+    SELECT CASE (fun)
+    CASE('fumin')
+      ierr = ma_fumin_r11(nb,work_in,scal,nb_max,work_out)
+    CASE('fumax')
+      ierr = ma_fumax_r11(nb,work_in,scal,nb_max,work_out)
+    CASE('add')
+      ierr = ma_add_r11(nb,work_in,scal,nb_max,work_out)
+    CASE('subi')
+      ierr = ma_subi_r11(nb,work_in,scal,nb_max,work_out)
+    CASE('sub')
+      ierr = ma_sub_r11(nb,work_in,scal,nb_max,work_out)
+    CASE('mult')
+      ierr = ma_mult_r11(nb,work_in,scal,nb_max,work_out)
+    CASE('div')
+      ierr = ma_div_r11(nb,work_in,scal,nb_max,work_out)
+    CASE('divi')
+      ierr = ma_divi_r11(nb,work_in,scal,nb_max,work_out)
+    CASE('power')
+      ierr = ma_power_r11(nb,work_in,scal,nb_max,work_out)
+    CASE DEFAULT
+      CALL ipslerr(3,"mathop", &
+ &      'Unknown operation with a scalar',fun,' ')
+    END SELECT
+    IF (ierr > 0) THEN
+      CALL ipslerr(3,"mathop", &
+ &      'Error while executing a scalar function',fun,' ')
+    ENDIF
+  ENDIF
+!------------------------
+END SUBROUTINE mathop_r11
+!-
+!=== FUNCTIONS (only one argument)
+!-
+INTEGER FUNCTION ma_sin_r11(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo,i
+  REAL :: x(nb),y(nbo)
+!---------------------------------------------------------------------
+  DO i=1,nb
+    y(i) = SIN(x(i))
+  ENDDO
+!-
+  nbo = nb
+  ma_sin_r11 = 0
+!----------------------
+END FUNCTION ma_sin_r11
+!===
+INTEGER FUNCTION ma_cos_r11(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo,i
+  REAL :: x(nb),y(nbo)
+!---------------------------------------------------------------------
+  DO i=1,nb
+    y(i) = COS(x(i))
+  ENDDO
+!-
+  nbo = nb
+  ma_cos_r11 = 0
+!----------------------
+END FUNCTION ma_cos_r11
+!===
+INTEGER FUNCTION ma_tan_r11(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo,i
+  REAL :: x(nb),y(nbo)
+!---------------------------------------------------------------------
+  DO i=1,nb
+    y(i) = TAN(x(i))
+  ENDDO
+!-
+  nbo = nb
+  ma_tan_r11 = 0
+!----------------------
+END FUNCTION ma_tan_r11
+!===
+INTEGER FUNCTION ma_asin_r11(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo,i
+  REAL :: x(nb),y(nbo)
+!---------------------------------------------------------------------
+  DO i=1,nb
+    y(i) = ASIN(x(i))
+  ENDDO
+!-
+  nbo = nb
+  ma_asin_r11 = 0
+!-----------------------
+END FUNCTION ma_asin_r11
+!===
+INTEGER FUNCTION ma_acos_r11(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo,i
+  REAL :: x(nb),y(nbo)
+!---------------------------------------------------------------------
+  DO i=1,nb
+    y(i) = ACOS(x(i))
+  ENDDO
+!-
+  nbo = nb
+  ma_acos_r11 = 0
+!-----------------------
+END FUNCTION ma_acos_r11
+!===
+INTEGER FUNCTION ma_atan_r11(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo,i
+  REAL :: x(nb),y(nbo)
+!---------------------------------------------------------------------
+  DO i=1,nb
+    y(i) = ATAN(x(i))
+  ENDDO
+!-
+  nbo = nb
+  ma_atan_r11 = 0
+!-----------------------
+END FUNCTION ma_atan_r11
+!===
+INTEGER FUNCTION ma_exp_r11(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo,i
+  REAL :: x(nb),y(nbo)
+!---------------------------------------------------------------------
+  DO i=1,nb
+    y(i) = EXP(x(i))
+  ENDDO
+!-
+  nbo = nb
+  ma_exp_r11 = 0
+!----------------------
+END FUNCTION ma_exp_r11
+!===
+INTEGER FUNCTION ma_log_r11(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo,i
+  REAL :: x(nb),y(nbo)
+!---------------------------------------------------------------------
+  DO i=1,nb
+    y(i) = log(x(i))
+  ENDDO
+!-
+  nbo = nb
+  ma_log_r11 = 0
+!----------------------
+END FUNCTION ma_log_r11
+!===
+INTEGER FUNCTION ma_sqrt_r11(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo,i
+  REAL :: x(nb),y(nbo)
+!---------------------------------------------------------------------
+  DO i=1,nb
+    y(i) = SQRT(x(i))
+  ENDDO
+!-
+  nbo = nb
+  ma_sqrt_r11 = 0
+!-----------------------
+END FUNCTION ma_sqrt_r11
+!===
+INTEGER FUNCTION ma_abs_r11(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo,i
+  REAL :: x(nb),y(nbo)
+!---------------------------------------------------------------------
+  DO i=1,nb
+    y(i) = ABS(x(i))
+  ENDDO
+!-
+  nbo = nb
+  ma_abs_r11 = 0
+!----------------------
+END FUNCTION ma_abs_r11
+!===
+INTEGER FUNCTION ma_chs_r11(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo,i
+  REAL :: x(nb),y(nbo)
+!---------------------------------------------------------------------
+  DO i=1,nb
+    y(i) = x(i)*(-1.)
+  ENDDO
+!-
+  nbo = nb
+  ma_chs_r11 = 0
+!----------------------
+END FUNCTION ma_chs_r11
+!===
+INTEGER FUNCTION ma_cels_r11(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo,i
+  REAL :: x(nb),y(nbo)
+!---------------------------------------------------------------------
+  DO i=1,nb
+    y(i) = x(i)-273.15
+  ENDDO
+!-
+  nbo = nb
+  ma_cels_r11 = 0
+!-----------------------
+END FUNCTION ma_cels_r11
+!===
+INTEGER FUNCTION ma_kelv_r11(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo,i
+  REAL :: x(nb),y(nbo)
+!---------------------------------------------------------------------
+  DO i=1,nb
+    y(i) = x(i)+273.15
+  ENDDO
+!-
+  nbo = nb
+  ma_kelv_r11 = 0
+!-----------------------
+END FUNCTION ma_kelv_r11
+!===
+INTEGER FUNCTION ma_deg_r11(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo,i
+  REAL :: x(nb),y(nbo)
+!---------------------------------------------------------------------
+  DO i=1,nb
+    y(i) = x(i)*57.29577951
+  ENDDO
+!-
+  nbo = nb
+  ma_deg_r11 = 0
+!-----------------------
+END FUNCTION ma_deg_r11
+!===
+INTEGER FUNCTION ma_rad_r11(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo,i
+  REAL :: x(nb),y(nbo)
+!---------------------------------------------------------------------
+  DO i=1,nb
+    y(i) = x(i)*0.01745329252
+  ENDDO
+!-
+  nbo = nb
+  ma_rad_r11 = 0
+!----------------------
+END FUNCTION ma_rad_r11
+!===
+INTEGER FUNCTION ma_ident_r11(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo,i
+  REAL :: x(nb),y(nbo)
+!---------------------------------------------------------------------
+  DO i=1,nb
+    y(i) = x(i)
+  ENDDO
+!-
+  nbo = nb
+  ma_ident_r11 = 0
+!------------------------
+END FUNCTION ma_ident_r11
+!-
+!=== OPERATIONS (two argument)
+!-
+INTEGER FUNCTION ma_add_r11(nb,x,s,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo
+  REAL :: x(nb),s,y(nbo)
+!-
+  INTEGER :: i
+!---------------------------------------------------------------------
+  DO i=1,nb
+    y(i) = x(i)+s
+  ENDDO
+!-
+  nbo = nb
+  ma_add_r11 = 0
+!-----------------------
+  END FUNCTION ma_add_r11
+!===
+INTEGER FUNCTION ma_sub_r11(nb,x,s,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo
+  REAL :: x(nb),s,y(nbo)
+!-
+  INTEGER :: i
+!---------------------------------------------------------------------
+  DO i=1,nb
+    y(i) = x(i)-s
+  ENDDO
+!-
+  nbo = nb
+  ma_sub_r11 = 0
+!----------------------
+END FUNCTION ma_sub_r11
+!===
+INTEGER FUNCTION ma_subi_r11(nb,x,s,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo
+  REAL :: x(nb),s,y(nbo)
+!-
+  INTEGER :: i
+!---------------------------------------------------------------------
+  DO i=1,nb
+    y(i) =  s-x(i)
+  ENDDO
+!-
+  nbo = nb
+  ma_subi_r11 = 0
+!-----------------------
+END FUNCTION ma_subi_r11
+!===
+INTEGER FUNCTION ma_mult_r11(nb,x,s,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo
+  REAL :: x(nb),s,y(nbo)
+!-
+  INTEGER :: i
+!---------------------------------------------------------------------
+  DO i=1,nb
+    y(i) = x(i)*s
+  ENDDO
+!-
+  nbo = nb
+  ma_mult_r11 = 0
+!-----------------------
+END FUNCTION ma_mult_r11
+!===
+INTEGER FUNCTION ma_div_r11(nb,x,s,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo
+  REAL :: x(nb),s,y(nbo)
+!-
+  INTEGER :: i
+!---------------------------------------------------------------------
+  DO i=1,nb
+    y(i) = x(i)/s
+  ENDDO
+!-
+  nbo = nb
+  ma_div_r11 = 0
+!-----------------------
+  END FUNCTION ma_div_r11
+!===
+INTEGER FUNCTION ma_divi_r11(nb,x,s,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo
+  REAL :: x(nb),s,y(nbo)
+!-
+  INTEGER :: i
+!---------------------------------------------------------------------
+  DO i=1,nb
+    y(i) = s/x(i)
+  ENDDO
+!-
+  nbo = nb
+  ma_divi_r11 = 0
+!-----------------------
+END FUNCTION ma_divi_r11
+!===
+INTEGER FUNCTION ma_power_r11(nb,x,s,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo
+  REAL :: x(nb),s,y(nbo)
+!-
+  INTEGER :: i
+!---------------------------------------------------------------------
+  DO i=1,nb
+    y(i) = x(i)**s
+  ENDDO
+!-
+  nbo = nb
+  ma_power_r11 = 0
+!-----------------------
+END FUNCTION ma_power_r11
+!===
+INTEGER FUNCTION ma_fumin_r11(nb,x,s,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo
+  REAL :: x(nb),s,y(nbo)
+!-
+  INTEGER :: i
+!---------------------------------------------------------------------
+  DO i=1,nb
+    y(i) = MIN(x(i),s)
+  ENDDO
+!-
+  nbo = nb
+  ma_fumin_r11 = 0
+!------------------------
+END FUNCTION ma_fumin_r11
+!===
+INTEGER FUNCTION ma_fumax_r11(nb,x,s,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo
+  REAL :: x(nb),s,y(nbo)
+!-
+  INTEGER :: i
+!---------------------------------------------------------------------
+  DO i=1,nb
+    y(i) = MAX(x(i),s)
+  ENDDO
+!-
+  nbo = nb
+  ma_fumax_r11 = 0
+!------------------------
+END FUNCTION ma_fumax_r11
+!===
+INTEGER FUNCTION ma_fuscat_r11(nb,x,nbi,ind,miss_val,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo,nbi
+  INTEGER :: ind(nbi)
+  REAL :: x(nb),miss_val,y(nbo)
+!-
+  INTEGER :: i,ii,ipos
+!---------------------------------------------------------------------
+  ma_fuscat_r11 = 0
+!-
+  y(1:nbo) = miss_val
+!-
+  IF (nbi <= nb) THEN
+    ipos = 0
+    DO i=1,nbi
+      IF (ind(i) <= nbo .AND. ind(i) > 0) THEN
+        ipos = ipos+1
+        y(ind(i)) = x(ipos)
+      ELSE
+        IF (ind(i) > nbo) ma_fuscat_r11  = ma_fuscat_r11+1
+      ENDIF
+    ENDDO
+!-- Repeat the data if needed
+    IF (MINVAL(ind) < 0) THEN
+      DO i=1,nbi
+        IF (ind(i) <= 0) THEN
+          DO ii=1,ABS(ind(i))-1
+            IF (ind(i+1)+ii <= nbo) THEN
+              y(ind(i+1)+ii) = y(ind(i+1))
+            ELSE
+              ma_fuscat_r11  = ma_fuscat_r11+1
+            ENDIF
+          ENDDO
+        ENDIF
+      ENDDO
+    ENDIF
+  ELSE
+    ma_fuscat_r11  = 1
+  ENDIF
+!-------------------------
+END FUNCTION ma_fuscat_r11
+!===
+INTEGER FUNCTION ma_fugath_r11(nb,x,nbi,ind,miss_val,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo,nbi
+  INTEGER :: ind(nbi)
+  REAL :: x(nb),miss_val,y(nbo)
+!-
+  INTEGER :: i,ipos
+!---------------------------------------------------------------------
+  IF (nbi <= nbo) THEN
+    ma_fugath_r11 = 0
+    y(1:nbo) = miss_val
+    ipos = 0
+    DO i=1,nbi
+      IF (ipos+1 <= nbo) THEN
+        IF (ind(i) > 0) THEN
+          ipos = ipos+1
+          y(ipos) = x(ind(i))
+        ENDIF
+      ELSE
+        IF (ipos+1 > nbo) ma_fugath_r11  = ma_fugath_r11+1
+      ENDIF
+    ENDDO
+  ELSE
+    ma_fugath_r11 = 1
+  ENDIF
+!-
+  nbo = ipos
+!-------------------------
+END FUNCTION ma_fugath_r11
+!===
+INTEGER FUNCTION ma_fufill_r11(nb,x,nbi,ind,miss_val,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo,nbi
+  INTEGER :: ind(nbi)
+  REAL :: x(nb),miss_val,y(nbo)
+!-
+  INTEGER :: i,ii,ipos
+!---------------------------------------------------------------------
+  ma_fufill_r11 = 0
+!-
+  IF (nbi <= nb) THEN
+    ipos = 0
+    DO i=1,nbi
+      IF (ind(i) <= nbo .AND. ind(i) > 0) THEN
+        ipos = ipos+1
+        y(ind(i)) = x(ipos)
+      ELSE
+        IF (ind(i) > nbo) ma_fufill_r11  = ma_fufill_r11+1
+      ENDIF
+    ENDDO
+!-- Repeat the data if needed
+    IF (MINVAL(ind) < 0) THEN
+      DO i=1,nbi
+        IF (ind(i) <= 0) THEN
+          DO ii=1,ABS(ind(i))-1
+            IF (ind(i+1)+ii <= nbo) THEN
+              y(ind(i+1)+ii) = y(ind(i+1))
+            ELSE
+              ma_fufill_r11  = ma_fufill_r11+1
+            ENDIF
+          ENDDO
+        ENDIF
+      ENDDO
+    ENDIF
+  ELSE
+    ma_fufill_r11  = 1
+  ENDIF
+!-------------------------
+END FUNCTION ma_fufill_r11
+!===
+INTEGER FUNCTION ma_fucoll_r11(nb,x,nbi,ind,miss_val,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo,nbi
+  INTEGER :: ind(nbi)
+  REAL :: x(nb),miss_val,y(nbo)
+!-
+  INTEGER :: i,ipos
+!---------------------------------------------------------------------
+  IF (nbi <= nbo) THEN
+    ma_fucoll_r11 = 0
+    ipos = 0
+    DO i=1,nbi
+      IF (ipos+1 <= nbo) THEN
+        IF (ind(i) > 0) THEN
+          ipos = ipos+1
+          y(ipos) = x(ind(i))
+        ENDIF
+      ELSE
+        IF (ipos+1 > nbo) ma_fucoll_r11  = ma_fucoll_r11+1
+      ENDIF
+    ENDDO
+  ELSE
+    ma_fucoll_r11 = 1
+  ENDIF
+!-
+  nbo = ipos
+!-------------------------
+END FUNCTION ma_fucoll_r11
+!===
+INTEGER FUNCTION ma_fuundef_r11(nb,x,nbi,ind,miss_val,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo,nbi
+  INTEGER :: ind(nbi)
+  REAL :: x(nb),miss_val,y(nbo)
+!-
+  INTEGER :: i
+!---------------------------------------------------------------------
+  IF (nbi <= nbo .AND. nbo == nb) THEN
+    ma_fuundef_r11 = 0
+    DO i=1,nbo
+      y(i) = x(i)
+    ENDDO
+    DO i=1,nbi
+      IF (ind(i) <= nbo .AND. ind(i) > 0) THEN
+        y(ind(i)) =  miss_val
+      ELSE
+        IF (ind(i) > nbo) ma_fuundef_r11  = ma_fuundef_r11+1
+      ENDIF
+    ENDDO
+  ELSE
+    ma_fuundef_r11 = 1
+  ENDIF
+!--------------------------
+END FUNCTION ma_fuundef_r11
+!===
+INTEGER FUNCTION ma_fuonly_r11(nb,x,nbi,ind,miss_val,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb,nbo,nbi
+  INTEGER :: ind(nbi)
+  REAL :: x(nb),miss_val,y(nbo)
+!-
+  INTEGER :: i
+!---------------------------------------------------------------------
+  IF (     (nbi <= nbo).AND.(nbo == nb) &
+ &    .AND.ALL(ind(1:nbi) <= nbo) ) THEN
+    ma_fuonly_r11 = 0
+    y(1:nbo) = miss_val
+    DO i=1,nbi
+      IF (ind(i) > 0) THEN
+        y(ind(i)) =  x(ind(i))
+      ENDIF
+    ENDDO
+  ELSE
+    ma_fuonly_r11 = 1
+  ENDIF
+!-------------------------
+END FUNCTION ma_fuonly_r11
+!===
+!===
+SUBROUTINE mathop_r21 &
+ &  (fun,nb,work_in,miss_val,nb_index,nindex,scal,nb_max,work_out)
+!---------------------------------------------------------------------
+!- This subroutines gives an interface to the various operations
+!- which are allowed. The interface is general enough to allow its use
+!- for other cases.
+!-
+!- INPUT
+!-
+!- fun      : function to be applied to the vector of data
+!- nb       : Length of input vector
+!- work_in  : Input vector of data (REAL)
+!- miss_val : The value of the missing data flag (it has to be a
+!-            maximum value, in f90 : huge( a real ))
+!- nb_index : Length of index vector
+!- nindex   : Vector of indices
+!- scal     : A scalar value for vector/scalar operations
+!- nb_max   : maximum length of output vector
+!-
+!- OUTPUT
+!-
+!- nb_max   : Actual length of output variable
+!- work_out : Output vector after the operation was applied
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=7) :: fun
+  INTEGER :: nb(2),nb_max,nb_index
+  INTEGER :: nindex(nb_index)
+  REAL :: work_in(nb(1),nb(2)),scal,miss_val
+  REAL :: work_out(nb_max)
+!-
+  INTEGER :: ierr
+!-
+  INTRINSIC SIN,COS,TAN,ASIN,ACOS,ATAN,EXP,LOG,SQRT,ABS
+!---------------------------------------------------------------------
+  ierr = 0
+!-
+  IF (scal >= miss_val-1.) THEN
+    IF (INDEX(indexfu,fun(1:LEN_TRIM(fun))) == 0) THEN
+      SELECT CASE (fun)
+      CASE('sin')
+        ierr = ma_sin_r21(nb,work_in,nb_max,work_out)
+      CASE('cos')
+        ierr = ma_cos_r21(nb,work_in,nb_max,work_out)
+      CASE('tan')
+        ierr = ma_tan_r21(nb,work_in,nb_max,work_out)
+      CASE('asin')
+        ierr = ma_asin_r21(nb,work_in,nb_max,work_out)
+      CASE('acos')
+        ierr = ma_acos_r21(nb,work_in,nb_max,work_out)
+      CASE('atan')
+        ierr = ma_atan_r21(nb,work_in,nb_max,work_out)
+      CASE('exp')
+        ierr = ma_exp_r21(nb,work_in,nb_max,work_out)
+      CASE('log')
+        ierr = ma_log_r21(nb,work_in,nb_max,work_out)
+      CASE('sqrt')
+        ierr = ma_sqrt_r21(nb,work_in,nb_max,work_out)
+      CASE('chs')
+        ierr = ma_chs_r21(nb,work_in,nb_max,work_out)
+      CASE('abs')
+        ierr = ma_abs_r21(nb,work_in,nb_max,work_out)
+      CASE('cels')
+        ierr = ma_cels_r21(nb,work_in,nb_max,work_out)
+      CASE('kelv')
+        ierr = ma_kelv_r21(nb,work_in,nb_max,work_out)
+      CASE('deg')
+        ierr = ma_deg_r21(nb,work_in,nb_max,work_out)
+      CASE('rad')
+        ierr = ma_rad_r21(nb,work_in,nb_max,work_out)
+      CASE('ident')
+        ierr = ma_ident_r21(nb,work_in,nb_max,work_out)
+      CASE DEFAULT
+        CALL ipslerr(3,"mathop", &
+ &        'scalar variable undefined and no indexing', &
+ &        'but still unknown function',fun)
+      END SELECT
+      IF (ierr > 0) THEN
+        CALL ipslerr(3,"mathop", &
+ &        'Error while executing a simple function',fun,' ')
+      ENDIF
+    ELSE
+      SELECT CASE (fun)
+      CASE('gather')
+        ierr = ma_fugath_r21(nb,work_in,nb_index,nindex, &
+ &                            miss_val,nb_max,work_out)
+      CASE('scatter')
+        IF (nb_index > (nb(1)*nb(2)) ) THEN
+          work_out(1:nb_max) = miss_val
+          ierr=1
+        ELSE
+          ierr = ma_fuscat_r21(nb,work_in,nb_index,nindex, &
+ &                             miss_val,nb_max,work_out)
+        ENDIF
+      CASE('coll')
+        ierr = ma_fucoll_r21(nb,work_in,nb_index,nindex, &
+ &                           miss_val,nb_max,work_out)
+      CASE('fill')
+        ierr = ma_fufill_r21(nb,work_in,nb_index,nindex, &
+ &                           miss_val,nb_max,work_out)
+      CASE('undef')
+        ierr = ma_fuundef_r21(nb,work_in,nb_index,nindex, &
+ &                           miss_val,nb_max,work_out)
+      CASE('only')
+        ierr = ma_fuonly_r21(nb,work_in,nb_index,nindex, &
+ &                           miss_val,nb_max,work_out)
+      CASE DEFAULT
+        CALL ipslerr(3,"mathop", &
+ &        'scalar variable undefined and indexing', &
+ &        'was requested but with unknown function',fun)
+      END SELECT
+      IF (ierr > 0) THEN
+        CALL ipslerr(3,"mathop_r21", &
+ &        'Error while executing an indexing function',fun,' ')
+      ENDIF
+    ENDIF
+  ELSE
+    SELECT CASE (fun)
+    CASE('fumin')
+      ierr = ma_fumin_r21(nb,work_in,scal,nb_max,work_out)
+    CASE('fumax')
+      ierr = ma_fumax_r21(nb,work_in,scal,nb_max,work_out)
+    CASE('add')
+      ierr = ma_add_r21(nb,work_in,scal,nb_max,work_out)
+    CASE('subi')
+      ierr = ma_subi_r21(nb,work_in,scal,nb_max,work_out)
+    CASE('sub')
+      ierr = ma_sub_r21(nb,work_in,scal,nb_max,work_out)
+    CASE('mult')
+      ierr = ma_mult_r21(nb,work_in,scal,nb_max,work_out)
+    CASE('div')
+      ierr = ma_div_r21(nb,work_in,scal,nb_max,work_out)
+    CASE('divi')
+      ierr = ma_divi_r21(nb,work_in,scal,nb_max,work_out)
+    CASE('power')
+      ierr = ma_power_r21(nb,work_in,scal,nb_max,work_out)
+    CASE DEFAULT
+      CALL ipslerr(3,"mathop", &
+ &      'Unknown operation with a scalar',fun,' ')
+    END SELECT
+    IF (ierr > 0) THEN
+      CALL ipslerr(3,"mathop", &
+ &      'Error while executing a scalar function',fun,' ')
+    ENDIF
+  ENDIF
+!------------------------
+END SUBROUTINE mathop_r21
+!-
+!=== FUNCTIONS (only one argument)
+!-
+INTEGER FUNCTION ma_sin_r21(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo,i,j,ij
+  REAL :: x(nb(1),nb(2)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO j=1,nb(2)
+    DO i=1,nb(1)
+      ij = ij+1
+      y(ij) = SIN(x(i,j))
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)
+  ma_sin_r21 = 0
+!----------------------
+END FUNCTION ma_sin_r21
+!===
+INTEGER FUNCTION ma_cos_r21(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo,i,j,ij
+  REAL :: x(nb(1),nb(2)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO j=1,nb(2)
+    DO i=1,nb(1)
+      ij = ij+1
+      y(ij) = COS(x(i,j))
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)
+  ma_cos_r21 = 0
+!----------------------
+END FUNCTION ma_cos_r21
+!===
+INTEGER FUNCTION ma_tan_r21(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo,i,j,ij
+  REAL :: x(nb(1),nb(2)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO j=1,nb(2)
+    DO i=1,nb(1)
+      ij = ij+1
+      y(ij) = TAN(x(i,j))
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)
+  ma_tan_r21 = 0
+!----------------------
+END FUNCTION ma_tan_r21
+!===
+  INTEGER FUNCTION ma_asin_r21(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo,i,j,ij
+  REAL :: x(nb(1),nb(2)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO j=1,nb(2)
+    DO i=1,nb(1)
+      ij = ij+1
+      y(ij) = ASIN(x(i,j))
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)
+  ma_asin_r21 = 0
+!-----------------------
+END FUNCTION ma_asin_r21
+!===
+INTEGER FUNCTION ma_acos_r21(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo,i,j,ij
+  REAL :: x(nb(1),nb(2)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO j=1,nb(2)
+    DO i=1,nb(1)
+      ij = ij+1
+      y(ij) = ACOS(x(i,j))
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)
+  ma_acos_r21 = 0
+!-----------------------
+END FUNCTION ma_acos_r21
+!===
+INTEGER FUNCTION ma_atan_r21(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo,i,j,ij
+  REAL :: x(nb(1),nb(2)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO j=1,nb(2)
+    DO i=1,nb(1)
+      ij = ij+1
+      y(ij) = ATAN(x(i,j))
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)
+  ma_atan_r21 = 0
+!-----------------------
+END FUNCTION ma_atan_r21
+!===
+INTEGER FUNCTION ma_exp_r21(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo,i,j,ij
+  REAL :: x(nb(1),nb(2)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO j=1,nb(2)
+    DO i=1,nb(1)
+      ij = ij+1
+      y(ij) = EXP(x(i,j))
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)
+  ma_exp_r21 = 0
+!----------------------
+END FUNCTION ma_exp_r21
+!===
+INTEGER FUNCTION ma_log_r21(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo,i,j,ij
+  REAL :: x(nb(1),nb(2)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO j=1,nb(2)
+    DO i=1,nb(1)
+      ij = ij+1
+      y(ij) = LOG(x(i,j))
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)
+  ma_log_r21 = 0
+!----------------------
+END FUNCTION ma_log_r21
+!===
+INTEGER FUNCTION ma_sqrt_r21(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo,i,j,ij
+  REAL :: x(nb(1),nb(2)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO j=1,nb(2)
+    DO i=1,nb(1)
+      ij = ij+1
+      y(ij) = SQRT(x(i,j))
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)
+  ma_sqrt_r21 = 0
+!-----------------------
+END FUNCTION ma_sqrt_r21
+!===
+INTEGER FUNCTION ma_abs_r21(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo,i,j,ij
+  REAL :: x(nb(1),nb(2)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO j=1,nb(2)
+    DO i=1,nb(1)
+      ij = ij+1
+      y(ij) = ABS(x(i,j))
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)
+  ma_abs_r21 = 0
+!----------------------
+END FUNCTION ma_abs_r21
+!===
+INTEGER FUNCTION ma_chs_r21(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo,i,j,ij
+  REAL :: x(nb(1),nb(2)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO j=1,nb(2)
+    DO i=1,nb(1)
+      ij = ij+1
+      y(ij) = x(i,j)*(-1.)
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)
+  ma_chs_r21 = 0
+!----------------------
+END FUNCTION ma_chs_r21
+!===
+INTEGER FUNCTION ma_cels_r21(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo,i,j,ij
+  REAL :: x(nb(1),nb(2)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO j=1,nb(2)
+    DO i=1,nb(1)
+      ij = ij+1
+      y(ij) = x(i,j)-273.15
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)
+  ma_cels_r21 = 0
+!-----------------------
+END FUNCTION ma_cels_r21
+!===
+INTEGER FUNCTION ma_kelv_r21(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo,i,j,ij
+  REAL :: x(nb(1),nb(2)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO j=1,nb(2)
+    DO i=1,nb(1)
+      ij = ij+1
+      y(ij) = x(i,j)+273.15
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)
+  ma_kelv_r21 = 0
+!-----------------------
+END FUNCTION ma_kelv_r21
+!===
+INTEGER FUNCTION ma_deg_r21(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo,i,j,ij
+  REAL :: x(nb(1),nb(2)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO j=1,nb(2)
+    DO i=1,nb(1)
+      ij = ij+1
+      y(ij) = x(i,j)*57.29577951
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)
+  ma_deg_r21 = 0
+!----------------------
+END FUNCTION ma_deg_r21
+!===
+INTEGER FUNCTION ma_rad_r21(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo,i,j,ij
+  REAL :: x(nb(1),nb(2)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO j=1,nb(2)
+    DO i=1,nb(1)
+      ij = ij+1
+      y(ij) = x(i,j)*0.01745329252
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)
+  ma_rad_r21 = 0
+!----------------------
+END FUNCTION ma_rad_r21
+!===
+INTEGER FUNCTION ma_ident_r21(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo,i,j,ij
+  REAL :: x(nb(1),nb(2)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO j=1,nb(2)
+    DO i=1,nb(1)
+      ij = ij+1
+      y(ij) = x(i,j)
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)
+  ma_ident_r21 = 0
+!------------------------
+END FUNCTION ma_ident_r21
+!-
+!=== OPERATIONS (two argument)
+!-
+INTEGER FUNCTION ma_add_r21(nb,x,s,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo
+  REAL :: x(nb(1),nb(2)),s,y(nbo)
+!-
+  INTEGER :: i,j,ij
+!---------------------------------------------------------------------
+  ij = 0
+  DO j=1,nb(2)
+    DO i=1,nb(1)
+      ij = ij+1
+      y(ij) = x(i,j)+s
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)
+  ma_add_r21 = 0
+!----------------------
+END FUNCTION ma_add_r21
+!===
+INTEGER FUNCTION ma_sub_r21(nb,x,s,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo
+  REAL :: x(nb(1),nb(2)),s,y(nbo)
+!-
+  INTEGER :: i,j,ij
+!---------------------------------------------------------------------
+  ij = 0
+  DO j=1,nb(2)
+    DO i=1,nb(1)
+      ij = ij+1
+      y(ij) = x(i,j)-s
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)
+  ma_sub_r21 = 0
+!----------------------
+END FUNCTION ma_sub_r21
+!===
+INTEGER FUNCTION ma_subi_r21(nb,x,s,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo
+  REAL :: x(nb(1),nb(2)),s,y(nbo)
+!-
+  INTEGER :: i,j,ij
+!---------------------------------------------------------------------
+  ij = 0
+  DO j=1,nb(2)
+    DO i=1,nb(1)
+      ij = ij+1
+      y(ij) =  s-x(i,j)
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)
+  ma_subi_r21 = 0
+!-----------------------
+END FUNCTION ma_subi_r21
+!===
+INTEGER FUNCTION ma_mult_r21(nb,x,s,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo
+  REAL :: x(nb(1),nb(2)),s,y(nbo)
+!-
+  INTEGER :: i,j,ij
+!---------------------------------------------------------------------
+  ij = 0
+  DO j=1,nb(2)
+    DO i=1,nb(1)
+      ij = ij+1
+      y(ij) = x(i,j)*s
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)
+  ma_mult_r21 = 0
+!-----------------------
+END FUNCTION ma_mult_r21
+!===
+INTEGER FUNCTION ma_div_r21(nb,x,s,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo
+  REAL :: x(nb(1),nb(2)),s,y(nbo)
+!-
+  INTEGER :: i,j,ij
+!---------------------------------------------------------------------
+  ij = 0
+  DO j=1,nb(2)
+    DO i=1,nb(1)
+      ij = ij+1
+      y(ij) = x(i,j)/s
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)
+  ma_div_r21 = 0
+!----------------------
+END FUNCTION ma_div_r21
+!===
+INTEGER FUNCTION ma_divi_r21(nb,x,s,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo
+  REAL :: x(nb(1),nb(2)),s,y(nbo)
+!-
+  INTEGER :: i,j,ij
+!---------------------------------------------------------------------
+  ij = 0
+  DO j=1,nb(2)
+    DO i=1,nb(1)
+      ij = ij+1
+      y(ij) = s/x(i,j)
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)
+  ma_divi_r21 = 0
+!-----------------------
+END FUNCTION ma_divi_r21
+!===
+INTEGER FUNCTION ma_power_r21(nb,x,s,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo
+  REAL :: x(nb(1),nb(2)),s,y(nbo)
+!-
+  INTEGER :: i,j,ij
+!---------------------------------------------------------------------
+  ij = 0
+  DO j=1,nb(2)
+    DO i=1,nb(1)
+      ij = ij+1
+      y(ij) = x(i,j) ** s
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)
+  ma_power_r21 = 0
+!------------------------
+END FUNCTION ma_power_r21
+!===
+INTEGER FUNCTION ma_fumin_r21(nb,x,s,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo
+  REAL :: x(nb(1),nb(2)),s,y(nbo)
+!-
+  INTEGER :: i,j,ij
+!---------------------------------------------------------------------
+  ij = 0
+  DO j=1,nb(2)
+    DO i=1,nb(1)
+      ij = ij+1
+      y(ij) = MIN(x(i,j),s)
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)
+  ma_fumin_r21 = 0
+!------------------------
+END FUNCTION ma_fumin_r21
+!===
+INTEGER FUNCTION ma_fumax_r21(nb,x,s,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo
+  REAL :: x(nb(1),nb(2)),s,y(nbo)
+!-
+  INTEGER :: i,j,ij
+!---------------------------------------------------------------------
+  ij = 0
+  DO j=1,nb(2)
+    DO i=1,nb(1)
+      ij = ij+1
+      y(ij) = MAX(x(i,j),s)
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)
+  ma_fumax_r21 = 0
+!------------------------
+END FUNCTION ma_fumax_r21
+!===
+INTEGER FUNCTION ma_fuscat_r21(nb,x,nbi,ind,miss_val,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo,nbi
+  INTEGER :: ind(nbi)
+  REAL :: x(nb(1),nb(2)),miss_val,y(nbo)
+!-
+  INTEGER :: i,j,ij,ii,ipos
+!---------------------------------------------------------------------
+  ma_fuscat_r21 = 0
+!-
+  y(1:nbo) = miss_val
+!-
+  IF (nbi <= nb(1)*nb(2)) THEN
+    ipos = 0
+    DO ij=1,nbi
+       IF (ind(ij) <= nbo .AND. ind(ij) > 0) THEN
+         ipos = ipos+1
+         j = ((ipos-1)/nb(1))+1
+         i = (ipos-(j-1)*nb(1))
+         y(ind(ij)) = x(i,j)
+       ELSE
+         IF (ind(ij) > nbo) ma_fuscat_r21  = ma_fuscat_r21+1
+       ENDIF
+    ENDDO
+!-- Repeat the data if needed
+    IF (MINVAL(ind) < 0) THEN
+      DO i=1,nbi
+        IF (ind(i) <= 0) THEN
+          DO ii=1,ABS(ind(i))-1
+            IF (ind(i+1)+ii <= nbo) THEN
+              y(ind(i+1)+ii) = y(ind(i+1))
+            ELSE
+              ma_fuscat_r21  = ma_fuscat_r21+1
+            ENDIF
+          ENDDO
+        ENDIF
+      ENDDO
+    ENDIF
+  ELSE
+    ma_fuscat_r21  = 1
+  ENDIF
+!-------------------------
+END FUNCTION ma_fuscat_r21
+!===
+INTEGER FUNCTION ma_fugath_r21(nb,x,nbi,ind,miss_val,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo,nbi
+  INTEGER :: ind(nbi)
+  REAL :: x(nb(1),nb(2)),miss_val,y(nbo)
+!-
+  INTEGER :: i,j,ij,ipos
+!---------------------------------------------------------------------
+  IF (nbi <= nbo) THEN
+    ma_fugath_r21 = 0
+    y(1:nbo) = miss_val
+    ipos = 0
+    DO ij=1,nbi
+      IF (ipos+1 <= nbo) THEN
+        IF (ind(ij) > 0) THEN
+          j = ((ind(ij)-1)/nb(1))+1
+          i = (ind(ij)-(j-1)*nb(1))
+          ipos = ipos+1
+          y(ipos) = x(i,j)
+        ENDIF
+      ELSE
+        IF (ipos+1 > nbo) ma_fugath_r21  = ma_fugath_r21+1
+      ENDIF
+    ENDDO
+  ELSE
+    ma_fugath_r21 = 1
+  ENDIF
+  nbo = ipos
+!-------------------------
+END FUNCTION ma_fugath_r21
+!===
+INTEGER FUNCTION ma_fufill_r21(nb,x,nbi,ind,miss_val,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo,nbi
+  INTEGER :: ind(nbi)
+  REAL :: x(nb(1),nb(2)),miss_val,y(nbo)
+!-
+  INTEGER :: i,j,ij,ii,ipos
+!---------------------------------------------------------------------
+  ma_fufill_r21 = 0
+!-
+  IF (nbi <= nb(1)*nb(2)) THEN
+    ipos = 0
+    DO ij=1,nbi
+      IF (ind(ij) <= nbo .AND. ind(ij) > 0) THEN
+        ipos = ipos+1
+        j = ((ipos-1)/nb(1))+1
+        i = (ipos-(j-1)*nb(1))
+        y(ind(ij)) = x(i,j)
+      ELSE
+        IF (ind(ij) > nbo) ma_fufill_r21  = ma_fufill_r21+1
+      ENDIF
+    ENDDO
+!-- Repeat the data if needed
+    IF (MINVAL(ind) < 0) THEN
+      DO i=1,nbi
+        IF (ind(i) <= 0) THEN
+          DO ii=1,ABS(ind(i))-1
+            IF (ind(i+1)+ii <= nbo) THEN
+              y(ind(i+1)+ii) = y(ind(i+1))
+            ELSE
+              ma_fufill_r21  = ma_fufill_r21+1
+            ENDIF
+          ENDDO
+        ENDIF
+      ENDDO
+    ENDIF
+  ELSE
+    ma_fufill_r21  = 1
+  ENDIF
+!-------------------------
+END FUNCTION ma_fufill_r21
+!===
+INTEGER FUNCTION ma_fucoll_r21(nb,x,nbi,ind,miss_val,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo,nbi
+  INTEGER :: ind(nbi)
+  REAL :: x(nb(1),nb(2)),miss_val,y(nbo)
+!-
+  INTEGER :: i,j,ij,ipos
+!---------------------------------------------------------------------
+  IF (nbi <= nbo) THEN
+    ma_fucoll_r21 = 0
+    ipos = 0
+    DO ij=1,nbi
+      IF (ipos+1 <= nbo) THEN
+        IF (ind(ij) > 0) THEN
+          j = ((ind(ij)-1)/nb(1))+1
+          i = (ind(ij)-(j-1)*nb(1))
+          ipos = ipos+1
+          y(ipos) = x(i,j)
+        ENDIF
+      ELSE
+        IF (ipos+1 > nbo) ma_fucoll_r21  = ma_fucoll_r21+1
+      ENDIF
+    ENDDO
+  ELSE
+    ma_fucoll_r21 = 1
+  ENDIF
+  nbo = ipos
+!-------------------------
+END FUNCTION ma_fucoll_r21
+!===
+INTEGER FUNCTION ma_fuundef_r21(nb,x,nbi,ind,miss_val,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo,nbi
+  INTEGER :: ind(nbi)
+  REAL :: x(nb(1),nb(2)),miss_val,y(nbo)
+!-
+  INTEGER :: i,j,ij
+!---------------------------------------------------------------------
+  IF (nbi <= nbo .AND. nbo == nb(1)*nb(2)) THEN
+    ma_fuundef_r21 = 0
+    DO ij=1,nbo
+      j = ((ij-1)/nb(1))+1
+      i = (ij-(j-1)*nb(1))
+      y(ij) = x(i,j)
+    ENDDO
+    DO i=1,nbi
+      IF (ind(i) <= nbo .AND. ind(i) > 0) THEN
+        y(ind(i)) =  miss_val
+      ELSE
+        IF (ind(i) > nbo) ma_fuundef_r21 = ma_fuundef_r21+1
+      ENDIF
+    ENDDO
+  ELSE
+    ma_fuundef_r21 = 1
+  ENDIF
+!--------------------------
+END FUNCTION ma_fuundef_r21
+!===
+INTEGER FUNCTION ma_fuonly_r21(nb,x,nbi,ind,miss_val,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(2),nbo,nbi
+  INTEGER :: ind(nbi)
+  REAL :: x(nb(1),nb(2)),miss_val,y(nbo)
+!-
+  INTEGER :: i,j,ij
+!---------------------------------------------------------------------
+  IF (     (nbi <= nbo).AND.(nbo == nb(1)*nb(2)) &
+ &    .AND.ALL(ind(1:nbi) <= nbo) ) THEN
+    ma_fuonly_r21 = 0
+    y(1:nbo) = miss_val
+    DO ij=1,nbi
+      IF (ind(ij) > 0) THEN
+        j = ((ind(ij)-1)/nb(1))+1
+        i = (ind(ij)-(j-1)*nb(1))
+        y(ind(ij)) =  x(i,j)
+      ENDIF
+    ENDDO
+  ELSE
+    ma_fuonly_r21 = 1
+  ENDIF
+!-------------------------
+END FUNCTION ma_fuonly_r21
+!===
+!===
+SUBROUTINE mathop_r31 &
+ &  (fun,nb,work_in,miss_val,nb_index,nindex,scal,nb_max,work_out)
+!---------------------------------------------------------------------
+!- This subroutines gives an interface to the various operations
+!- which are allowed. The interface is general enough to allow its use
+!- for other cases.
+!-
+!- INPUT
+!-
+!- fun      : function to be applied to the vector of data
+!- nb       : Length of input vector
+!- work_in  : Input vector of data (REAL)
+!- miss_val : The value of the missing data flag (it has to be a
+!-            maximum value, in f90 : huge( a real ))
+!- nb_index : Length of index vector
+!- nindex   : Vector of indices
+!- scal     : A scalar value for vector/scalar operations
+!- nb_max   : maximum length of output vector
+!-
+!- OUTPUT
+!-
+!- nb_max   : Actual length of output variable
+!- work_out : Output vector after the operation was applied
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=7) :: fun
+  INTEGER :: nb(3),nb_max,nb_index
+  INTEGER :: nindex(nb_index)
+  REAL :: work_in(nb(1),nb(2),nb(3)),scal,miss_val
+  REAL :: work_out(nb_max)
+!-
+  INTEGER :: ierr
+!-
+  INTRINSIC SIN,COS,TAN,ASIN,ACOS,ATAN,EXP,LOG,SQRT,ABS
+!---------------------------------------------------------------------
+  ierr = 0
+!-
+  IF (scal >= miss_val-1.) THEN
+    IF (INDEX(indexfu,fun(1:LEN_TRIM(fun))) == 0) THEN
+      SELECT CASE (fun)
+      CASE('sin')
+        ierr = ma_sin_r31(nb,work_in,nb_max,work_out)
+      CASE('cos')
+        ierr = ma_cos_r31(nb,work_in,nb_max,work_out)
+      CASE('tan')
+        ierr = ma_tan_r31(nb,work_in,nb_max,work_out)
+      CASE('asin')
+        ierr = ma_asin_r31(nb,work_in,nb_max,work_out)
+      CASE('acos')
+        ierr = ma_acos_r31(nb,work_in,nb_max,work_out)
+      CASE('atan')
+        ierr = ma_atan_r31(nb,work_in,nb_max,work_out)
+      CASE('exp')
+        ierr = ma_exp_r31(nb,work_in,nb_max,work_out)
+      CASE('log')
+        ierr = ma_log_r31(nb,work_in,nb_max,work_out)
+      CASE('sqrt')
+        ierr = ma_sqrt_r31(nb,work_in,nb_max,work_out)
+      CASE('chs')
+        ierr = ma_chs_r31(nb,work_in,nb_max,work_out)
+      CASE('abs')
+        ierr = ma_abs_r31(nb,work_in,nb_max,work_out)
+      CASE('cels')
+        ierr = ma_cels_r31(nb,work_in,nb_max,work_out)
+      CASE('kelv')
+        ierr = ma_kelv_r31(nb,work_in,nb_max,work_out)
+      CASE('deg')
+        ierr = ma_deg_r31(nb,work_in,nb_max,work_out)
+      CASE('rad')
+        ierr = ma_rad_r31(nb,work_in,nb_max,work_out)
+      CASE('ident')
+        ierr = ma_ident_r31(nb,work_in,nb_max,work_out)
+      CASE DEFAULT
+        CALL ipslerr(3,"mathop", &
+ &        'scalar variable undefined and no indexing', &
+ &        'but still unknown function',fun)
+      END SELECT
+      IF (ierr > 0) THEN
+         CALL ipslerr(3,"mathop", &
+ &        'Error while executing a simple function',fun,' ')
+      ENDIF
+    ELSE
+      SELECT CASE (fun)
+      CASE('gather')
+        ierr = ma_fugath_r31(nb,work_in,nb_index,nindex, &
+ &                           miss_val,nb_max,work_out)
+      CASE('scatter')
+        IF (nb_index > (nb(1)*nb(2)*nb(3))) THEN
+          work_out(1:nb_max) = miss_val
+          ierr=1
+        ELSE
+          ierr = ma_fuscat_r31(nb,work_in,nb_index,nindex, &
+ &                             miss_val,nb_max,work_out)
+        ENDIF
+      CASE('coll')
+        ierr = ma_fucoll_r31(nb,work_in,nb_index,nindex, &
+ &                           miss_val,nb_max,work_out)
+      CASE('fill')
+        ierr = ma_fufill_r31(nb,work_in,nb_index,nindex, &
+ &                           miss_val,nb_max,work_out)
+      CASE('undef')
+        ierr = ma_fuundef_r31(nb,work_in,nb_index,nindex, &
+ &                           miss_val,nb_max,work_out)
+      CASE('only')
+        ierr = ma_fuonly_r31(nb,work_in,nb_index,nindex, &
+ &                           miss_val,nb_max,work_out)
+      CASE DEFAULT
+        CALL ipslerr(3,"mathop", &
+ &        'scalar variable undefined and indexing', &
+ &        'was requested but with unknown function',fun)
+      END SELECT
+      IF (ierr > 0) THEN
+        CALL ipslerr(3,"mathop_r31", &
+ &        'Error while executing an indexing function',fun,' ')
+      ENDIF
+    ENDIF
+  ELSE
+    SELECT CASE (fun)
+    CASE('fumin')
+      ierr = ma_fumin_r31(nb,work_in,scal,nb_max,work_out)
+    CASE('fumax')
+      ierr = ma_fumax_r31(nb,work_in,scal,nb_max,work_out)
+    CASE('add')
+      ierr = ma_add_r31(nb,work_in,scal,nb_max,work_out)
+    CASE('subi')
+      ierr = ma_subi_r31(nb,work_in,scal,nb_max,work_out)
+    CASE('sub')
+      ierr = ma_sub_r31(nb,work_in,scal,nb_max,work_out)
+    CASE('mult')
+      ierr = ma_mult_r31(nb,work_in,scal,nb_max,work_out)
+    CASE('div')
+      ierr = ma_div_r31(nb,work_in,scal,nb_max,work_out)
+    CASE('divi')
+      ierr = ma_divi_r31(nb,work_in,scal,nb_max,work_out)
+    CASE('power')
+      ierr = ma_power_r31(nb,work_in,scal,nb_max,work_out)
+    CASE DEFAULT
+      CALL ipslerr(3,"mathop", &
+ &      'Unknown operation with a scalar',fun,' ')
+    END SELECT
+    IF (ierr > 0) THEN
+      CALL ipslerr(3,"mathop", &
+ &      'Error while executing a scalar function',fun,' ')
+    ENDIF
+  ENDIF
+!------------------------
+END SUBROUTINE mathop_r31
+!-
+!=== FUNCTIONS (only one argument)
+!-
+INTEGER FUNCTION ma_sin_r31(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo,i,j,k,ij
+  REAL :: x(nb(1),nb(2),nb(3)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO k=1,nb(3)
+    DO j=1,nb(2)
+      DO i=1,nb(1)
+        ij = ij+1
+        y(ij) = SIN(x(i,j,k))
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)*nb(3)
+  ma_sin_r31 = 0
+!----------------------
+END FUNCTION ma_sin_r31
+!===
+INTEGER FUNCTION ma_cos_r31(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo,i,j,k,ij
+  REAL :: x(nb(1),nb(2),nb(3)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO k=1,nb(3)
+    DO j=1,nb(2)
+      DO i=1,nb(1)
+        ij = ij+1
+        y(ij) = COS(x(i,j,k))
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)*nb(3)
+  ma_cos_r31 = 0
+!----------------------
+END FUNCTION ma_cos_r31
+!===
+INTEGER FUNCTION ma_tan_r31(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo,i,j,k,ij
+  REAL :: x(nb(1),nb(2),nb(3)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO k=1,nb(3)
+    DO j=1,nb(2)
+      DO i=1,nb(1)
+        ij = ij+1
+        y(ij) = TAN(x(i,j,k))
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)*nb(3)
+  ma_tan_r31 = 0
+!----------------------
+END FUNCTION ma_tan_r31
+!===
+INTEGER FUNCTION ma_asin_r31(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo,i,j,k,ij
+  REAL :: x(nb(1),nb(2),nb(3)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO k=1,nb(3)
+    DO j=1,nb(2)
+      DO i=1,nb(1)
+        ij = ij+1
+        y(ij) = ASIN(x(i,j,k))
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)*nb(3)
+  ma_asin_r31 = 0
+!-----------------------
+END FUNCTION ma_asin_r31
+!===
+INTEGER FUNCTION ma_acos_r31(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo,i,j,k,ij
+  REAL :: x(nb(1),nb(2),nb(3)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO k=1,nb(3)
+    DO j=1,nb(2)
+      DO i=1,nb(1)
+        ij = ij+1
+        y(ij) = ACOS(x(i,j,k))
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)*nb(3)
+  ma_acos_r31 = 0
+!-----------------------
+END FUNCTION ma_acos_r31
+!===
+INTEGER FUNCTION ma_atan_r31(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo,i,j,k,ij
+  REAL :: x(nb(1),nb(2),nb(3)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO k=1,nb(3)
+    DO j=1,nb(2)
+      DO i=1,nb(1)
+        ij = ij+1
+        y(ij) = ATAN(x(i,j,k))
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)*nb(3)
+  ma_atan_r31 = 0
+!-----------------------
+  END FUNCTION ma_atan_r31
+!===
+INTEGER FUNCTION ma_exp_r31(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo,i,j,k,ij
+  REAL :: x(nb(1),nb(2),nb(3)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO k=1,nb(3)
+    DO j=1,nb(2)
+      DO i=1,nb(1)
+        ij = ij+1
+        y(ij) = EXP(x(i,j,k))
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)*nb(3)
+  ma_exp_r31 = 0
+!----------------------
+END FUNCTION ma_exp_r31
+!===
+INTEGER FUNCTION ma_log_r31(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo,i,j,k,ij
+  REAL :: x(nb(1),nb(2),nb(3)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO k=1,nb(3)
+    DO j=1,nb(2)
+      DO i=1,nb(1)
+        ij = ij+1
+        y(ij) = LOG(x(i,j,k))
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)*nb(3)
+  ma_log_r31 = 0
+!----------------------
+END FUNCTION ma_log_r31
+!===
+INTEGER FUNCTION ma_sqrt_r31(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo,i,j,k,ij
+  REAL :: x(nb(1),nb(2),nb(3)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO k=1,nb(3)
+    DO j=1,nb(2)
+      DO i=1,nb(1)
+        ij = ij+1
+        y(ij) = SQRT(x(i,j,k))
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)*nb(3)
+  ma_sqrt_r31 = 0
+!-----------------------
+END FUNCTION ma_sqrt_r31
+!===
+INTEGER FUNCTION ma_abs_r31(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo,i,j,k,ij
+  REAL :: x(nb(1),nb(2),nb(3)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO k=1,nb(3)
+    DO j=1,nb(2)
+      DO i=1,nb(1)
+        ij = ij+1
+        y(ij) = ABS(x(i,j,k))
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)*nb(3)
+  ma_abs_r31 = 0
+!----------------------
+END FUNCTION ma_abs_r31
+!===
+INTEGER FUNCTION ma_chs_r31(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo,i,j,k,ij
+  REAL :: x(nb(1),nb(2),nb(3)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO k=1,nb(3)
+    DO j=1,nb(2)
+      DO i=1,nb(1)
+        ij = ij+1
+        y(ij) = x(i,j,k)*(-1.)
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)*nb(3)
+  ma_chs_r31 = 0
+!----------------------
+END FUNCTION ma_chs_r31
+!===
+INTEGER FUNCTION ma_cels_r31(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo,i,j,k,ij
+  REAL :: x(nb(1),nb(2),nb(3)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO k=1,nb(3)
+    DO j=1,nb(2)
+      DO i=1,nb(1)
+        ij = ij+1
+        y(ij) = x(i,j,k)-273.15
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)*nb(3)
+  ma_cels_r31 = 0
+!-----------------------
+END FUNCTION ma_cels_r31
+!===
+INTEGER FUNCTION ma_kelv_r31(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo,i,j,k,ij
+  REAL :: x(nb(1),nb(2),nb(3)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO k=1,nb(3)
+    DO j=1,nb(2)
+      DO i=1,nb(1)
+        ij = ij+1
+        y(ij) = x(i,j,k)+273.15
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)*nb(3)
+  ma_kelv_r31 = 0
+!-----------------------
+  END FUNCTION ma_kelv_r31
+!===
+INTEGER FUNCTION ma_deg_r31(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo,i,j,k,ij
+  REAL :: x(nb(1),nb(2),nb(3)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO k=1,nb(3)
+    DO j=1,nb(2)
+      DO i=1,nb(1)
+        ij = ij+1
+        y(ij) = x(i,j,k)*57.29577951
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)*nb(3)
+  ma_deg_r31 = 0
+!----------------------
+END FUNCTION ma_deg_r31
+!===
+INTEGER FUNCTION ma_rad_r31(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo,i,j,k,ij
+  REAL :: x(nb(1),nb(2),nb(3)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO k=1,nb(3)
+    DO j=1,nb(2)
+      DO i=1,nb(1)
+        ij = ij+1
+        y(ij) = x(i,j,k)*0.01745329252
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)*nb(3)
+  ma_rad_r31 = 0
+!----------------------
+END FUNCTION ma_rad_r31
+!===
+INTEGER FUNCTION ma_ident_r31(nb,x,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo,i,j,k,ij
+  REAL :: x(nb(1),nb(2),nb(3)),y(nbo)
+!---------------------------------------------------------------------
+  ij = 0
+  DO k=1,nb(3)
+    DO j=1,nb(2)
+      DO i=1,nb(1)
+        ij = ij+1
+        y(ij) = x(i,j,k)
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)*nb(3)
+  ma_ident_r31 = 0
+!------------------------
+END FUNCTION ma_ident_r31
+!-
+!=== OPERATIONS (two argument)
+!-
+INTEGER FUNCTION ma_add_r31(nb,x,s,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo
+  REAL :: x(nb(1),nb(2),nb(3)),s,y(nbo)
+!-
+  INTEGER :: i,j,k,ij
+!---------------------------------------------------------------------
+  ij = 0
+  DO k=1,nb(3)
+    DO j=1,nb(2)
+      DO i=1,nb(1)
+        ij = ij+1
+        y(ij) = x(i,j,k)+s
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)*nb(3)
+  ma_add_r31 = 0
+!----------------------
+END FUNCTION ma_add_r31
+!===
+INTEGER FUNCTION ma_sub_r31(nb,x,s,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo
+  REAL :: x(nb(1),nb(2),nb(3)),s,y(nbo)
+!-
+  INTEGER :: i,j,k,ij
+!---------------------------------------------------------------------
+  ij = 0
+  DO k=1,nb(3)
+    DO j=1,nb(2)
+      DO i=1,nb(1)
+        ij = ij+1
+        y(ij) = x(i,j,k)-s
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)*nb(3)
+  ma_sub_r31 = 0
+!----------------------
+END FUNCTION ma_sub_r31
+!===
+INTEGER FUNCTION ma_subi_r31(nb,x,s,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo
+  REAL :: x(nb(1),nb(2),nb(3)),s,y(nbo)
+!-
+  INTEGER :: i,j,k,ij
+!---------------------------------------------------------------------
+  ij = 0
+  DO k=1,nb(3)
+    DO j=1,nb(2)
+      DO i=1,nb(1)
+        ij = ij+1
+        y(ij) =  s-x(i,j,k)
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)*nb(3)
+  ma_subi_r31 = 0
+!-----------------------
+END FUNCTION ma_subi_r31
+!===
+INTEGER FUNCTION ma_mult_r31(nb,x,s,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo
+  REAL :: x(nb(1),nb(2),nb(3)),s,y(nbo)
+!-
+  INTEGER :: i,j,k,ij
+!---------------------------------------------------------------------
+  ij = 0
+  DO k=1,nb(3)
+    DO j=1,nb(2)
+      DO i=1,nb(1)
+        ij = ij+1
+        y(ij) = x(i,j,k)*s
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)*nb(3)
+  ma_mult_r31 = 0
+!-----------------------
+END FUNCTION ma_mult_r31
+!===
+INTEGER FUNCTION ma_div_r31(nb,x,s,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo
+  REAL :: x(nb(1),nb(2),nb(3)),s,y(nbo)
+!-
+  INTEGER :: i,j,k,ij
+!---------------------------------------------------------------------
+  ij = 0
+  DO k=1,nb(3)
+    DO j=1,nb(2)
+      DO i=1,nb(1)
+        ij = ij+1
+        y(ij) = x(i,j,k)/s
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)*nb(3)
+  ma_div_r31 = 0
+!----------------------
+END FUNCTION ma_div_r31
+!===
+INTEGER FUNCTION ma_divi_r31(nb,x,s,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo
+  REAL :: x(nb(1),nb(2),nb(3)),s,y(nbo)
+!-
+  INTEGER :: i,j,k,ij
+!---------------------------------------------------------------------
+  ij = 0
+  DO k=1,nb(3)
+    DO j=1,nb(2)
+      DO i=1,nb(1)
+        ij = ij+1
+        y(ij) = s/x(i,j,k)
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)*nb(3)
+  ma_divi_r31 = 0
+!-----------------------
+END FUNCTION ma_divi_r31
+!===
+INTEGER FUNCTION ma_power_r31(nb,x,s,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo
+  REAL :: x(nb(1),nb(2),nb(3)),s,y(nbo)
+!-
+  INTEGER :: i,j,k,ij
+!---------------------------------------------------------------------
+  ij = 0
+  DO k=1,nb(3)
+    DO j=1,nb(2)
+      DO i=1,nb(1)
+        ij = ij+1
+        y(ij) = x(i,j,k)**s
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)*nb(3)
+  ma_power_r31 = 0
+!------------------------
+END FUNCTION ma_power_r31
+!===
+INTEGER FUNCTION ma_fumin_r31(nb,x,s,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo
+  REAL :: x(nb(1),nb(2),nb(3)),s,y(nbo)
+!-
+  INTEGER :: i,j,k,ij
+!---------------------------------------------------------------------
+  ij = 0
+  DO k=1,nb(3)
+    DO j=1,nb(2)
+      DO i=1,nb(1)
+        ij = ij+1
+        y(ij) = MIN(x(i,j,k),s)
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)*nb(3)
+  ma_fumin_r31 = 0
+!------------------------
+END FUNCTION ma_fumin_r31
+!===
+INTEGER FUNCTION ma_fumax_r31(nb,x,s,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo
+  REAL :: x(nb(1),nb(2),nb(3)),s,y(nbo)
+!-
+  INTEGER :: i,j,k,ij
+!---------------------------------------------------------------------
+  ij = 0
+  DO k=1,nb(3)
+    DO j=1,nb(2)
+      DO i=1,nb(1)
+        ij = ij+1
+        y(ij) = MAX(x(i,j,k),s)
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+  nbo = nb(1)*nb(2)*nb(3)
+  ma_fumax_r31 = 0
+!------------------------
+END FUNCTION ma_fumax_r31
+!===
+INTEGER FUNCTION ma_fuscat_r31(nb,x,nbi,ind,miss_val,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo,nbi
+  INTEGER :: ind(nbi)
+  REAL :: x(nb(1),nb(2),nb(3)),miss_val,y(nbo)
+!-
+  INTEGER :: i,j,k,ij,ii,ipos,ipp,isb
+!---------------------------------------------------------------------
+  ma_fuscat_r31 = 0
+!-
+  y(1:nbo) = miss_val
+!-
+  IF (nbi <= nb(1)*nb(2)*nb(3)) THEN
+    ipos = 0
+    isb = nb(1)*nb(2)
+    DO ij=1,nbi
+      IF (ind(ij) <= nbo .AND. ind(ij) > 0) THEN
+        ipos = ipos+1
+        k = ((ipos-1)/isb)+1
+        ipp = ipos-(k-1)*isb
+        j = ((ipp-1)/nb(1))+1
+        i = (ipp-(j-1)*nb(1))
+        y(ind(ij)) = x(i,j,k)
+      ELSE
+        IF (ind(ij) > nbo) ma_fuscat_r31  = ma_fuscat_r31+1
+      ENDIF
+    ENDDO
+!-- Repeat the data if needed
+    IF (MINVAL(ind) < 0) THEN
+      DO i=1,nbi
+        IF (ind(i) <= 0) THEN
+          DO ii=1,ABS(ind(i))-1
+            IF (ind(i+1)+ii <= nbo) THEN
+              y(ind(i+1)+ii) = y(ind(i+1))
+            ELSE
+              ma_fuscat_r31  = ma_fuscat_r31+1
+            ENDIF
+          ENDDO
+        ENDIF
+      ENDDO
+    ENDIF
+  ELSE
+    ma_fuscat_r31  = 1
+  ENDIF
+!-------------------------
+END FUNCTION ma_fuscat_r31
+!===
+INTEGER FUNCTION ma_fugath_r31(nb,x,nbi,ind,miss_val,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo,nbi
+  INTEGER :: ind(nbi)
+  REAL :: x(nb(1),nb(2),nb(3)),miss_val,y(nbo)
+!-
+  INTEGER :: i,j,k,ij,ipos,ipp,isb
+!---------------------------------------------------------------------
+  IF (nbi <= nbo) THEN
+    ma_fugath_r31 = 0
+    y(1:nbo) = miss_val
+    ipos = 0
+    isb = nb(1)*nb(2)
+    DO ij=1,nbi
+      IF (ipos+1 <= nbo) THEN
+        IF (ind(ij) > 0) THEN
+          k = ((ind(ij)-1)/isb)+1
+          ipp = ind(ij)-(k-1)*isb
+          j = ((ipp-1)/nb(1))+1
+          i = (ipp-(j-1)*nb(1))
+          ipos = ipos+1
+          y(ipos) = x(i,j,k)
+        ENDIF
+      ELSE
+        IF (ipos+1 > nbo) ma_fugath_r31  = ma_fugath_r31+1
+      ENDIF
+    ENDDO
+  ELSE
+    ma_fugath_r31 = 1
+  ENDIF
+  nbo = ipos
+!-------------------------
+END FUNCTION ma_fugath_r31
+!===
+INTEGER FUNCTION ma_fufill_r31(nb,x,nbi,ind,miss_val,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo,nbi
+  INTEGER :: ind(nbi)
+  REAL :: x(nb(1),nb(2),nb(3)),miss_val,y(nbo)
+!-
+  INTEGER :: i,j,k,ij,ii,ipos,ipp,isb
+!---------------------------------------------------------------------
+  ma_fufill_r31 = 0
+  IF (nbi <= nb(1)*nb(2)*nb(3)) THEN
+    ipos = 0
+    isb = nb(1)*nb(2)
+    DO ij=1,nbi
+      IF (ind(ij) <= nbo .AND. ind(ij) > 0) THEN
+        ipos = ipos+1
+        k = ((ipos-1)/isb)+1
+        ipp = ipos-(k-1)*isb
+        j = ((ipp-1)/nb(1))+1
+        i = (ipp-(j-1)*nb(1))
+        y(ind(ij)) = x(i,j,k)
+      ELSE
+        IF (ind(ij) > nbo) ma_fufill_r31  = ma_fufill_r31+1
+      ENDIF
+    ENDDO
+!-- Repeat the data if needed
+    IF (MINVAL(ind) < 0) THEN
+      DO i=1,nbi
+        IF (ind(i) <= 0) THEN
+          DO ii=1,ABS(ind(i))-1
+            IF (ind(i+1)+ii <= nbo) THEN
+              y(ind(i+1)+ii) = y(ind(i+1))
+            ELSE
+              ma_fufill_r31  = ma_fufill_r31+1
+            ENDIF
+          ENDDO
+        ENDIF
+      ENDDO
+    ENDIF
+  ELSE
+    ma_fufill_r31  = 1
+  ENDIF
+!-------------------------
+END FUNCTION ma_fufill_r31
+!===
+INTEGER FUNCTION ma_fucoll_r31(nb,x,nbi,ind,miss_val,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo,nbi
+  INTEGER :: ind(nbi)
+  REAL :: x(nb(1),nb(2),nb(3)),miss_val,y(nbo)
+!-
+  INTEGER :: i,j,k,ij,ipos,ipp,isb
+!---------------------------------------------------------------------
+  IF (nbi <= nbo) THEN
+    ma_fucoll_r31 = 0
+    ipos = 0
+    isb = nb(1)*nb(2)
+    DO ij=1,nbi
+      IF (ipos+1 <= nbo) THEN
+        IF (ind(ij) > 0) THEN
+          k = ((ind(ij)-1)/isb)+1
+          ipp = ind(ij)-(k-1)*isb
+          j = ((ipp-1)/nb(1))+1
+          i = (ipp-(j-1)*nb(1))
+          ipos = ipos+1
+          y(ipos) = x(i,j,k)
+        ENDIF
+      ELSE
+        IF (ipos+1 > nbo) ma_fucoll_r31  = ma_fucoll_r31+1
+      ENDIF
+    ENDDO
+  ELSE
+    ma_fucoll_r31 = 1
+  ENDIF
+  nbo = ipos
+!-------------------------
+END FUNCTION ma_fucoll_r31
+!===
+INTEGER FUNCTION ma_fuundef_r31(nb,x,nbi,ind,miss_val,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo,nbi
+  INTEGER :: ind(nbi)
+  REAL :: x(nb(1),nb(2),nb(3)),miss_val,y(nbo)
+!-
+  INTEGER :: i,j,k,ij,ipp,isb
+!---------------------------------------------------------------------
+  IF (nbi <= nbo .AND. nbo == nb(1)*nb(2)*nb(3)) THEN
+    ma_fuundef_r31 = 0
+    isb = nb(1)*nb(2)
+    DO ij=1,nbo
+      k = ((ij-1)/isb)+1
+      ipp = ij-(k-1)*isb
+      j = ((ipp-1)/nb(1))+1
+      i = (ipp-(j-1)*nb(1))
+      y(ij) = x(i,j,k)
+    ENDDO
+    DO i=1,nbi
+      IF (ind(i) <= nbo .AND. ind(i) > 0) THEN
+        y(ind(i)) =  miss_val
+      ELSE
+        IF (ind(i) > nbo) ma_fuundef_r31  = ma_fuundef_r31+1
+      ENDIF
+    ENDDO
+  ELSE
+    ma_fuundef_r31 = 1
+  ENDIF
+!--------------------------
+END FUNCTION ma_fuundef_r31
+!===
+INTEGER FUNCTION ma_fuonly_r31(nb,x,nbi,ind,miss_val,nbo,y)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: nb(3),nbo,nbi
+  INTEGER :: ind(nbi)
+  REAL :: x(nb(1),nb(2),nb(3)),miss_val,y(nbo)
+!-
+  INTEGER :: i,j,k,ij,ipp,isb
+!---------------------------------------------------------------------
+  IF (     (nbi <= nbo).AND.(nbo == nb(1)*nb(2)*nb(3)) &
+ &    .AND.ALL(ind(1:nbi) <= nbo) ) THEN
+    ma_fuonly_r31 = 0
+    y(1:nbo) = miss_val
+    isb = nb(1)*nb(2)
+    DO ij=1,nbi
+      IF (ind(ij) > 0) THEN
+        k = ((ind(ij)-1)/isb)+1
+        ipp = ind(ij)-(k-1)*isb
+        j = ((ipp-1)/nb(1))+1
+        i = (ipp-(j-1)*nb(1))
+        y(ind(ij)) = x(i,j,k)
+      ENDIF
+    ENDDO
+  ELSE
+    ma_fuonly_r31 = 1
+  ENDIF
+!-------------------------
+END FUNCTION ma_fuonly_r31
+!===
+SUBROUTINE moycum (opp,np,px,py,pwx)
+!---------------------------------------------------------------------
+!- Does time operations
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=7) :: opp
+  INTEGER :: np
+  REAL,DIMENSION(:) :: px,py
+  INTEGER :: pwx
+!---------------------------------------------------------------------
+  IF (pwx /= 0) THEN
+    IF      (opp == 'ave') THEN
+      px(1:np)=(px(1:np)*pwx+py(1:np))/REAL(pwx+1)
+    ELSE IF (opp == 't_sum') THEN
+      px(1:np)=px(1:np)+py(1:np)
+    ELSE IF ( (opp == 'l_min').OR.(opp == 't_min') ) THEN
+      px(1:np)=MIN(px(1:np),py(1:np))
+    ELSE IF ( (opp == 'l_max').OR.(opp == 't_max') ) THEN
+      px(1:np)=MAX(px(1:np),py(1:np))
+    ELSE
+      CALL ipslerr(3,"moycum",'Unknown time operation',opp,' ')
+    ENDIF
+  ELSE
+    IF      (opp == 'l_min') THEN
+      px(1:np)=MIN(px(1:np),py(1:np))
+    ELSE IF (opp == 'l_max') THEN
+      px(1:np)=MAX(px(1:np),py(1:np))
+    ELSE
+      px(1:np)=py(1:np)
+    ENDIF
+  ENDIF
+!--------------------
+END SUBROUTINE moycum
+!===
+SUBROUTINE moycum_index( opp, px, py, pwx, nbi, ind )
+!---------------------------------------------------------------------
+!- Does time operations on index points 
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+
+  !! 0. Parameters and variables declaration
+
+  !! 0.1 Input variables
+
+  CHARACTER(LEN=7), INTENT(in)        :: opp  !! Operation performed
+  INTEGER, INTENT(in)                 :: nbi  !! Size of index vector
+  INTEGER, DIMENSION(nbi), INTENT(in) :: ind  !! Index vector
+  REAL, DIMENSION(:), INTENT(in)      :: py   !! Vector containing the 
+                                              !! previous values of px
+                                              !! Warning : due to memory 
+                                              !! optimization, we have
+                                              !! generally SIZE(px) /= SIZE(py)  
+  INTEGER, INTENT(in)                 :: pwx  !! Used to calculate average value                             
+
+  !! 0.3 Modified variables
+
+  REAL, DIMENSION(:), INTENT(inout)   :: px   !! Result
+
+  !! 0.4 Local variables
+
+  INTEGER :: ig                               !! Index
+
+!---------------------------------------------------------------------
+
+  !! Perform operations only if the values of ind don't exceed the size of px 
+
+  IF ( MAXVAL(ind) > SIZE(px) ) THEN
+     CALL ipslerr(3,"moycum_index", &
+          & "the index vector is out of range for px", &
+          & "Indexation vector problem. We stop", " " )
+  END IF
+
+  IF (pwx /= 0) THEN
+     IF      (opp == 'ave') THEN
+        DO ig = 1,nbi
+           px(ind(ig)) = (px(ind(ig))*pwx + py(ind(ig)))/REAL(pwx+1)
+        END DO
+     ELSE IF (opp == 't_sum') THEN
+        DO ig = 1,nbi
+           px(ind(ig)) = px(ind(ig)) + py(ind(ig))
+        END DO
+     ELSE IF ( (opp == 'l_min').OR.(opp == 't_min') ) THEN
+        DO ig = 1,nbi
+           px(ind(ig)) = MIN(px(ind(ig)),py(ind(ig)))
+        END DO
+     ELSE IF ( (opp == 'l_max').OR.(opp == 't_max') ) THEN
+        DO ig = 1,nbi
+           px(ind(ig)) = MAX(px(ind(ig)),py(ind(ig)))
+        END DO
+     ELSE
+        CALL ipslerr(3,"moycum_index",'Unknown time operation',opp,' ')
+     END IF
+  ELSE
+    IF      (opp == 'l_min') THEN
+       DO ig = 1,nbi
+          px(ind(ig)) = MIN(px(ind(ig)),py(ind(ig)))
+       END DO
+    ELSE IF (opp == 'l_max') THEN
+       DO ig = 1,nbi
+          px(ind(ig)) = MAX(px(ind(ig)),py(ind(ig)))
+       END DO
+    ELSE
+       DO ig = 1,nbi
+          px(ind(ig)) = py(ind(ig))
+       END DO
+    ENDIF
+ END IF
+
+END SUBROUTINE moycum_index
+
+
+!-----------------
+END MODULE mathelp
Index: /IOIPSL/tags/v2_2_5/src/restcom.f90
===================================================================
--- /IOIPSL/tags/v2_2_5/src/restcom.f90	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/src/restcom.f90	(revision 5058)
@@ -0,0 +1,3154 @@
+MODULE restcom
+!-
+!$Id$
+!-
+! This software is governed by the CeCILL license
+! See IOIPSL/IOIPSL_License_CeCILL.txt
+!-
+USE netcdf
+!-
+USE defprec
+USE errioipsl, ONLY : ipslerr,ipsldbg, ipslout
+USE stringop
+USE calendar
+USE mathelp
+USE fliocom,   ONLY : flio_dom_file,flio_dom_att
+!-
+IMPLICIT NONE
+!-
+PRIVATE
+!-
+PUBLIC :: &
+ &  restini, restget, restput, restclo, &
+ &  ioconf_setatt, ioget_vname, ioconf_expval, &
+ &  ioget_expval, ioget_vdim
+!-
+INTERFACE restput
+  MODULE PROCEDURE &
+ &  restput_r3d, restput_r2d, restput_r1d, &
+ &  restput_opp_r2d, restput_opp_r1d, restput_opp_r3d, &
+ &  restput_opp_r4d, restput_opp_r5d
+END INTERFACE
+!-
+INTERFACE restget
+  MODULE PROCEDURE &
+ &  restget_r3d,restget_r2d,restget_r1d, &
+ &  restget_opp_r2d, restget_opp_r1d, restget_opp_r3d, &
+ &  restget_opp_r4d, restget_opp_r5d
+END INTERFACE
+!-
+! We do not use allocatable arrays because these sizes are safe
+! and we do not know from start how many variables will be in
+! the out file.
+!-
+  INTEGER,PARAMETER :: &
+ &  max_var=1500, max_file=50, MAX_DIM=NF90_MAX_VAR_DIMS, RESTART_MAX_DIMS=7 !
+! RESTART_MAX_DIMS = Max dimensions in 1 variable IOIPSL can manage for restart files (> 3)
+!-
+  CHARACTER(LEN=9),SAVE :: calend_str='unknown'
+!-
+! The IDs of the netCDF files are going in pairs.
+! The input one (netcdf_id(?,1)) and the output one (netcdf_id(?,2))
+!-
+  INTEGER,SAVE :: nb_fi = 0
+  INTEGER,DIMENSION(max_file,2),SAVE :: netcdf_id = -1
+  CHARACTER(LEN=120),DIMENSION(max_file,2),SAVE :: netcdf_name='NONE'
+!-
+! Description of the content of the 'in' files and the 'out' files.
+!   Number of variables   : nbvar_*
+!   Number of dimensions  : nbdim_*
+!   ID of the time axis   : tdimid_*
+!-
+  INTEGER,SAVE :: nbvar_in(max_file), nbvar_out(max_file)
+  INTEGER,SAVE :: tdimid_in(max_file), tdimid_out(max_file)
+!-
+! Variables for one or the other file
+!-
+! Number of dimensions in the input file               : nbdim_in
+! Number of variables read so far from the input file  : nbvar_read
+! Type of variable read from the input file            : vartyp_in
+!   (Could be used later to test if we have a restart file)
+!-
+  INTEGER,SAVE :: nbdim_in(max_file), nbvar_read(max_file)
+  INTEGER,SAVE :: vartyp_in(max_file, max_var)
+!-
+! Time step and time origine in the input file.
+!-
+  REAL,DIMENSION(max_file),SAVE :: deltat,timeorig
+!-
+! Description of the axes in the output file
+!-
+!   tstp_out  : Index on the tie axis currently beeing written
+!   itau_out  : Time step which is written on this index of the file
+!-
+  INTEGER,DIMENSION(max_file),SAVE :: tstp_out,itau_out
+!-
+! Description of the axes in the output file
+!-
+! For the ?ax_infs variable the following order is used :
+!   ?ax_infs (if,in,1) = size of axis
+!   ?ax_infs (if,in,2) = id of dimension
+! Number of x,y and z axes in the output file :
+!   ?ax_nb(if)
+!-
+  INTEGER,DIMENSION(max_file,RESTART_MAX_DIMS,max_dim,2),SAVE :: &
+ &  ax_infs!,yax_infs,zax_infs,wax_infs
+  INTEGER,DIMENSION(max_file,RESTART_MAX_DIMS),SAVE :: &
+ &  ax_nb=0! ,yax_nb=0,zax_nb=0,wax_nb=0
+!-
+! Dimensions names, must be equal to RESTART_MAX_DIMS:
+! - DO not repeat any char
+! - ONLY 1 single char per dimension
+  CHARACTER, DIMENSION(RESTART_MAX_DIMS), PARAMETER :: RESTART_DIMS_NAMES = &
+     (/ 'x','y','z','l','m','n', 'o'/)
+
+!- Dimensions constants for basic dimensions
+  INTEGER, PARAMETER :: RESTART_DIM_X = 1
+  INTEGER, PARAMETER :: RESTART_DIM_Y = 2
+  INTEGER, PARAMETER :: RESTART_DIM_Z = 3
+!-
+! Description of the time axes in the input and output files
+!-
+!   ID of the variable which contains the itaus :
+!     tind_varid_*
+!   ID of the variables which contains the seconds since date :
+!     tax_varid_*
+!   Size of the time axis in the input file :
+!     tax_size_in
+!-
+  INTEGER,SAVE :: tind_varid_in(max_file),  tax_varid_in(max_file), &
+ &                tind_varid_out(max_file), tax_varid_out(max_file)
+  INTEGER,SAVE :: tax_size_in(max_file)=1
+!-
+! The two time axes we have in the input file :
+!   t_index   : dates in itaus
+!               (thus the variable has a tstep_sec attribute)
+!   t_julian  : Julian days of the time axis
+!-
+  INTEGER,SAVE,ALLOCATABLE :: t_index(:,:)
+  REAL,SAVE,ALLOCATABLE :: t_julian(:,:)
+!-
+! Here we save a number of informations on the variables
+! in the files we are handling
+!-
+! Name of variables :                                    varname_*
+! ID of the variables :                                  varid_*
+! Number of dimensions of the variable :                 varnbdim_*
+! Dimensions which are used for the variable :           vardims_*
+! Number of attributes for a variables :                 varatt_*
+! A flag which markes the variables we have worked on :  touched_*
+!-
+  INTEGER, PARAMETER :: clen = 100
+  CHARACTER(LEN=clen),DIMENSION(max_file,max_var),SAVE :: &
+ &  varname_in,varname_out
+  INTEGER,DIMENSION(max_file,max_var),SAVE :: &
+ &  varid_in,varid_out,varnbdim_in,varatt_in
+  INTEGER,DIMENSION(max_file,max_var,max_dim),SAVE :: &
+ &  vardims_in
+  LOGICAL,DIMENSION(max_file,max_var),SAVE :: &
+ &  touched_in,touched_out
+!-
+  CHARACTER(LEN=120),SAVE :: indchfun= 'scatter, fill, gather, coll'
+  REAL,PARAMETER :: missing_val=1.e20
+!                or HUGE(1.0) (maximum real number)
+!-
+! The default value we will use for variables
+! which are not present in the restart file
+!-
+  REAL,SAVE :: val_exp =  999999.
+  LOGICAL,SAVE :: lock_valexp = .FALSE.
+!-
+! Temporary variables in which we store the attributed which are going
+! to be given to a new variable which is going to be defined.
+!-
+  CHARACTER(LEN=80),SAVE :: rest_units='XXXXX',rest_lname='XXXXX'
+!-
+! For allocations
+!-
+  REAL,ALLOCATABLE,DIMENSION(:),SAVE :: buff_tmp1,buff_tmp2
+!-
+! Compression
+!-
+  LOGICAL,SAVE :: IS_COMPRESSION_ENABLED = .FALSE.
+!-
+!===
+CONTAINS
+!===
+!-
+SUBROUTINE restini &
+ & (fnamein,iim,jjm,lon,lat,llm,lev, &
+ &  fnameout,itau,date0,dt,fid,owrite_time_in, &
+ &  domain_id,use_compression)
+!---------------------------------------------------------------------
+!- This subroutine sets up all the restart process.
+!- It will call the subroutine which opens the input
+!- and output files.
+!- The time step (itau), date of origine (date0) and time step are
+!- READ from the input file.
+!- A file ID, which is common to the input and output file is returned
+!-
+!- If fnamein = fnameout then the same file is used for the reading
+!- the restart conditions and writing the new restart.
+!-
+!- A special mode can be switched in with filename='NONE'.
+!- This means that no restart file is present.
+!- Usefull for creating the first restart file
+!- or to get elements in a file without creating an output file.
+!-
+!- A mode needs to be written in which itau, date0 and dt
+!- are given to the restart process and thus
+!- written into the output restart file.
+!-
+!- INPUT
+!-
+!- fnamein  : name of the file for the restart
+!- iim      : Dimension in x
+!- jjm      : Dimension in y
+!- lon      : Longitude in the x,y domain
+!- lat      : Latitude in the x,y domain
+!- llm      : Dimension in the vertical
+!- lev      : Positions of the levels
+!- fnameout :
+!-
+!- OUTPUT
+!-
+!- itau     : Time step of the restart file and at which the model
+!-            should restart
+!- date0    : Time at which itau = 0
+!- dt       : time step in seconds between two succesiv itaus
+!- fid      : File identification of the restart file
+!-
+!- Optional INPUT arguments
+!-
+!- owrite_time_in  : logical  argument which allows to
+!-                   overwrite the time in the restart file
+!- use_compression : Enable netcdf compression
+!- domain_id       : Domain identifier
+!-
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*),INTENT(IN) :: fnamein,fnameout
+  INTEGER :: iim,jjm,llm,fid,itau
+  REAL :: lon(iim,jjm),lat(iim,jjm),lev(llm)
+  REAL :: date0,dt
+  LOGICAL,OPTIONAL :: owrite_time_in
+  INTEGER,INTENT(IN),OPTIONAL :: domain_id
+  LOGICAL,OPTIONAL,INTENT(IN) :: use_compression
+!-
+  INTEGER :: ncfid
+  REAL :: dt_tmp,date0_tmp
+  LOGICAL :: l_fi,l_fo,l_rw
+  LOGICAL :: overwrite_time
+  CHARACTER(LEN=120) :: fname
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+! 0.0 Prepare the configuration before opening any files
+!-
+  IF (.NOT.PRESENT(owrite_time_in)) THEN
+    overwrite_time = .FALSE.
+  ELSE
+    overwrite_time = owrite_time_in
+  ENDIF
+!-
+  IS_COMPRESSION_ENABLED = .FALSE. 
+  IF (PRESENT(use_compression) .AND. use_compression) THEN
+    IS_COMPRESSION_ENABLED = .TRUE.
+  ENDIF
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) 'restini 0.0 : ',TRIM(fnamein),' , ',TRIM(fnameout)
+  ENDIF
+!-
+  nb_fi = nb_fi+1
+!-
+  IF (nb_fi > max_file) THEN
+    CALL ipslerr (3,'restini',&
+ &   'Too many restart files are used. The problem can be',&
+ &   'solved by increasing max_file in restcom.f90 ',&
+ &   'and recompiling ioipsl.')
+  ENDIF
+!-
+  l_fi = (TRIM(fnamein)  /= 'NONE')
+  l_fo = (TRIM(fnameout) /= 'NONE')
+  IF ((.NOT.l_fi).AND.(.NOT.l_fo)) THEN
+    CALL ipslerr (3,'restini',&
+ &   'Input and output file names are both to NONE.',&
+ &   'It is probably an error.','Verify your logic.')
+  ENDIF
+  l_rw = l_fi.AND.l_fo.AND.(TRIM(fnamein) == TRIM(fnameout))
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) 'restini 0.1 l_fi, l_fo, l_rw : ',l_fi,l_fo,l_rw
+  ENDIF
+!-
+! 1.0 Open the input file.
+!-
+  IF (l_fi) THEN
+!---
+    IF (l_dbg) WRITE(ipslout,*) 'restini 1.0 : Open input file'
+!-- Add DOMAIN number and ".nc" suffix in file names if needed
+    fname = fnamein
+    CALL flio_dom_file (fname,domain_id)
+!-- Open the file
+    CALL restopenin (nb_fi,fname,l_rw,iim,jjm,lon,lat,llm,lev,ncfid)
+    netcdf_id(nb_fi,1) = ncfid
+    netcdf_name(nb_fi,1) = TRIM(fnamein)
+!---
+!-- 1.3 Extract the time information
+!---
+    IF (overwrite_time) THEN
+       date0_tmp = date0
+    ENDIF
+    CALL restsett (dt_tmp,date0_tmp,itau,overwrite_time)
+    IF (.NOT.overwrite_time) THEN
+      dt = dt_tmp
+      date0 = date0_tmp
+    ENDIF
+!---
+  ELSE
+!---
+!-- 2.0 The case of a missing restart file is dealt with
+!---
+    IF (l_dbg) WRITE(ipslout,*) 'restini 2.0'
+!---
+    IF (     (ALL(MINLOC(lon(:iim,:jjm)) == MAXLOC(lon(:iim,:jjm)))) &
+        .AND.(iim > 1) ) THEN
+      CALL ipslerr (3,'restini',&
+        & 'For creating a restart file the longitudes of the',&
+        & 'grid need to be provided to restini. This ',&
+        & 'information is needed for the restart files')
+    ENDIF
+    IF (     (ALL(MINLOC(lat(:iim,:jjm)) == MAXLOC(lat(:iim,:jjm)))) &
+        .AND.(jjm > 1) ) THEN
+      CALL ipslerr (3,'restini',&
+        & 'For creating a restart file the latitudes of the',&
+        & 'grid need to be provided to restini. This ',&
+        & 'information is needed for the restart files')
+    ENDIF
+    IF (     (ALL(MINLOC(lev(:llm)) == MAXLOC(lev(:llm)))) &
+        .AND.(llm > 1) ) THEN
+      CALL ipslerr (3,'restini',&
+        & 'For creating a restart file the levels of the',&
+        & 'grid need to be provided to restini. This',&
+        & 'information is needed for the restart files')
+    ENDIF
+!---
+!-- 2.2 Allocate the time axes and write the inputed variables
+!---
+    tax_size_in(nb_fi) = 1
+    CALL rest_atim (l_dbg,'restini')
+    t_index(nb_fi,1) = itau
+    t_julian(nb_fi,1) = date0
+  ENDIF
+!-
+  IF (l_fo.AND.(.NOT.l_rw)) THEN
+!-- Add DOMAIN number and ".nc" suffix in file names if needed
+    fname = fnameout
+    CALL flio_dom_file (fname,domain_id)
+!-- Open the file
+    CALL restopenout &
+      (nb_fi,fname,iim,jjm,lon,lat,llm,lev,dt,date0,ncfid,domain_id)
+    netcdf_id(nb_fi,2) = ncfid
+    netcdf_name(nb_fi,2) = TRIM(fnameout)
+  ELSE IF (l_fi.AND.l_fo) THEN
+    netcdf_id(nb_fi,2) = netcdf_id(nb_fi,1)
+    netcdf_name(nb_fi,2) = netcdf_name(nb_fi,1)
+    varname_out(nb_fi,:) = varname_in(nb_fi,:)
+    nbvar_out(nb_fi) = nbvar_in(nb_fi)
+    tind_varid_out(nb_fi) = tind_varid_in(nb_fi)
+    tax_varid_out(nb_fi) = tax_varid_in(nb_fi)
+    varid_out(nb_fi,:) = varid_in(nb_fi,:)
+    touched_out(nb_fi,:) = .TRUE.
+  ENDIF
+!-
+! 2.3 Set the calendar for the run.
+!     This should not produce any error message if
+!     This does not mean any change in calendar
+!     (to be modified in ioconf_calendar)
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) 'restini 2.3 : Configure calendar if needed : ', &
+                calend_str
+  ENDIF
+!-
+  IF (INDEX(calend_str,'unknown') < 1) THEN
+    CALL ioconf_calendar (calend_str)
+    IF (l_dbg) THEN
+      WRITE(ipslout,*) 'restini 2.3b : new calendar : ',calend_str
+    ENDIF
+  ENDIF
+!-
+! Save some data in the module
+!-
+  deltat(nb_fi) = dt
+!-
+! Prepare the variables which will be returned
+!-
+  fid = nb_fi
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) 'SIZE of t_index :',SIZE(t_index), &
+               SIZE(t_index,dim=1),SIZE(t_index,dim=2)
+    WRITE(ipslout,*) 't_index = ',t_index(fid,:)
+  ENDIF
+  itau = t_index(fid,1)
+!-
+  IF (l_dbg) WRITE(ipslout,*) 'restini END'
+!---------------------
+END SUBROUTINE restini
+!===
+SUBROUTINE restopenin &
+  (fid,fname,l_rw,iim,jjm,lon,lat,llm,lev,ncfid)
+!---------------------------------------------------------------------
+!- Opens the restart file and checks that it belongsd to the model.
+!- This means that the coordinates of the model are compared to the
+!- ones in the file.
+!-
+!- The number and name of variable in the file are exctracted. Also
+!- the time details.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: fid,iim,jjm,llm
+  CHARACTER(LEN=*),INTENT(IN) :: fname
+  REAL :: lon(iim,jjm),lat(iim,jjm),lev(llm)
+  LOGICAL,INTENT(IN) :: l_rw
+  INTEGER,INTENT(OUT) :: ncfid
+!-
+  INTEGER,DIMENSION(max_dim) :: var_dims,dimlen
+  INTEGER :: nb_dim,nb_var,id_unl,id,iv
+  INTEGER :: iread,jread,lread,iret
+  INTEGER :: lon_vid,lat_vid
+  REAL :: lon_read(iim,jjm),lat_read(iim,jjm)
+  REAL :: lev_read(llm)
+  REAL :: mdlon,mdlat
+  CHARACTER(LEN=80) :: units
+  CHARACTER(LEN=NF90_max_name),DIMENSION(max_dim) :: dimname
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+! If we reuse the same file for input and output
+! then we open it in write mode
+!-
+  IF (l_rw) THEN; id = IOR(NF90_WRITE,NF90_NETCDF4); ELSE; id = IOR(NF90_NOWRITE,NF90_NETCDF4); ENDIF
+  iret = NF90_OPEN(fname,id,ncfid)
+  IF (iret /= NF90_NOERR) THEN
+    CALL ipslerr (3,'restopenin','Could not open file :',fname,' ')
+  ENDIF
+!-
+  IF (l_dbg) WRITE (*,*) "restopenin 0.0 ",TRIM(fname)
+  iret = NF90_INQUIRE(ncfid,nDimensions=nb_dim, &
+ &         nVariables=nb_var,unlimitedDimId=id_unl)
+  tdimid_in(fid) = id_unl
+!-
+  IF (nb_dim > max_dim) THEN
+    CALL ipslerr (3,'restopenin',&
+      & 'More dimensions present in file that can be store',&
+      & 'Please increase max_dim in the global variables ',&
+      & 'in restcom.F90')
+  ENDIF
+  IF (nb_var > max_var) THEN
+    CALL ipslerr (3,'restopenin',&
+      & 'More variables present in file that can be store',&
+      & 'Please increase max_var in the global variables ',&
+      & 'in restcom.F90')
+  ENDIF
+!-
+  nbvar_in(fid) = nb_var
+  nbdim_in(fid) = nb_dim
+  iread = -1; jread = -1; lread = -1;
+  DO id=1,nb_dim
+    iret = NF90_INQUIRE_DIMENSION(ncfid,id, &
+ &           len=dimlen(id),name=dimname(id))
+    IF (l_dbg) THEN
+      WRITE (*,*) "restopenin 0.0 dimname",id,TRIM(dimname(id))
+    ENDIF
+    IF      (TRIM(dimname(id)) == RESTART_DIMS_NAMES(RESTART_DIM_X)) THEN
+      iread = dimlen(id)
+      IF (l_dbg) WRITE (*,*) "iread",iread
+    ELSE IF (TRIM(dimname(id)) == RESTART_DIMS_NAMES(RESTART_DIM_Y)) THEN
+      jread = dimlen(id)
+      IF (l_dbg) WRITE (*,*) "jread",jread
+    ELSE IF (TRIM(dimname(id)) == RESTART_DIMS_NAMES(RESTART_DIM_Z)) THEN
+      lread = dimlen(id)
+      IF (l_dbg) WRITE (*,*) "lread",lread
+    ENDIF
+  ENDDO
+!-
+  IF (id_unl > 0) THEN
+!---
+!-- 0.1 If we are going to add values to this file
+!--     we need to know where it ends
+!--     We also need to have all the dimensions in the file
+!---
+    IF (l_rw) THEN
+      tstp_out(fid) = dimlen(id_unl)
+      itau_out(fid) = -1
+      tdimid_out(fid) =  tdimid_in(fid)
+      IF (l_dbg) THEN
+        WRITE (*,*) &
+ &       "restopenin 0.0 unlimited axis dimname", &
+ &       dimname(id_unl),tstp_out(fid)
+      ENDIF
+!-----
+      ax_nb(fid,:) = 0
+!-----
+      DO id=1,nb_dim
+        ! Order is important
+        IF      (dimname(id)(1:1) == RESTART_DIMS_NAMES(id)) THEN
+          ax_nb(fid,id) = ax_nb(fid,id)+1
+          ax_infs(fid,id,ax_nb(fid,id),1) = dimlen(id)
+          ax_infs(fid,id,ax_nb(fid,id),2) = id
+        ENDIF
+      ENDDO
+!-----
+!---  Initialize non basic dimensions
+!-----
+      DO id=4,RESTART_MAX_DIMS
+        ax_nb(fid,id) = 1
+      ENDDO
+    ENDIF
+  ELSE
+!---
+!-- Still need to find a method for dealing with this
+!---
+!  CALL ipslerr (3,'restopenin',&
+!    & ' We do not deal yet with files without time axis.',' ',' ')
+  ENDIF
+!-
+! 1.0 First let us check that we have the righ restart file
+!-
+  IF ((iread /= iim).OR.(jread /= jjm).OR.(lread /= llm)) THEN
+    CALL ipslerr (3,'restopenin',&
+ &    'The grid of the restart file does not correspond',&
+ &    'to that of the model',' ')
+  ENDIF
+!-
+! 2.0 Get the list of variables
+!-
+  IF (l_dbg) WRITE(ipslout,*) 'restopenin 1.2'
+!-
+  lat_vid = -1
+  lon_vid = -1
+  tind_varid_in(fid) = -1
+  tax_varid_in(fid) = -1
+!-
+  DO iv=1,nb_var
+!---
+    varid_in(fid,iv) = iv
+    var_dims(:) = 0
+    iret = NF90_INQUIRE_VARIABLE(ncfid,iv, &
+ &           name=varname_in(fid,iv),xtype=vartyp_in(fid,iv), &
+ &           ndims=varnbdim_in(fid,iv),dimids=var_dims, &
+ &           nAtts=varatt_in(fid,iv))
+!---
+    DO id=1,varnbdim_in(fid,iv)
+      iret = NF90_INQUIRE_DIMENSION &
+ &             (ncfid,var_dims(id),len=vardims_in(fid,iv,id))
+    ENDDO
+!---
+!-- 2.1 Read the units of the variable
+!---
+    units=''
+    iret = NF90_GET_ATT(ncfid,iv,'units',units)
+    CALL strlowercase (units)
+    CALL cmpblank (units)
+!---
+!-- 2.2 Catch the time variables
+!---
+    IF (varnbdim_in(fid,iv) == 1) THEN
+      IF (     (INDEX(units,'timesteps since') > 0) &
+          .AND.(tind_varid_in(fid) < 0) ) THEN
+        tind_varid_in(fid) = iv
+        tax_size_in(fid) = vardims_in(fid,iv,1)
+      ENDIF
+      IF (     (INDEX(units,'seconds since') > 0) &
+          .AND.(tax_varid_in(fid) < 0) ) THEN
+        tax_varid_in(fid) = iv
+        tax_size_in(fid) = vardims_in(fid,iv,1)
+      ENDIF
+    ENDIF
+!---
+!-- 2.3 Catch longitude and latitude variables
+!---
+    IF      (INDEX(units,'degrees_nort') > 0) THEN
+      lat_vid = iv
+    ELSE IF (INDEX(units,'degrees_east') > 0) THEN
+      lon_vid = iv
+    ENDIF
+!---
+  ENDDO
+!-
+! 2.4 None of the variables was yet read
+!-
+  nbvar_read(fid) = 0
+  touched_in(fid,:) = .FALSE.
+!-
+! 3.0 Reading the coordinates from the input restart file
+!-
+  lon_read = missing_val
+  lat_read = missing_val
+!-
+  IF (lon_vid < 0 .OR. lat_vid < 0) THEN
+    CALL ipslerr (3,'restopenin',&
+      & ' No variables containing longitude or latitude were ',&
+      & ' found in the restart file.',' ')
+  ELSE
+    iret = NF90_GET_VAR(ncfid,lon_vid,lon_read)
+    iret = NF90_GET_VAR(ncfid,lat_vid,lat_read)
+!---
+    IF (  (ABS( MAXVAL(lon(:,:)) &
+ &             -MINVAL(lon(:,:))) < EPSILON(MAXVAL(lon(:,:)))) &
+ &   .AND.(ABS( MAXVAL(lat(:,:)) &
+ &             -MINVAL(lat(:,:))) < EPSILON(MAXVAL(lat(:,:)))) ) THEN
+!-----
+!---- 3.1 No longitude nor latitude are provided thus
+!---- they are taken from the restart file
+!-----
+      lon(:,:) = lon_read(:,:)
+      lat(:,:) = lat_read(:,:)
+    ELSE
+!-----
+!---- 3.2 We check that the longitudes and latitudes
+!----     in the file and the model are the same
+!-----
+      mdlon = MAXVAL(ABS(lon_read-lon))
+      mdlat = MAXVAL(ABS(lat_read-lat))
+!-----
+!---- We can not test against epsilon here as the longitude
+!---- can be stored at another precision in the netCDF file.
+!---- The test here does not need to be very precise.
+!-----
+      IF (mdlon > 1.e-4 .OR. mdlat > 1.e-4) THEN
+        CALL ipslerr (3,'restopenin',&
+          & ' The longitude or latitude found in the restart ',&
+          & ' file are not the same as the ones used in the model.',&
+          & ' ')
+      ENDIF
+    ENDIF
+  ENDIF
+!------------------------
+END SUBROUTINE restopenin
+!===
+SUBROUTINE restsett (timestep,date0,itau,owrite_time_in)
+!---------------------------------------------------------------------
+!- Here we get all the time information from the file.
+!-
+!- The time information can come in three forms :
+!- -global attributes which give the time origine and the
+!-  time step is taken from the input to restinit
+!- -A physical time exists and thus the julian date from the
+!-  input is used for positioning using the itau as input
+!- -A time-step axis exists and itau is positioned on it.
+!-
+!- What takes precedence : the model
+!-
+!- itau     : Time step of the model
+!-
+!- Optional INPUT arguments
+!-
+!- owrite_time_in : logical  argument which allows to
+!-                  overwrite the time in the restart file
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  REAL :: date0,timestep
+  INTEGER :: itau
+  LOGICAL,OPTIONAL :: owrite_time_in
+!-
+  INTEGER :: ncfid,iret,it,iax,iv
+  CHARACTER(LEN=80) :: itau_orig,tax_orig,calendar
+  CHARACTER(LEN=9) :: tmp_cal
+  INTEGER :: year0,month0,day0,hours0,minutes0,seci
+  REAL :: sec0,one_day,one_year,date0_ju,ttmp
+  CHARACTER :: strc
+  LOGICAL :: ow_time
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (PRESENT(owrite_time_in)) THEN
+    ow_time = owrite_time_in
+  ELSE
+    ow_time = .FALSE.
+  ENDIF
+!-
+  ncfid = netcdf_id(nb_fi,1)
+!-
+! Allocate the space we need for the time axes
+!-
+  CALL rest_atim (l_dbg,'restsett')
+!-
+! Get the calendar if possible. Else it will be gregorian.
+!-
+  IF (tax_size_in(nb_fi) > 0) THEN
+    calendar = ' '
+    iret = NF90_GET_ATT(ncfid,tax_varid_in(nb_fi),'calendar',calendar)
+    IF (iret == NF90_NOERR) THEN
+      CALL ioconf_calendar (calendar)
+      IF (l_dbg) THEN
+        WRITE(ipslout,*) 'restsett : calendar of the restart ',calendar
+      ENDIF
+    ENDIF
+  ENDIF
+  CALL ioget_calendar (one_year,one_day)
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) 'one_year,one_day = ',one_year,one_day
+  ENDIF
+!-
+  itau_orig = 'XXXXX'
+  tax_orig  = 'XXXXX'
+!-
+! Get the time steps of the time axis if available on the restart file
+!-
+  IF (tind_varid_in(nb_fi) > 0) THEN
+    IF (ow_time) THEN
+      t_index(nb_fi,:) = itau
+      IF (l_dbg) THEN
+        WRITE(ipslout,*) "nb_fi,t_index",nb_fi,t_index(nb_fi,:)
+      ENDIF
+      CALL ju2ymds (date0,year0,month0,day0,sec0)
+      hours0 = NINT(sec0/3600)
+      sec0 = sec0 - 3600 * hours0
+      minutes0 = NINT(sec0 / 60)
+      sec0 = sec0 - 60 * minutes0
+      seci = NINT(sec0)
+      strc=':'
+      IF (l_dbg) THEN
+        WRITE(ipslout,*) date0
+        WRITE (UNIT=itau_orig,FMT='(I4.4,5(A,I2.2))') &
+ &       year0,'-',month0,'-',day0,' ',hours0,':',minutes0,':',seci
+        WRITE(ipslout,*) "itau_orig : ",itau_orig
+      ENDIF
+    ELSE
+      iret = NF90_GET_VAR(ncfid,tind_varid_in(nb_fi),t_index(nb_fi,:))
+      IF (l_dbg) THEN
+        WRITE(ipslout,*) "restsett, time axis : ",t_index(nb_fi,:)
+      ENDIF
+      iret = NF90_GET_ATT(ncfid,tind_varid_in(nb_fi),'units',itau_orig)
+      itau_orig = &
+ &      itau_orig(INDEX(itau_orig,'since')+6:LEN_TRIM(itau_orig))
+      iret = &
+ &      NF90_GET_ATT(ncfid,tind_varid_in(nb_fi),'tstep_sec',timestep)
+!-----
+!---- This time origin will dominate as it is linked to the time steps.
+!-----
+      READ (UNIT=itau_orig,FMT='(I4.4,5(A,I2.2))') &
+ &      year0,strc,month0,strc,day0,strc, &
+ &      hours0,strc,minutes0,strc,seci
+      sec0 = REAL(seci)
+      sec0 = hours0*3600.+minutes0*60.+sec0
+      CALL ymds2ju (year0,month0,day0,sec0,date0)
+    ENDIF
+  ENDIF
+!-
+! If a julian day time axis is available then we get it
+!-
+  IF (tax_varid_in(nb_fi) > 0) THEN
+    iret = NF90_GET_VAR(ncfid,tax_varid_in(nb_fi),t_julian(nb_fi,:))
+    iret = NF90_GET_ATT(ncfid,tax_varid_in(nb_fi),'units',tax_orig)
+    tax_orig = tax_orig(INDEX(tax_orig,'since')+6:LEN_TRIM(tax_orig))
+    tmp_cal = ' '
+    iret = NF90_GET_ATT(ncfid,tax_varid_in(nb_fi),'calendar',tmp_cal)
+    IF (l_dbg) THEN
+      WRITE(ipslout,*) 'restsett : tmp_calendar of the restart ',tmp_cal
+    ENDIF
+!---
+    CALL strlowercase (tmp_cal)
+    IF (INDEX(calend_str,tmp_cal) < 1) THEN
+      IF (INDEX(calend_str,'unknown') > 0) THEN
+        calend_str = tmp_cal
+      ELSE
+        CALL ipslerr (2,'restsett', &
+ &       ' In the restart files two different calendars were found.', &
+ &       ' Please check the files you have used.',' ')
+      ENDIF
+    ENDIF
+!---
+!-- We need to transform that into julian days
+!-- to get ride of the intial date.
+!---
+    IF (l_dbg) WRITE(ipslout,*) 'tax_orig : ',TRIM(tax_orig)
+    READ (UNIT=tax_orig,FMT='(I4.4,5(a,I2.2))') &
+      year0,strc,month0,strc,day0,strc, &
+      hours0,strc,minutes0,strc,seci
+    sec0 = REAL(seci)
+    sec0 = hours0*3600.+minutes0*60.+sec0
+    CALL ymds2ju (year0,month0,day0,sec0,date0_ju)
+    t_julian(nb_fi,:) = t_julian(nb_fi,:)/one_day+date0_ju
+  ENDIF
+!-
+  IF (     (INDEX(itau_orig,'XXXXX') > 0) &
+      .AND.(INDEX(tax_orig,'XXXXX')  < 1) ) THEN
+!!- Compute the t_itau from the date read and the timestep in the input
+  ENDIF
+!-
+  IF (     (INDEX(tax_orig,'XXXXX')  > 0) &
+      .AND.(INDEX(itau_orig,'XXXXX') < 1) ) THEN
+    DO it=1,tax_size_in(nb_fi)
+      t_julian(nb_fi,it) = itau2date(t_index(nb_fi,it),date0,timestep)
+    ENDDO
+  ENDIF
+!-
+! If neither the indices or time is present then get global attributes
+! This is for compatibility reasons and should not be used.
+!-
+  IF ((tax_varid_in(nb_fi) < 0).AND.(tind_varid_in(nb_fi) < 0)) THEN
+    iax = -1
+    DO iv=1,nbvar_in(nb_fi)
+      IF (    (INDEX(varname_in(nb_fi,iv),'tsteps') > 0) &
+ &        .OR.(INDEX(varname_in(nb_fi,iv),'time_steps') > 0)) THEN
+        iax = iv
+      ENDIF
+    ENDDO
+!---
+    IF (iax < 0) THEN
+      CALL ipslerr (3,'restsett',&
+        & 'No time axis was found in the restart file. Please check',&
+        & 'that it corresponds to the convention used in restsett',&
+        & ' ')
+    ELSE
+      iret = NF90_GET_VAR(ncfid,tind_varid_in(nb_fi),t_index(nb_fi,:))
+      iret = NF90_GET_ATT(ncfid,NF90_GLOBAL,'delta_tstep_sec',timestep)
+      iret = NF90_GET_ATT(ncfid,NF90_GLOBAL,'year0',ttmp)
+      year0 = NINT(ttmp)
+      iret = NF90_GET_ATT(ncfid,NF90_GLOBAL,'month0',ttmp)
+      month0 = NINT(ttmp)
+      iret = NF90_GET_ATT(ncfid,NF90_GLOBAL,'day0',ttmp)
+      day0 = NINT(ttmp)
+      iret = NF90_GET_ATT(ncfid,NF90_GLOBAL,'sec0',sec0)
+!---
+      CALL ymds2ju (year0,month0,day0,sec0,date0)
+      t_julian(nb_fi,1) = itau2date(t_index(nb_fi,1),date0,timestep)
+    ENDIF
+  ENDIF
+!----------------------
+END SUBROUTINE restsett
+!===
+SUBROUTINE restopenout &
+  (fid,fname,iim,jjm, &
+   lon,lat,llm,lev,timestep,date,ncfid,domain_id)
+!---------------------------------------------------------------------
+!- Opens the restart file for output.
+!- The longitude and time variables are written.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: fid,iim,jjm,llm
+  CHARACTER(LEN=*) :: fname
+  REAL :: date,timestep
+  REAL :: lon(iim,jjm),lat(iim,jjm),lev(llm)
+  INTEGER,INTENT(OUT) :: ncfid
+  INTEGER,INTENT(IN),OPTIONAL :: domain_id
+!-
+  INTEGER :: iret
+  CHARACTER(LEN=70) :: str_t
+  INTEGER :: x_id,y_id,z_id,itauid
+  INTEGER :: nlonid,nlatid,nlevid,timeid
+  INTEGER :: year,month,day,hours,minutes
+  REAL :: sec
+  CHARACTER(LEN=3),DIMENSION(12) :: &
+    cal = (/'JAN','FEB','MAR','APR','MAY','JUN', &
+            'JUL','AUG','SEP','OCT','NOV','DEC'/)
+  CHARACTER(LEN=30) :: timenow
+  LOGICAL :: l_dbg
+  INTEGER :: cmode, id
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (l_dbg) WRITE(ipslout,*) "restopenout 0.0 ",TRIM(fname)
+!-
+!  If we use the same file for input and output
+!- we will not even call restopenout
+!-
+  iret = NF90_CREATE(fname,cmode=IOR(NF90_NOCLOBBER,NF90_NETCDF4),ncid=ncfid)
+  IF (iret == -35) THEN
+    CALL ipslerr (3,'restopenout',&
+      & ' The restart file aready exists on the disc. IOIPSL ',&
+      & ' will not overwrite it. You should remove the old one or ',&
+      & ' generate the new one with another name')
+  ENDIF
+!- Define basic dimensions (x, y, z)
+  iret = NF90_DEF_DIM(ncfid,RESTART_DIMS_NAMES(RESTART_DIM_X),iim,x_id)
+  ax_nb(fid,RESTART_DIM_X) = ax_nb(fid, RESTART_DIM_X)+1
+  ax_infs(fid,RESTART_DIM_X, ax_nb(fid, RESTART_DIM_X),1) = iim
+  ax_infs(fid,RESTART_DIM_X, ax_nb(fid, RESTART_DIM_X),2) = x_id
+!-
+  iret = NF90_DEF_DIM(ncfid,RESTART_DIMS_NAMES(RESTART_DIM_Y),jjm,y_id)
+  ax_nb(fid,RESTART_DIM_Y) = ax_nb(fid, RESTART_DIM_Y)+1
+  ax_infs(fid,RESTART_DIM_Y, ax_nb(fid, RESTART_DIM_Y),1) = jjm
+  ax_infs(fid,RESTART_DIM_Y, ax_nb(fid, RESTART_DIM_Y),2) = y_id
+!-
+  iret = NF90_DEF_DIM(ncfid,RESTART_DIMS_NAMES(RESTART_DIM_Z),llm,z_id)
+  ax_nb(fid,RESTART_DIM_Z) = ax_nb(fid, RESTART_DIM_Z)+1
+  ax_infs(fid,RESTART_DIM_Z, ax_nb(fid, RESTART_DIM_Z),1) = llm
+  ax_infs(fid,RESTART_DIM_Z, ax_nb(fid, RESTART_DIM_Z),2) = z_id
+!-
+!  Initialize non basic dimensions
+!-
+  DO id=4,RESTART_MAX_DIMS
+    ax_nb(fid,id) = 1
+  ENDDO
+!-
+  iret = NF90_DEF_DIM(ncfid,'time',1,tdimid_out(fid))
+!-
+! 1.0 Longitude
+!-
+  IF (l_dbg) WRITE(ipslout,*) "restopenout 1.0"
+!-
+  iret = NF90_DEF_VAR(ncfid,"nav_lon",NF90_FLOAT,(/x_id,y_id/),nlonid)
+  iret = NF90_PUT_ATT(ncfid,nlonid,'units',"degrees_east")
+  iret = NF90_PUT_ATT(ncfid,nlonid,'valid_min',REAL(-180.,KIND=4))
+  iret = NF90_PUT_ATT(ncfid,nlonid,'valid_max',REAL( 180.,KIND=4))
+  iret = NF90_PUT_ATT(ncfid,nlonid,'long_name',"Longitude")
+!-
+! 2.0 Latitude
+!-
+  IF (l_dbg) WRITE(ipslout,*) "restopenout 2.0"
+!-
+  iret = NF90_DEF_VAR(ncfid,"nav_lat",NF90_FLOAT,(/x_id,y_id/),nlatid)
+  iret = NF90_PUT_ATT(ncfid,nlatid,'units',"degrees_north")
+  iret = NF90_PUT_ATT(ncfid,nlatid,'valid_min',REAL(-90.,KIND=4))
+  iret = NF90_PUT_ATT(ncfid,nlatid,'valid_max',REAL( 90.,KIND=4))
+  iret = NF90_PUT_ATT(ncfid,nlatid,'long_name',"Latitude")
+!-
+! 3.0 Levels
+!-
+  IF (l_dbg) WRITE(ipslout,*) "restopenout 3.0"
+!-
+  iret = NF90_DEF_VAR(ncfid,"nav_lev",NF90_FLOAT,z_id,nlevid)
+  iret = NF90_PUT_ATT(ncfid,nlevid,'units',"model_levels")
+  iret = NF90_PUT_ATT(ncfid,nlevid,'valid_min', &
+ &                     REAL(MINVAL(lev),KIND=4))
+  iret = NF90_PUT_ATT(ncfid,nlevid,'valid_max', &
+ &                     REAL(MAXVAL(lev),KIND=4))
+  iret = NF90_PUT_ATT(ncfid,nlevid,'long_name',"Model levels")
+!-
+! 4.0 Time axis, this is the seconds since axis
+!-
+  IF (l_dbg) WRITE(ipslout,*) "restopenout 4.0"
+!-
+  iret = NF90_DEF_VAR(ncfid,"time",NF90_FLOAT, &
+                       tdimid_out(fid),timeid)
+  tax_varid_out(fid) = timeid
+!-
+  timeorig(fid) = date
+  CALL ju2ymds (date,year,month,day,sec)
+  hours   = INT(sec/(60.*60.))
+  minutes = INT((sec-hours*60.*60.)/60.)
+  sec     = sec-(hours*60.*60.+minutes*60.)
+  WRITE (UNIT=str_t, &
+   FMT='("seconds since ",I4.4,2("-",I2.2)," ",I2.2,2(":",I2.2))') &
+ &  year,month,day,hours,minutes,INT(sec)
+  iret = NF90_PUT_ATT(ncfid,timeid,'units',TRIM(str_t))
+!-
+  CALL ioget_calendar (str_t)
+  iret = NF90_PUT_ATT(ncfid,timeid,'calendar',TRIM(str_t))
+  iret = NF90_PUT_ATT(ncfid,timeid,'title','Time')
+  iret = NF90_PUT_ATT(ncfid,timeid,'long_name','Time axis')
+!-
+  WRITE(UNIT=str_t, &
+   FMT='(" ",I4.4,"-",A3,"-",I2.2," ",I2.2,2(":",I2.2))') &
+ &  year,cal(month),day,hours,minutes,INT(sec)
+  iret = NF90_PUT_ATT(ncfid,timeid,'time_origin',TRIM(str_t))
+!-
+! 5.0 Time axis, this is the time steps since axis
+!-
+  IF (l_dbg) WRITE(ipslout,*) "restopenout 5.0"
+!-
+  iret = NF90_DEF_VAR(ncfid,"time_steps",NF90_INT, &
+ &                    tdimid_out(fid),itauid)
+  tind_varid_out(fid) = itauid
+!-
+  CALL ju2ymds (date,year,month,day,sec)
+!-
+  hours   = INT(sec/(60.*60.))
+  minutes = INT((sec-hours*60.*60.)/60.)
+  sec     = sec-(hours*60.*60.+minutes*60.)
+!-
+  WRITE (UNIT=str_t, &
+   FMT='("timesteps since ",I4.4,2("-",I2.2)," ",I2.2,2(":",I2.2))') &
+ &  year,month,day,hours,minutes,INT(sec)
+!-
+  iret = NF90_PUT_ATT(ncfid,itauid,'units',TRIM(str_t))
+  iret = NF90_PUT_ATT(ncfid,itauid,'title','Time steps')
+  iret = NF90_PUT_ATT(ncfid,itauid,'tstep_sec',REAL(timestep,KIND=4))
+  iret = NF90_PUT_ATT(ncfid,itauid,'long_name','Time step axis')
+!-
+  WRITE(UNIT=str_t, &
+   FMT='(" ",I4.4,"-",A3,"-",I2.2," ",I2.2,2(":",I2.2))') &
+ &  year,cal(month),day,hours,minutes,INT(sec)
+  iret = NF90_PUT_ATT(ncfid,itauid,'time_origin',TRIM(str_t))
+!-
+!  5.2 Write global attributes
+!-
+  iret = NF90_PUT_ATT(ncfid,NF90_GLOBAL,'Conventions',"CF-1.1")
+  iret = NF90_PUT_ATT(ncfid,NF90_GLOBAL,'file_name',TRIM(fname))
+!!  TO BE DONE LATER
+!!   iret = NF90_PUT_ATT(ncfid,NF90_GLOBAL, &
+!!                       'production',TRIM(model_name))
+!!   lock_modname = .TRUE.
+  CALL ioget_timestamp (timenow)
+  iret = NF90_PUT_ATT(ncfid,NF90_GLOBAL,'TimeStamp',TRIM(timenow))
+!-
+! Add DOMAIN attributes if needed
+!-
+  CALL flio_dom_att (ncfid,domain_id)
+!-
+! 6.0 The coordinates are written to the file
+!-
+  iret = NF90_ENDDEF(ncfid)
+!-
+  iret = NF90_PUT_VAR(ncfid,nlonid,lon)
+  iret = NF90_PUT_VAR(ncfid,nlatid,lat)
+  iret = NF90_PUT_VAR(ncfid,nlevid,lev)
+!-
+! 7.0 Set a few variables related to the out file
+!-
+  nbvar_out(fid) = 0
+  itau_out(fid) = -1
+  tstp_out(fid) = 0
+  touched_out(fid,:) = .FALSE.
+!-
+! 7.1 The file is put back in define mode.
+!     This will last until itau_out >= 0
+!-
+  iret = NF90_REDEF(ncfid)
+!-
+  IF (l_dbg) WRITE(ipslout,*) "restopenout END"
+!-------------------------
+END SUBROUTINE restopenout
+!===
+SUBROUTINE restget_opp_r1d &
+ & (fid,vname_q,iim,jjm,llm,itau,def_beha, &
+ &  var,MY_OPERATOR,nbindex,ijndex)
+!---------------------------------------------------------------------
+!- This subroutine serves as an interface to restget_real
+!-
+!- Should work as restput_opp_r1d but the other way around !
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid
+  CHARACTER(LEN=*) :: vname_q
+  INTEGER :: iim,jjm,llm,itau
+  LOGICAL def_beha
+  REAL :: var(:)
+  CHARACTER(LEN=*) :: MY_OPERATOR
+  INTEGER :: nbindex,ijndex(nbindex)
+!-
+  INTEGER :: req_sz,siz1
+  REAL :: scal
+  CHARACTER(LEN=7) :: topp
+  LOGICAL :: l_dbg
+  INTEGER :: list_dims(2) ! x and y
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+! 0.0 What size should be the data in the file
+!-
+  req_sz = 1
+  IF (nbindex == iim .AND. jjm <= 1 .AND. llm <= 1) THEN
+    IF (ax_infs(fid,RESTART_DIM_X,1,1) > 0) req_sz = req_sz*ax_infs(fid,RESTART_DIM_X,1,1)
+    IF (ax_infs(fid,RESTART_DIM_Y,1,1) > 0) req_sz = req_sz*ax_infs(fid,RESTART_DIM_Y,1,1)
+    IF (ax_infs(fid,RESTART_DIM_Z,1,1) > 0) req_sz = req_sz*ax_infs(fid,RESTART_DIM_Z,1,1)
+  ELSE
+    CALL ipslerr (3,'resget_opp_r1d', &
+      'Unable to performe an operation on this variable as it has',&
+      'a second and third dimension',vname_q)
+  ENDIF
+!-
+! 1.0 Allocate the temporary buffer we need
+!     to put the variable in right dimension
+!-
+  siz1 = SIZE(var)
+  CALL rest_alloc (1,siz1,l_dbg,'restget_opp_r1d')
+  CALL rest_alloc (2,req_sz,l_dbg,'restget_opp_r1d')
+!-
+! 2.0 Here we get the variable from the restart file
+!-
+  list_dims = (/ ax_infs(fid,RESTART_DIM_X,1,1), ax_infs(fid,RESTART_DIM_Y,1,1) /)
+!-
+  CALL restget_real &
+    (fid,vname_q,list_dims,itau,def_beha,buff_tmp2)
+!-
+! 4.0 Transfer the buffer obtained from the restart file
+!     into the variable the model expects
+!-
+  topp = MY_OPERATOR(1:MIN(LEN_TRIM(MY_OPERATOR),7))
+!-
+  IF (INDEX(indchfun,topp(:LEN_TRIM(topp))) > 0) THEN
+    scal = missing_val
+    CALL mathop (topp,req_sz,buff_tmp2,missing_val, &
+ &               nbindex,ijndex,scal,siz1,buff_tmp1)
+    var(:) = buff_tmp1(1:siz1)
+  ELSE
+    CALL ipslerr (3,'resget_opp_r1d', &
+      'The operation you wish to do on the variable for the ',&
+      'restart file is not allowed.',topp)
+  ENDIF
+  IF (ALLOCATED(buff_tmp1)) DEALLOCATE(buff_tmp1)
+  IF (ALLOCATED(buff_tmp2)) DEALLOCATE(buff_tmp2)
+!-----------------------------
+END SUBROUTINE restget_opp_r1d
+!===
+SUBROUTINE restget_opp_r2d &
+ & (fid,vname_q,iim,jjm,llm,itau,def_beha, &
+ &  var,MY_OPERATOR,nbindex,ijndex)
+!---------------------------------------------------------------------
+!- This subroutine serves as an interface to restget_real
+!-
+!- Should work as restput_opp_r2d but the other way around !
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid
+  CHARACTER(LEN=*) :: vname_q
+  INTEGER :: iim,jjm,llm,itau
+  LOGICAL def_beha
+  REAL :: var(:,:)
+  CHARACTER(LEN=*) :: MY_OPERATOR
+  INTEGER :: nbindex,ijndex(nbindex)
+!-
+  INTEGER :: jj,req_sz,ist,var_sz,siz1
+  REAL :: scal
+  CHARACTER(LEN=7) :: topp
+  LOGICAL :: l_dbg
+  INTEGER :: list_dims(3)
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+! 0.0 What size should be the data in the file
+!-
+  req_sz = 1
+  IF (nbindex == iim  .AND. llm <= 1) THEN
+    IF (ax_infs(fid,RESTART_DIM_X,1,1) > 0) req_sz = req_sz*ax_infs(fid,RESTART_DIM_X,1,1)
+    IF (ax_infs(fid,RESTART_DIM_Y,1,1) > 0) req_sz = req_sz*ax_infs(fid,RESTART_DIM_Y,1,1)
+  ELSE
+    CALL ipslerr (3,'resget_opp_r2d', &
+      'Unable to performe an operation on this variable as it has', &
+      'a second and third dimension',vname_q)
+  ENDIF
+!-
+  IF (jjm < 1) THEN
+    CALL ipslerr (3,'resget_opp_r2d', &
+      'Please specify a second dimension which is the', &
+      'layer on which the operations are performed',vname_q)
+  ENDIF
+!-
+! 1.0 Allocate the temporary buffer we need
+!     to put the variable in right dimension
+!-
+  siz1 = SIZE(var,1)
+  CALL rest_alloc (1,siz1,l_dbg,'restget_opp_r2d')
+  CALL rest_alloc (2,req_sz*jjm,l_dbg,'restget_opp_r2d')
+!-
+! 2.0 Here we get the full variable from the restart file
+!-
+  list_dims = (/ ax_infs(fid,RESTART_DIM_X,1,1), ax_infs(fid,RESTART_DIM_Y,1,1), jjm /)
+!-
+  CALL restget_real &
+ & (fid,vname_q,list_dims,itau,def_beha,buff_tmp2)
+!-
+! 4.0 Transfer the buffer obtained from the restart file
+!     into the variable the model expects
+!-
+  topp = MY_OPERATOR(1:MIN(LEN_TRIM(MY_OPERATOR),7))
+!-
+  IF (INDEX(indchfun,topp(:LEN_TRIM(topp))) > 0) THEN
+    scal = missing_val
+    var_sz = siz1
+    DO jj = 1,jjm
+      ist = (jj-1)*req_sz+1
+      CALL mathop (topp,req_sz,buff_tmp2(ist:ist+req_sz-1), &
+ &      missing_val,nbindex,ijndex,scal,var_sz,buff_tmp1)
+      var(:,jj) = buff_tmp1(1:siz1)
+    ENDDO
+  ELSE
+    CALL ipslerr (3,'resget_opp_r2d', &
+      'The operation you wish to do on the variable for the ',&
+      'restart file is not allowed.',topp)
+  ENDIF
+  IF (ALLOCATED(buff_tmp1)) DEALLOCATE(buff_tmp1)
+  IF (ALLOCATED(buff_tmp2)) DEALLOCATE(buff_tmp2)
+!-----------------------------
+END SUBROUTINE restget_opp_r2d
+!===
+SUBROUTINE restget_opp_r3d &
+ & (fid,vname_q,iim,jjm,llm,itau,def_beha, &
+ &  var,MY_OPERATOR,nbindex,ijndex)
+!---------------------------------------------------------------------
+!- This subroutine serves as an interface to restget_real
+!-
+!- Should work as restput_opp_r2d but the other way around !
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid
+  CHARACTER(LEN=*) :: vname_q
+  INTEGER :: iim,jjm,llm,itau
+  LOGICAL def_beha
+  REAL :: var(:,:,:)
+  CHARACTER(LEN=*) :: MY_OPERATOR
+  INTEGER :: nbindex,ijndex(nbindex)
+!-
+  INTEGER :: ll,jj,req_sz,ist,var_sz,siz1
+  REAL :: scal
+  CHARACTER(LEN=7) :: topp
+  LOGICAL :: l_dbg
+  INTEGER :: list_dims(4)
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+! 0.0 What size should be the data in the file
+!-
+  req_sz = 1
+  IF (nbindex == iim) THEN
+    IF (ax_infs(fid,RESTART_DIM_X,1,1) > 0) req_sz = req_sz*ax_infs(fid,RESTART_DIM_X,1,1)
+    IF (ax_infs(fid,RESTART_DIM_Y,1,1) > 0) req_sz = req_sz*ax_infs(fid,RESTART_DIM_Y,1,1)
+  ELSE
+    CALL ipslerr (3,'resget_opp_r3d', &
+      'Unable to performe an operation on this variable as it has', &
+      'a second and third dimension',vname_q)
+  ENDIF
+!-
+  IF (jjm < 1) THEN
+    CALL ipslerr (3,'resget_opp_r3d', &
+      'Please specify a second dimension which is the', &
+      'layer on which the operations are performed',vname_q)
+  ENDIF
+!-
+  IF (llm < 1) THEN
+    CALL ipslerr (3,'resget_opp_r3d', &
+      'Please specify a third dimension which is the', &
+      'layer on which the operations are performed',vname_q)
+  ENDIF
+!-
+! 1.0 Allocate the temporary buffer we need
+!     to put the variable in right dimension
+!-
+  siz1 = SIZE(var,1)
+  CALL rest_alloc (1,siz1,l_dbg,'restget_opp_r3d')
+  CALL rest_alloc (2,req_sz*jjm*llm,l_dbg,'restget_opp_r3d')
+!-
+! 2.0 Here we get the full variable from the restart file
+!-
+  list_dims = (/ ax_infs(fid,RESTART_DIM_X,1,1), ax_infs(fid,RESTART_DIM_Y,1,1), jjm, llm /)
+!-
+  CALL restget_real &
+ & (fid,vname_q,list_dims,itau,def_beha,buff_tmp2)
+!-
+! 4.0 Transfer the buffer obtained from the restart file
+!     into the variable the model expects
+!-
+  topp = MY_OPERATOR(1:MIN(LEN_TRIM(MY_OPERATOR),7))
+!-
+  IF (INDEX(indchfun,topp(:LEN_TRIM(topp))) > 0) THEN
+    scal = missing_val
+    var_sz = siz1
+    DO ll = 1, llm
+      DO jj = 1,jjm
+          ist = (((ll-1)*jjm) + (jj-1))*req_sz+1
+          CALL mathop (topp,req_sz,buff_tmp2(ist:ist+req_sz-1), &
+ &           missing_val,nbindex,ijndex,scal,var_sz,buff_tmp1)
+          var(:,jj,ll) = buff_tmp1(1:siz1)
+      ENDDO
+    ENDDO
+  ELSE
+    CALL ipslerr (3,'resget_opp_r3d', &
+      'The operation you wish to do on the variable for the ',&
+      'restart file is not allowed.',topp)
+  ENDIF
+  IF (ALLOCATED(buff_tmp1)) DEALLOCATE(buff_tmp1)
+  IF (ALLOCATED(buff_tmp2)) DEALLOCATE(buff_tmp2)
+!-----------------------------
+END SUBROUTINE restget_opp_r3d
+!===
+SUBROUTINE restget_opp_r4d &
+ & (fid,vname_q,iim,jjm,llm,mmm,itau,def_beha, &
+ &  var,MY_OPERATOR,nbindex,ijndex)
+!---------------------------------------------------------------------
+!- This subroutine serves as an interface to restget_real
+!-
+!- Should work as restput_opp_r2d but the other way around !
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid
+  CHARACTER(LEN=*) :: vname_q
+  INTEGER :: iim,jjm,llm,mmm,itau
+  LOGICAL def_beha
+  REAL :: var(:,:,:,:)
+  CHARACTER(LEN=*) :: MY_OPERATOR
+  INTEGER :: nbindex,ijndex(nbindex)
+!-
+  INTEGER :: mm,ll,jj,req_sz,ist,var_sz,siz1
+  REAL :: scal
+  CHARACTER(LEN=7) :: topp
+  LOGICAL :: l_dbg
+  INTEGER :: list_dims(5)
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+! 0.0 What size should be the data in the file
+!-
+  req_sz = 1
+  IF (nbindex == iim) THEN
+    IF (ax_infs(fid,RESTART_DIM_X,1,1) > 0) req_sz = req_sz*ax_infs(fid,RESTART_DIM_X,1,1)
+    IF (ax_infs(fid,RESTART_DIM_Y,1,1) > 0) req_sz = req_sz*ax_infs(fid,RESTART_DIM_Y,1,1)
+  ELSE
+    CALL ipslerr (3,'restget_opp_r4d', &
+      'Unable to performe an operation on this variable as it has', &
+      'a second and third dimension',vname_q)
+  ENDIF
+!-
+! 1.0 Allocate the temporary buffer we need
+!     to put the variable in right dimension
+!-
+  siz1 = SIZE(var,1)
+  CALL rest_alloc (1,siz1,l_dbg,'restget_opp_r4d')
+  CALL rest_alloc (2,req_sz*jjm*llm*mmm,l_dbg,'restget_opp_r4d')
+!-
+! 2.0 Here we get the full variable from the restart file
+!-
+  list_dims = (/ ax_infs(fid,RESTART_DIM_X,1,1), ax_infs(fid,RESTART_DIM_Y,1,1), jjm, llm, mmm /)
+!-
+  CALL restget_real &
+ & (fid,vname_q,list_dims,itau,def_beha,buff_tmp2)
+!-
+! 4.0 Transfer the buffer obtained from the restart file
+!     into the variable the model expects
+!-
+  topp = MY_OPERATOR(1:MIN(LEN_TRIM(MY_OPERATOR),7))
+!-
+  IF (INDEX(indchfun,topp(:LEN_TRIM(topp))) > 0) THEN
+    scal = missing_val
+    var_sz = siz1
+    DO mm = 1, mmm
+      DO ll = 1, llm
+        DO jj = 1,jjm
+            ist = ((((mm-1)*llm + (ll-1))*jjm) + (jj-1))*req_sz+1
+            CALL mathop (topp,req_sz,buff_tmp2(ist:ist+req_sz-1), &
+ &             missing_val,nbindex,ijndex,scal,var_sz,buff_tmp1)
+            var(:,jj,ll,mm) = buff_tmp1(1:siz1)
+        ENDDO
+      ENDDO
+    ENDDO
+  ELSE
+    CALL ipslerr (3,'restget_opp_r4d', &
+      'The operation you wish to do on the variable for the ',&
+      'restart file is not allowed.',topp)
+  ENDIF
+  IF (ALLOCATED(buff_tmp1)) DEALLOCATE(buff_tmp1)
+  IF (ALLOCATED(buff_tmp2)) DEALLOCATE(buff_tmp2)
+!-----------------------------
+END SUBROUTINE restget_opp_r4d
+!===
+SUBROUTINE restget_opp_r5d &
+ & (fid,vname_q,iim,jjm,llm,mmm,nnm,itau,def_beha, &
+ &  var,MY_OPERATOR,nbindex,ijndex)
+!---------------------------------------------------------------------
+!- This subroutine serves as an interface to restget_real
+!-
+!- Should work as restput_opp_r2d but the other way around !
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid
+  CHARACTER(LEN=*) :: vname_q
+  INTEGER :: iim,jjm,llm,mmm,nnm,itau
+  LOGICAL def_beha
+  REAL :: var(:,:,:,:,:)
+  CHARACTER(LEN=*) :: MY_OPERATOR
+  INTEGER :: nbindex,ijndex(nbindex)
+!-
+  INTEGER :: mm,ll,jj,nn,req_sz,ist,var_sz,siz1
+  REAL :: scal
+  CHARACTER(LEN=7) :: topp
+  LOGICAL :: l_dbg
+  INTEGER :: list_dims(6)
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+! 0.0 What size should be the data in the file
+!-
+  req_sz = 1
+  IF (nbindex == iim) THEN
+    IF (ax_infs(fid,RESTART_DIM_X,1,1) > 0) req_sz = req_sz*ax_infs(fid,RESTART_DIM_X,1,1)
+    IF (ax_infs(fid,RESTART_DIM_Y,1,1) > 0) req_sz = req_sz*ax_infs(fid,RESTART_DIM_Y,1,1)
+  ELSE
+    CALL ipslerr (3,'restget_opp_r5d', &
+      'Unable to performe an operation on this variable as it has', &
+      'a second and third dimension',vname_q)
+  ENDIF
+!-
+! 1.0 Allocate the temporary buffer we need
+!     to put the variable in right dimension
+!-
+  siz1 = SIZE(var,1)
+  CALL rest_alloc (1,siz1,l_dbg,'restget_opp_r5d')
+  CALL rest_alloc (2,req_sz*jjm*llm*mmm*nnm,l_dbg,'restget_opp_r5d')
+!-
+! 2.0 Here we get the full variable from the restart file
+!-
+  list_dims = (/ ax_infs(fid,RESTART_DIM_X,1,1), ax_infs(fid,RESTART_DIM_Y,1,1), jjm, llm, mmm, nnm /)
+!-
+  CALL restget_real &
+ & (fid,vname_q,list_dims,itau,def_beha,buff_tmp2)
+!-
+! 4.0 Transfer the buffer obtained from the restart file
+!     into the variable the model expects
+!-
+  topp = MY_OPERATOR(1:MIN(LEN_TRIM(MY_OPERATOR),7))
+!-
+  IF (INDEX(indchfun,topp(:LEN_TRIM(topp))) > 0) THEN
+    scal = missing_val
+    var_sz = siz1
+    DO nn=1, nnm
+      DO mm = 1, mmm
+        DO ll = 1, llm
+          DO jj = 1,jjm
+              ist = ((((((nn-1)*mmm) + (mm-1))*llm + (ll-1))*jjm) + (jj-1))*req_sz+1
+              CALL mathop (topp,req_sz,buff_tmp2(ist:ist+req_sz-1), &
+ &               missing_val,nbindex,ijndex,scal,var_sz,buff_tmp1)
+              var(:,jj,ll,mm,nn) = buff_tmp1(1:siz1)
+          ENDDO
+        ENDDO
+      ENDDO
+    ENDDO
+  ELSE
+    CALL ipslerr (3,'restget_opp_r5d', &
+      'The operation you wish to do on the variable for the ',&
+      'restart file is not allowed.',topp)
+  ENDIF
+  IF (ALLOCATED(buff_tmp1)) DEALLOCATE(buff_tmp1)
+  IF (ALLOCATED(buff_tmp2)) DEALLOCATE(buff_tmp2)
+!-----------------------------
+END SUBROUTINE restget_opp_r5d
+!===
+SUBROUTINE restget_r1d &
+ & (fid,vname_q,iim,jjm,llm,itau,def_beha,var)
+!---------------------------------------------------------------------
+!- This subroutine serves as an interface to restget_real
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid
+  CHARACTER(LEN=*) :: vname_q
+  INTEGER :: iim,jjm,llm,itau
+  LOGICAL :: def_beha
+  REAL :: var(:)
+!-
+  INTEGER :: ji,jl,req_sz,var_sz,siz1
+  CHARACTER(LEN=70) :: str,str2
+  LOGICAL :: l_dbg
+  INTEGER :: list_dims(1)
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+! 1.0 Allocate the temporary buffer we need
+!     to put the variable in right dimension
+!-
+  siz1 = SIZE(var)
+  var_sz = siz1
+  CALL rest_alloc (1,var_sz,l_dbg,'restget_r1d')
+!-
+! 2.0 Here we could check if the sizes specified agree
+!     with the size of the variable provided
+!-
+  req_sz = 1
+  IF (iim > 0) req_sz = req_sz*iim
+  IF (jjm > 0) req_sz = req_sz*jjm
+  IF (llm > 0) req_sz = req_sz*llm
+  IF (req_sz > var_sz) THEN
+    WRITE(str, &
+ &    '("Size of variable requested from file should be ",I6)') req_sz
+    WRITE(str2, &
+ &    '("but the provided variable can only hold ",I6)') var_sz
+    CALL ipslerr (3,'restget_r1d',str,str2,' ')
+  ENDIF
+  IF (req_sz < var_sz) THEN
+    WRITE(str, &
+ &    '("the size of variable requested from file is ",I6)') req_sz
+    WRITE(str2, &
+ &    '("but the provided variable can hold ",I6)') var_sz
+    CALL ipslerr (2,'restget_r1d', &
+      'There could be a problem here :',str,str2)
+  ENDIF
+!-
+  list_dims = (/ iim /)
+!-
+  CALL restget_real &
+ & (fid,vname_q,list_dims,itau,def_beha,buff_tmp1)
+!-
+! 4.0 Transfer the buffer obtained from the restart file
+!     into the variable the model expects
+!-
+  jl=0
+  DO ji=1,siz1
+    jl=jl+1
+    var(ji) = buff_tmp1(jl)
+  ENDDO
+  IF (ALLOCATED(buff_tmp1)) DEALLOCATE(buff_tmp1)
+  IF (ALLOCATED(buff_tmp2)) DEALLOCATE(buff_tmp2)
+!-------------------------
+END SUBROUTINE restget_r1d
+!===
+SUBROUTINE restget_r2d &
+ & (fid,vname_q,iim,jjm,llm,itau,def_beha,var)
+!---------------------------------------------------------------------
+!- This subroutine serves as an interface to restget_real
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid
+  CHARACTER(LEN=*) :: vname_q
+  INTEGER :: iim,jjm,llm,itau
+  LOGICAL :: def_beha
+  REAL :: var(:,:)
+!-
+  INTEGER :: ji,jj,jl,req_sz,var_sz,siz1,siz2
+  CHARACTER(LEN=70) :: str,str2
+  LOGICAL :: l_dbg
+  INTEGER :: list_dims(2)
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+! 1.0 Allocate the temporary buffer we need
+!     to put the variable in right dimension
+!-
+  siz1 = SIZE(var,1)
+  siz2 = SIZE(var,2)
+  var_sz = siz1*siz2
+  CALL rest_alloc (1,var_sz,l_dbg,'restget_r2d')
+!-
+! 2.0 Here we check if the sizes specified agree
+!     with the size of the variable provided
+!-
+  req_sz = 1
+  IF (iim > 0) req_sz = req_sz*iim
+  IF (jjm > 0) req_sz = req_sz*jjm
+  IF (llm > 0) req_sz = req_sz*llm
+  IF (req_sz > var_sz) THEN
+    WRITE(str, &
+ &    '("Size of variable ",A, &
+ &      //" requested from file should be ",I6)') TRIM(vname_q),req_sz
+    WRITE(str2, &
+ &    '("but the provided variable can only hold ",I6)') var_sz
+    CALL ipslerr (3,'restget_r2d',str,str2,' ')
+  ENDIF
+  IF (req_sz < var_sz) THEN
+    WRITE(str, &
+ &    '("Size of variable ",A, &
+ &      //" requested from file is ",I6)') TRIM(vname_q),req_sz
+    WRITE(str2,'("but the provided variable can hold ",I6)') var_sz
+    CALL ipslerr (2,'restget_r2d', &
+      'There could be a problem here :',str,str2)
+  ENDIF
+!-
+  list_dims = (/ iim,jjm /)
+!-
+  CALL restget_real &
+ & (fid,vname_q,list_dims,itau,def_beha,buff_tmp1)
+!-
+! 4.0 Transfer the buffer obtained from the restart file
+!     into the variable the model expects
+!-
+  jl=0
+  DO jj=1,siz2
+    DO ji=1,siz1
+      jl=jl+1
+      var(ji,jj) = buff_tmp1(jl)
+    ENDDO
+  ENDDO
+  IF (ALLOCATED(buff_tmp1)) DEALLOCATE(buff_tmp1)
+  IF (ALLOCATED(buff_tmp2)) DEALLOCATE(buff_tmp2)
+!-------------------------
+END SUBROUTINE restget_r2d
+!===
+SUBROUTINE restget_r3d &
+  (fid,vname_q,iim,jjm,llm,itau,def_beha,var)
+!---------------------------------------------------------------------
+!- This subroutine serves as an interface to restget_real
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid
+  CHARACTER(LEN=*) :: vname_q
+  INTEGER :: iim,jjm,llm,itau
+  LOGICAL def_beha
+  REAL :: var(:,:,:)
+!-
+  INTEGER :: ji,jj,jk,jl,req_sz,var_sz,siz1,siz2,siz3
+  CHARACTER(LEN=70) :: str,str2
+  LOGICAL :: l_dbg
+  INTEGER :: list_dims(3)
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+! 1.0 Allocate the temporary buffer we need
+!     to put the variable in right dimension
+!-
+  siz1 = SIZE(var,1)
+  siz2 = SIZE(var,2)
+  siz3 = SIZE(var,3)
+  var_sz = siz1*siz2*siz3
+  CALL rest_alloc (1,var_sz,l_dbg,'restget_r3d')
+!-
+! 2.0 Here we check if the sizes specified agree
+!     with the size of the variable provided
+!-
+  req_sz = 1
+  IF (iim > 0) req_sz = req_sz*iim
+  IF (jjm > 0) req_sz = req_sz*jjm
+  IF (llm > 0) req_sz = req_sz*llm
+  IF (req_sz > var_sz) THEN
+    WRITE(str, &
+ &    '("Size of variable ",A, &
+ &      //" requested from file should be ",I6)') TRIM(vname_q),req_sz
+    WRITE(str2, &
+ &    '("but the provided variable can only hold ",I6)') var_sz
+    CALL ipslerr (3,'restget_r3d',str,str2,' ')
+  ENDIF
+  IF (req_sz < var_sz) THEN
+    WRITE(str, &
+ &    '("Size of variable ",A, &
+ &      //" requested from file is ",I6)') TRIM(vname_q),req_sz
+    WRITE(str2,'("but the provided variable can hold ",I6)') var_sz
+    CALL ipslerr (2,'restget_r3d', &
+      'There could be a problem here :',str,str2)
+  ENDIF
+!-
+  list_dims = (/ iim,jjm,llm /)
+!-
+  CALL restget_real &
+    (fid,vname_q,list_dims,itau,def_beha,buff_tmp1)
+!-
+! 4.0 Transfer the buffer obtained from the restart file
+!     into the variable the model expects
+!-
+  jl=0
+  DO jk=1,siz3
+    DO jj=1,siz2
+      DO ji=1,siz1
+        jl=jl+1
+        var(ji,jj,jk) = buff_tmp1(jl)
+      ENDDO
+    ENDDO
+  ENDDO
+  IF (ALLOCATED(buff_tmp1)) DEALLOCATE(buff_tmp1)
+  IF (ALLOCATED(buff_tmp2)) DEALLOCATE(buff_tmp2)
+!-------------------------
+END SUBROUTINE restget_r3d
+!===
+SUBROUTINE restget_real &
+  (fid,vname_q,list_dims,itau,def_beha,var)
+!---------------------------------------------------------------------
+!- This subroutine is for getting a variable from the restart file.
+!- A number of verifications will be made :
+!- - Is this the first time we read this variable ?
+!- - Are the dimensions correct ?
+!- - Is the correct time step present in the file
+!- - is a default behaviour possible. If not the model is stoped.
+!- Default procedure is to write the content of val_exp on all values.
+!-
+!- INPUT
+!-
+!- fid            : Identification of the file
+!- vname_q        : Name of the variable to be read
+!- iim, jjm ,llm  : Dimensions of the variable that should be read
+!- itau           : Time step at whcih we are when we want
+!-                  to read the variable
+!- def_beha       : If the model can restart without this variable
+!-                  then some strange value is given.
+!-
+!- OUTPUT
+!-
+!- var            : Variable in which the data is put
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid
+  CHARACTER(LEN=*) :: vname_q
+  INTEGER,DIMENSION(:),INTENT(in) :: list_dims
+  INTEGER :: itau
+  LOGICAL :: def_beha
+  REAL :: var(:)
+!-
+  INTEGER :: vid,vnb,ncfid,iret,index,it,ndim,ia,itedim
+  CHARACTER(LEN=70) str,str2
+  CHARACTER(LEN=80) attname
+  INTEGER,DIMENSION(RESTART_MAX_DIMS) :: corner,edge
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!---------------------------------------------------------------------
+  IF (l_dbg) WRITE(ipslout,*) 'RESTGET 0.0 : ',netcdf_name(fid,2),vname_q,list_dims,itau,def_beha
+!-
+  ncfid = netcdf_id(fid,1)
+!-
+  CALL find_str (varname_in(fid,1:nbvar_in(fid)),vname_q,vnb)
+!-
+! 1.0 If the variable is not present then ERROR or filled up
+!     by default values if allowed
+!-
+  IF (l_dbg) WRITE(ipslout,*) 'RESTGET 1.0 : ',vnb
+!-
+  IF (ANY(list_dims < 1)) THEN
+      CALL ipslerr (3,'restget', &
+ &      'All values in list_dims must be positive','','')
+  ENDIF
+  IF (SIZE(list_dims, 1) > RESTART_MAX_DIMS) THEN
+      CALL ipslerr (3,'restget', &
+ &      'Limit reached for dimensions','Please increase RESTART_MAX_DIMS to allow', &
+        ' more dimensions in single variable ')
+  ENDIF
+  IF (SIZE(list_dims, 1) == 0) THEN
+      CALL ipslerr (3,'restget', &
+ &      'No dimension is given','Make sure to pass a valid list_dims', &
+        ' ')
+  ENDIF
+!-
+  IF (vnb < 0) THEN
+    IF (def_beha) THEN
+!-----
+      lock_valexp = .TRUE.
+      var(:) = val_exp
+!----
+      str = 'Variable '//TRIM(vname_q) &
+          //' is not present in the restart file'
+      CALL ipslerr (1,'restget', &
+ &      str,'but default values are used to fill in',' ')
+!----
+      IF (nbvar_in(fid) >= max_var) THEN
+        CALL ipslerr (3,'restget', &
+         'Too many variables for the restcom module', &
+         'Please increase the value of max_var',' ')
+      ENDIF
+      nbvar_in(fid) = nbvar_in(fid)+1
+      vnb = nbvar_in(fid)
+      varname_in(fid,vnb) = vname_q
+      touched_in(fid,vnb) = .TRUE.
+!-----
+      CALL restdefv (fid,vname_q,list_dims,.TRUE.)
+      IF (l_dbg) WRITE(ipslout,*) 'RESTGET 1.1 : ',vnb
+!-----
+    ELSE
+      str = 'Variable '//TRIM(vname_q) &
+          //' is not present in the restart file'
+      CALL ipslerr (3,'restget', &
+ &      str,'but it is need to restart the model',' ')
+    ENDIF
+!---
+  ELSE
+!---
+!--  2.0 Check if the variable has not yet been read
+!--      and that the time is OK
+!---
+    vid = varid_in(fid,vnb)
+    IF (l_dbg) WRITE(ipslout,*) 'RESTGET 2.0 : ',vid
+!---
+    nbvar_read(fid) = nbvar_read(fid)+1
+!---
+    IF (touched_in(fid,vnb)) THEN
+      str = 'Variable '//TRIM(vname_q) &
+          //' has already been read from file'
+      CALL ipslerr (3,'restget',str,' ',' ')
+    ENDIF
+!---
+!-- 3.0 get the time step of the restart file
+!--     and check if it is correct
+!---
+    index = -1
+    DO it=1,tax_size_in(fid)
+      IF (t_index(fid,it) == itau)  index = it
+    ENDDO
+    IF (index < 0) THEN
+      str = 'The time step requested for variable '//TRIM(vname_q)
+      CALL ipslerr (3,'restget', &
+ &      str,'is not available in the current file',' ')
+    ENDIF
+    IF (l_dbg) WRITE(ipslout,*) 'RESTGET 3.0 : ',index
+!---
+!-- 4.0 Read the data. Note that the variables in the restart files
+!--     have no time axis is and thus we write -1
+!---
+    str='Incorrect dimension for '//TRIM(vname_q)
+    ndim = 0
+!--
+    DO itedim=1, SIZE(list_dims,1)
+        ndim = ndim+1
+        IF (vardims_in(fid,vnb,ndim) == list_dims(itedim)) THEN
+          corner(ndim) = 1
+          edge(ndim) = list_dims(itedim)
+        ELSE
+          WRITE (str2,'("Incompatibility for I2 dimension : ",I6,I6,I6)') &
+               itedim,list_dims(itedim),vardims_in(fid,vnb,ndim)
+          CALL ipslerr (3,'restget',str,str2,' ')
+        ENDIF
+    ENDDO
+!---
+!-- Time
+!---
+    ndim = ndim+1
+    corner(ndim) = index
+    edge(ndim) = 1
+!---
+    iret = NF90_GET_VAR(ncfid,vid,var, &
+ &                      start=corner(1:ndim),count=edge(1:ndim))
+    IF (l_dbg) WRITE(ipslout,*) 'RESTGET 4.0 : ',iret
+!---
+!-- 5.0 The variable we have just read is created
+!--      in the next restart file
+!---
+    IF (     (netcdf_id(fid,1) /= netcdf_id(fid,2))  &
+ &      .AND.(netcdf_id(fid,2) > 0) ) THEN
+!-----
+      CALL restdefv (fid,vname_q,list_dims,.TRUE.)
+!-----
+      DO ia = 1,varatt_in(fid,vnb)
+        iret = NF90_INQ_ATTNAME(ncfid,vid,ia,attname)
+        iret = NF90_COPY_ATT(ncfid,vid,attname, &
+ &               netcdf_id(fid,2),varid_out(fid,nbvar_out(fid)))
+      ENDDO
+!-----
+      IF (itau_out(fid) >= 0) THEN
+        iret = NF90_ENDDEF(netcdf_id(fid,2))
+      ENDIF
+    ENDIF
+!---
+  ENDIF
+!--------------------------
+END SUBROUTINE restget_real
+!===
+SUBROUTINE restput_opp_r1d &
+ & (fid,vname_q,iim,jjm,llm,itau,var,MY_OPERATOR,nbindex,ijndex)
+!---------------------------------------------------------------------
+!- This subroutine is the interface to restput_real which allows
+!- to re-index data onto the original grid of the restart file.
+!- The logic we use is still fuzzy in my mind but that is probably
+!- only because I have not yet though through everything.
+!-
+!- In the case iim = nbindex it means that the user attempts
+!- to project a vector back onto the original 2D or 3D field.
+!- This requires that jjm and llm be equal to 1 or 0,
+!- else I would not know what it means.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid
+  CHARACTER(LEN=*) :: vname_q
+  INTEGER :: iim,jjm,llm,itau
+  REAL :: var(:)
+  CHARACTER(LEN=*) :: MY_OPERATOR
+  INTEGER :: nbindex,ijndex(nbindex)
+!-
+  INTEGER :: req_sz,siz1
+  REAL :: scal
+  CHARACTER(LEN=7) :: topp
+  LOGICAL :: l_dbg
+  INTEGER :: list_dims(2)
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+! 0.0 What size should be the data in the file
+!-
+  req_sz = 1
+  IF ( nbindex == iim .AND. jjm <= 1 .AND. llm <= 1) THEN
+    IF (ax_infs(fid,RESTART_DIM_X,1,1) > 0) req_sz = req_sz*ax_infs(fid,RESTART_DIM_X,1,1)
+    IF (ax_infs(fid,RESTART_DIM_Y,1,1) > 0) req_sz = req_sz*ax_infs(fid,RESTART_DIM_Y,1,1)
+  ELSE
+    CALL ipslerr (3,'restput_opp_r1d', &
+      'Unable to performe an operation on this variable as it has', &
+      'a second and third dimension',vname_q)
+  ENDIF
+!-
+! 1.0 Allocate the temporary buffer we need
+!     to put the variable in right dimension
+!-
+  siz1 = SIZE(var)
+  CALL rest_alloc (1,siz1,l_dbg,'restput_opp_r1d')
+  CALL rest_alloc (2,req_sz,l_dbg,'restput_opp_r1d')
+!-
+! 2.0 We do the operation needed.
+!     It can only be a re-indexing operation.
+!     You would not want to change the values in a restart file or ?
+!-
+  topp = MY_OPERATOR(1:MIN(LEN_TRIM(MY_OPERATOR),7))
+!-
+  IF (INDEX(indchfun,topp(:LEN_TRIM(topp))) > 0) THEN
+    scal = missing_val
+    buff_tmp1(1:siz1) = var(:)
+    CALL mathop &
+ &    (topp,siz1,buff_tmp1,missing_val,nbindex,ijndex, &
+ &     scal,req_sz,buff_tmp2)
+  ELSE
+    CALL ipslerr (3,'restput_opp_r1d', &
+ &    'The operation you wish to do on the variable for the ', &
+ &    'restart file is not allowed.',topp)
+  ENDIF
+!-
+  list_dims = (/ ax_infs(fid,RESTART_DIM_X,1,1), ax_infs(fid,RESTART_DIM_Y,1,1) /)
+!-
+  CALL restput_real &
+ & (fid,vname_q,list_dims,itau,buff_tmp2)
+!-----------------------------
+  IF (ALLOCATED(buff_tmp1)) DEALLOCATE(buff_tmp1)
+  IF (ALLOCATED(buff_tmp2)) DEALLOCATE(buff_tmp2)
+END SUBROUTINE restput_opp_r1d
+!===
+SUBROUTINE restput_opp_r2d &
+ & (fid,vname_q,iim,jjm,llm,itau,var,MY_OPERATOR,nbindex,ijndex)
+!---------------------------------------------------------------------
+!- This subroutine is the interface to restput_real which allows
+!- to re-index data onto the original grid of the restart file.
+!- The logic we use is still fuzzy in my mind but that is probably
+!- only because I have not yet though through everything.
+!-
+!- In the case iim = nbindex it means that the user attempts
+!- to project the first dimension of the matrix back onto a 3D field
+!- where jjm will be the third dimension.
+!- Here we do not allow for 4D data, thus we will take the first
+!- two dimensions in the file and require that llm = 1.
+!- These are pretty heavy constraints but I do not know how
+!- to make it more general. I need to think about it some more.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid
+  CHARACTER(LEN=*) :: vname_q
+  INTEGER :: iim,jjm,llm,itau
+  REAL :: var(:,:)
+  CHARACTER(LEN=*) :: MY_OPERATOR
+  INTEGER :: nbindex,ijndex(nbindex)
+!-
+  INTEGER :: jj,req_sz,ist,siz1
+  REAL :: scal
+  CHARACTER(LEN=7) :: topp
+  LOGICAL :: l_dbg
+  INTEGER :: list_dims(3)
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+! 0.0 What size should be the data in the file
+!-
+  req_sz = 1
+  IF ( nbindex == iim .AND. llm <= 1) THEN
+    IF (ax_infs(fid,RESTART_DIM_X,1,1) > 0) req_sz = req_sz*ax_infs(fid,RESTART_DIM_X,1,1)
+    IF (ax_infs(fid,RESTART_DIM_Y,1,1) > 0) req_sz = req_sz*ax_infs(fid,RESTART_DIM_Y,1,1)
+  ELSE
+    CALL ipslerr (3,'restput_opp_r2d', &
+      'Unable to performe an operation on this variable as it has', &
+      'a second and third dimension',vname_q)
+  ENDIF
+!-
+  IF (jjm < 1) THEN
+    CALL ipslerr (3,'restput_opp_r2d', &
+      'Please specify a second dimension which is the', &
+      'layer on which the operations are performed',vname_q)
+  ENDIF
+!-
+! 1.0 Allocate the temporary buffer we need
+!     to put the variable in right dimension
+!-
+  siz1 = SIZE(var,1)
+  CALL rest_alloc (1,siz1,l_dbg,'restput_opp_r2d')
+  CALL rest_alloc (2,req_sz*jjm,l_dbg,'restput_opp_r2d')
+!-
+! 2.0 We do the operation needed.
+!     It can only be a re-indexing operation.
+!     You would not want to change the values in a restart file or ?
+!-
+  topp = MY_OPERATOR(1:MIN(LEN_TRIM(MY_OPERATOR),7))
+!-
+  IF (INDEX(indchfun,topp(:LEN_TRIM(topp))) > 0) THEN
+    scal = missing_val
+    DO jj = 1,jjm
+      buff_tmp1(1:siz1) = var(:,jj)
+      ist = (jj-1)*req_sz+1
+      CALL mathop &
+ &      (topp,siz1,buff_tmp1,missing_val,nbindex,ijndex, &
+ &       scal,req_sz,buff_tmp2(ist:ist+req_sz-1))
+    ENDDO
+  ELSE
+    CALL ipslerr (3,'restput_opp_r2d', &
+ &    'The operation you wish to do on the variable for the ', &
+ &    'restart file is not allowed.',topp)
+  ENDIF
+!-
+  list_dims = (/ ax_infs(fid,RESTART_DIM_X,1,1),ax_infs(fid,RESTART_DIM_Y,1,1),jjm /)
+!-
+  CALL restput_real (fid,vname_q, list_dims,itau,buff_tmp2)
+!-----------------------------
+  IF (ALLOCATED(buff_tmp1)) DEALLOCATE(buff_tmp1)
+  IF (ALLOCATED(buff_tmp2)) DEALLOCATE(buff_tmp2)
+END SUBROUTINE restput_opp_r2d
+!===
+SUBROUTINE restput_opp_r3d &
+ & (fid,vname_q,iim,jjm,llm,itau,var,MY_OPERATOR,nbindex,ijndex)
+!---------------------------------------------------------------------
+!- This subroutine is the interface to restput_real which allows
+!- to re-index data onto the original grid of the restart file.
+!- The logic we use is still fuzzy in my mind but that is probably
+!- only because I have not yet though through everything.
+!-
+!- In the case iim = nbindex it means that the user attempts
+!- to project the first dimension of the matrix back onto a 3D field
+!- where jjm will be the third dimension.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid
+  CHARACTER(LEN=*) :: vname_q
+  INTEGER :: iim,jjm,llm,itau
+  REAL :: var(:,:,:)
+  CHARACTER(LEN=*) :: MY_OPERATOR
+  INTEGER :: nbindex,ijndex(nbindex)
+!-
+  INTEGER :: jj,ll,req_sz,ist,siz1
+  REAL :: scal
+  CHARACTER(LEN=7) :: topp
+  LOGICAL :: l_dbg
+  INTEGER :: list_dims(4)
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+! 0.0 What size should be the data in the file
+!-
+  req_sz = 1
+  IF ( nbindex == iim) THEN
+    IF (ax_infs(fid,RESTART_DIM_X,1,1) > 0) req_sz = req_sz*ax_infs(fid,RESTART_DIM_X,1,1)
+    IF (ax_infs(fid,RESTART_DIM_Y,1,1) > 0) req_sz = req_sz*ax_infs(fid,RESTART_DIM_Y,1,1)
+  ELSE
+    CALL ipslerr (3,'restput_opp_r3d', &
+      'Unable to performe an operation on this variable as it has', &
+      'a second and third dimension',vname_q)
+  ENDIF
+!-
+  IF (jjm < 1 .OR. llm < 1) THEN
+    CALL ipslerr (3,'restput_opp_r3d', &
+      'Please specify a second dimension which is the', &
+      'layer on which the operations are performed',vname_q)
+  ENDIF
+!-
+! 1.0 Allocate the temporary buffer we need
+!     to put the variable in right dimension
+!-
+  siz1 = SIZE(var,1)
+  CALL rest_alloc (1,siz1,l_dbg,'restput_opp_r3d')
+  CALL rest_alloc (2,req_sz*jjm*llm,l_dbg,'restput_opp_r3d')
+!-
+! 2.0 We do the operation needed.
+!     It can only be a re-indexing operation.
+!     You would not want to change the values in a restart file or ?
+!-
+  topp = MY_OPERATOR(1:MIN(LEN_TRIM(MY_OPERATOR),7))
+!-
+  IF (INDEX(indchfun,topp(:LEN_TRIM(topp))) > 0) THEN
+    scal = missing_val
+    DO ll = 1,llm
+        DO jj = 1,jjm
+          buff_tmp1(1:siz1) = var(:,jj,ll)
+          ist = (((ll-1)*jjm) + (jj-1))*req_sz+1
+          CALL mathop &
+ &        (topp,siz1,buff_tmp1,missing_val,nbindex,ijndex, &
+ &           scal,req_sz,buff_tmp2(ist:ist+req_sz-1))
+        ENDDO
+    ENDDO
+  ELSE
+    CALL ipslerr (3,'restput_opp_r3d', &
+ &    'The operation you wish to do on the variable for the ', &
+ &    'restart file is not allowed.',topp)
+  ENDIF
+!-
+  list_dims = (/ ax_infs(fid,RESTART_DIM_X,1,1),ax_infs(fid,RESTART_DIM_Y,1,1),jjm,llm /)
+!-
+  CALL restput_real &
+ & (fid,vname_q,list_dims,itau,buff_tmp2)
+!-----------------------------
+  IF (ALLOCATED(buff_tmp1)) DEALLOCATE(buff_tmp1)
+  IF (ALLOCATED(buff_tmp2)) DEALLOCATE(buff_tmp2)
+END SUBROUTINE restput_opp_r3d
+!===
+SUBROUTINE restput_opp_r4d &
+ & (fid,vname_q,iim,jjm,llm,mmm,itau,var,MY_OPERATOR,nbindex,ijndex)
+!---------------------------------------------------------------------
+!- This subroutine is the interface to restput_real which allows
+!- to re-index data onto the original grid of the restart file.
+!- The logic we use is still fuzzy in my mind but that is probably
+!- only because I have not yet though through everything.
+!-
+!- In the case iim = nbindex it means that the user attempts
+!- to project the first dimension of the matrix back onto a 3D field
+!- where jjm will be the third dimension.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid
+  CHARACTER(LEN=*) :: vname_q
+  INTEGER :: iim,jjm,llm,mmm,itau
+  REAL :: var(:,:,:,:)
+  CHARACTER(LEN=*) :: MY_OPERATOR
+  INTEGER :: nbindex,ijndex(nbindex)
+!-
+  INTEGER :: jj,ll,mm,req_sz,ist,siz1
+  REAL :: scal
+  CHARACTER(LEN=7) :: topp
+  LOGICAL :: l_dbg
+  INTEGER :: list_dims(5)
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+! 0.0 What size should be the data in the file
+!-
+  req_sz = 1
+  IF ( nbindex == iim) THEN
+    IF (ax_infs(fid,RESTART_DIM_X,1,1) > 0) req_sz = req_sz*ax_infs(fid,RESTART_DIM_X,1,1)
+    IF (ax_infs(fid,RESTART_DIM_Y,1,1) > 0) req_sz = req_sz*ax_infs(fid,RESTART_DIM_Y,1,1)
+  ELSE
+    CALL ipslerr (3,'restput_opp_r4d', &
+      'Unable to performe an operation on this variable as it has', &
+      'a second and third dimension',vname_q)
+  ENDIF
+!-
+  IF (jjm < 1 .OR. llm < 1 .OR. mmm < 1) THEN
+    CALL ipslerr (3,'restput_opp_r4d', &
+      'Please specify a second dimension which is the', &
+      'layer on which the operations are performed',vname_q)
+  ENDIF
+!-
+! 1.0 Allocate the temporary buffer we need
+!     to put the variable in right dimension
+!-
+  siz1 = SIZE(var,1)
+  CALL rest_alloc (1,siz1,l_dbg,'restput_opp_r4d')
+  CALL rest_alloc (2,req_sz*jjm*llm*mmm,l_dbg,'restput_opp_r4d')
+!-
+! 2.0 We do the operation needed.
+!     It can only be a re-indexing operation.
+!     You would not want to change the values in a restart file or ?
+!-
+  topp = MY_OPERATOR(1:MIN(LEN_TRIM(MY_OPERATOR),7))
+!-
+  IF (INDEX(indchfun,topp(:LEN_TRIM(topp))) > 0) THEN
+    scal = missing_val
+    DO mm = 1,mmm
+      DO ll = 1,llm
+        DO jj = 1,jjm
+          buff_tmp1(1:siz1) = var(:,jj,ll,mm)
+          ist = ((((mm-1)*llm + (ll-1))*jjm) + (jj-1))*req_sz+1
+          CALL mathop &
+ &        (topp,siz1,buff_tmp1,missing_val,nbindex,ijndex, &
+ &           scal,req_sz,buff_tmp2(ist:ist+req_sz-1))
+        ENDDO
+      ENDDO
+    ENDDO
+  ELSE
+    CALL ipslerr (3,'restput_opp_r4d', &
+ &    'The operation you wish to do on the variable for the ', &
+ &    'restart file is not allowed.',topp)
+  ENDIF
+!-
+  list_dims = (/ ax_infs(fid,RESTART_DIM_X,1,1),ax_infs(fid,RESTART_DIM_Y,1,1),jjm,llm,mmm /)
+!-
+  CALL restput_real &
+ & (fid,vname_q,list_dims,itau,buff_tmp2)
+!-----------------------------
+  IF (ALLOCATED(buff_tmp1)) DEALLOCATE(buff_tmp1)
+  IF (ALLOCATED(buff_tmp2)) DEALLOCATE(buff_tmp2)
+END SUBROUTINE restput_opp_r4d
+!===
+SUBROUTINE restput_opp_r5d &
+ & (fid,vname_q,iim,jjm,llm,mmm,nnm,itau,var,MY_OPERATOR,nbindex,ijndex)
+!---------------------------------------------------------------------
+!- This subroutine is the interface to restput_real which allows
+!- to re-index data onto the original grid of the restart file.
+!- The logic we use is still fuzzy in my mind but that is probably
+!- only because I have not yet though through everything.
+!-
+!- In the case iim = nbindex it means that the user attempts
+!- to project the first dimension of the matrix back onto a 3D field
+!- where jjm will be the third dimension.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid
+  CHARACTER(LEN=*) :: vname_q
+  INTEGER :: iim,jjm,llm,mmm,nnm,itau
+  REAL :: var(:,:,:,:,:)
+  CHARACTER(LEN=*) :: MY_OPERATOR
+  INTEGER :: nbindex,ijndex(nbindex)
+!-
+  INTEGER :: jj,ll,mm,nn,req_sz,ist,siz1
+  REAL :: scal
+  CHARACTER(LEN=7) :: topp
+  LOGICAL :: l_dbg
+  INTEGER :: list_dims(6)
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+! 0.0 What size should be the data in the file
+!-
+  req_sz = 1
+  IF ( nbindex == iim) THEN
+    IF (ax_infs(fid,RESTART_DIM_X,1,1) > 0) req_sz = req_sz*ax_infs(fid,RESTART_DIM_X,1,1)
+    IF (ax_infs(fid,RESTART_DIM_Y,1,1) > 0) req_sz = req_sz*ax_infs(fid,RESTART_DIM_Y,1,1)
+  ELSE
+    CALL ipslerr (3,'restput_opp_r5d', &
+      'Unable to performe an operation on this variable as it has', &
+      'a second and third dimension',vname_q)
+  ENDIF
+!-
+  IF (jjm < 1 .OR. llm < 1 .OR. mmm < 1 .OR. nnm < 1) THEN
+    CALL ipslerr (3,'restput_opp_r5d', &
+      'Please make sure all dimenensions are at least 1', &
+      '',vname_q)
+  ENDIF
+!-
+! 1.0 Allocate the temporary buffer we need
+!     to put the variable in right dimension
+!-
+  siz1 = SIZE(var,1)
+  CALL rest_alloc (1,siz1,l_dbg,'restput_opp_r5d')
+  CALL rest_alloc (2,req_sz*jjm*llm*mmm*nnm,l_dbg,'restput_opp_r5d')
+!-
+! 2.0 We do the operation needed.
+!     It can only be a re-indexing operation.
+!     You would not want to change the values in a restart file or ?
+!-
+  topp = MY_OPERATOR(1:MIN(LEN_TRIM(MY_OPERATOR),7))
+!-
+  IF (INDEX(indchfun,topp(:LEN_TRIM(topp))) > 0) THEN
+    scal = missing_val
+    DO nn = 1, nnm
+      DO mm = 1,mmm
+        DO ll = 1,llm
+          DO jj = 1,jjm
+            buff_tmp1(1:siz1) = var(:,jj,ll,mm,nn)
+            ist = ((((((nn-1) * mmm) + (mm-1))*llm + (ll-1))*jjm) + (jj-1))*req_sz+1
+            CALL mathop &
+ &          (topp,siz1,buff_tmp1,missing_val,nbindex,ijndex, &
+ &             scal,req_sz,buff_tmp2(ist:ist+req_sz-1))
+          ENDDO
+        ENDDO
+      ENDDO
+    ENDDO
+  ELSE
+    CALL ipslerr (3,'restput_opp_r5d', &
+ &    'The operation you wish to do on the variable for the ', &
+ &    'restart file is not allowed.',topp)
+  ENDIF
+!-
+  list_dims = (/ ax_infs(fid,RESTART_DIM_X,1,1),ax_infs(fid,RESTART_DIM_Y,1,1),jjm,llm,mmm,nnm /)
+!-
+  CALL restput_real &
+ & (fid,vname_q,list_dims,itau,buff_tmp2)
+!-----------------------------
+  IF (ALLOCATED(buff_tmp1)) DEALLOCATE(buff_tmp1)
+  IF (ALLOCATED(buff_tmp2)) DEALLOCATE(buff_tmp2)
+END SUBROUTINE restput_opp_r5d
+!===
+SUBROUTINE restput_r1d (fid,vname_q,iim,jjm,llm,itau,var)
+!---------------------------------------------------------------------
+!- This subroutine serves as an interface to restput_real
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid
+  CHARACTER(LEN=*) :: vname_q
+  INTEGER :: iim,jjm,llm,itau
+  REAL :: var(:)
+!-
+  INTEGER :: ji,jl,req_sz,var_sz,siz1
+  CHARACTER(LEN=70) :: str,str2
+  LOGICAL :: l_dbg
+  INTEGER :: list_dims(1)
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+! 1.0 Allocate the temporary buffer we need
+!     to put the variable in right dimension
+!-
+  list_dims(1) = iim
+  siz1 = SIZE(var)
+  var_sz = siz1
+  CALL rest_alloc (1,var_sz,l_dbg,'restput_r1d')
+!-
+! 2.0 Here we could check if the sizes specified agree
+!     with the size of the variable provided
+!-
+  req_sz = 1
+  IF (iim > 0) req_sz = req_sz*iim
+  IF (jjm > 0) req_sz = req_sz*jjm
+  IF (llm > 0) req_sz = req_sz*llm
+  IF (req_sz > var_sz) THEN
+    WRITE(str, &
+ &    '("Size of variable put to the file should be ",I6)') req_sz
+    WRITE(str2, &
+ &    '("but the provided variable is of size ",I6)') var_sz
+    CALL ipslerr (3,'restput_r1d',str,str2,' ')
+  ENDIF
+  IF (req_sz < var_sz) THEN
+    WRITE(str,'("the size of variable put to the file is ",I6)') req_sz
+    WRITE(str2,'("but the provided variable is larger ",I6)') var_sz
+    CALL ipslerr (2,'restput_r1d', &
+      'There could be a problem here :',str,str2)
+  ENDIF
+!-
+! 4.0 Transfer the buffer obtained from the restart file
+!     into the variable the model expects
+!-
+  jl=0
+  DO ji=1,siz1
+    jl=jl+1
+    buff_tmp1(jl) = var(ji)
+  ENDDO
+!-
+  CALL restput_real (fid,vname_q,list_dims,itau,buff_tmp1)
+!-------------------------
+  IF (ALLOCATED(buff_tmp1)) DEALLOCATE(buff_tmp1)
+  IF (ALLOCATED(buff_tmp2)) DEALLOCATE(buff_tmp2)
+END SUBROUTINE restput_r1d
+!===
+SUBROUTINE restput_r2d (fid,vname_q,iim,jjm,llm,itau,var)
+!---------------------------------------------------------------------
+!- This subroutine serves as an interface to restput_real
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid
+  CHARACTER(LEN=*) :: vname_q
+  INTEGER :: iim,jjm,llm,itau
+  REAL :: var(:,:)
+!-
+  INTEGER :: ji,jj,jl,req_sz,var_sz,siz1,siz2
+  CHARACTER(LEN=70) :: str,str2
+  LOGICAL :: l_dbg
+  INTEGER :: list_dims(2)
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+! 1.0 Allocate the temporary buffer we need
+!     to put the variable in right dimension
+!-
+  list_dims = (/ iim,jjm /)
+  siz1 = SIZE(var,1)
+  siz2 = SIZE(var,2)
+  var_sz = siz1*siz2
+  CALL rest_alloc (1,var_sz,l_dbg,'restput_r2d')
+!-
+! 2.0 Here we could check if the sizes specified agree
+!     with the size of the variable provided
+!-
+  req_sz = 1
+  IF (iim > 0) req_sz = req_sz*iim
+  IF (jjm > 0) req_sz = req_sz*jjm
+  IF (llm > 0) req_sz = req_sz*llm
+  IF (req_sz > var_sz) THEN
+    WRITE(str, &
+&         '("Size of variable put to the file should be ",I6)') req_sz
+    WRITE(str2,'("but the provided variable is of size  ",I6)') var_sz
+    CALL ipslerr (3,'restput_r2d',str,str2,' ')
+  ENDIF
+  IF (req_sz < var_sz) THEN
+    WRITE(str,'("the size of variable put to the file is ",I6)') req_sz
+    WRITE(str2,'("but the provided variable is larger ",I6)')  var_sz
+    CALL ipslerr (2,'restput_r2d', &
+      'There could be a problem here :',str,str2)
+  ENDIF
+!-
+! 4.0 Transfer the buffer obtained from the restart file
+!     into the variable the model expects
+!-
+  jl=0
+  DO jj=1,siz2
+    DO ji=1,siz1
+      jl=jl+1
+      buff_tmp1(jl) = var(ji,jj)
+    ENDDO
+  ENDDO
+!-
+  CALL restput_real(fid,vname_q,list_dims,itau,buff_tmp1)
+!-------------------------
+  IF (ALLOCATED(buff_tmp1)) DEALLOCATE(buff_tmp1)
+  IF (ALLOCATED(buff_tmp2)) DEALLOCATE(buff_tmp2)
+END SUBROUTINE restput_r2d
+!===
+SUBROUTINE restput_r3d (fid,vname_q,iim,jjm,llm,itau,var)
+!---------------------------------------------------------------------
+!- This subroutine serves as an interface to restput_real
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER :: fid
+  CHARACTER(LEN=*) :: vname_q
+  INTEGER :: iim,jjm,llm,itau
+  REAL :: var(:,:,:)
+!-
+  INTEGER :: ji,jj,jk,jl,req_sz,var_sz,siz1,siz2,siz3
+  CHARACTER(LEN=70) :: str,str2
+  LOGICAL :: l_dbg
+  INTEGER :: list_dims(3)
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+! 1.0 Allocate the temporary buffer we need
+!     to put the variable in right dimension
+!-
+  list_dims = (/ iim, jjm, llm /)
+  siz1 = SIZE(var,1)
+  siz2 = SIZE(var,2)
+  siz3 = SIZE(var,3)
+  var_sz = siz1*siz2*siz3
+  CALL rest_alloc (1,var_sz,l_dbg,'restput_r3d')
+!-
+! 2.0 Here we could check if the sizes specified agree
+!     with the size of the variable provided
+!-
+  req_sz = 1
+  IF (iim > 0) req_sz = req_sz*iim
+  IF (jjm > 0) req_sz = req_sz*jjm
+  IF (llm > 0) req_sz = req_sz*llm
+  IF (req_sz > var_sz) THEN
+    WRITE(str, &
+ &    '("Size of variable put to the file should be ",I6)') req_sz
+    WRITE(str2, &
+ &    '("but the provided variable is of size  ",I6)')  var_sz
+    CALL ipslerr (3,'restput_r3d',str,str2,' ')
+  ENDIF
+  IF (req_sz < var_sz) THEN
+    WRITE(str,'("the size of variable put to the file is ",I6)') req_sz
+    WRITE(str2,'("but the provided variable is larger ",I6)')  var_sz
+    CALL ipslerr (2,'restput_r3d', &
+      'There could be a problem here :',str,str2)
+  ENDIF
+!-
+! 4.0 Transfer the buffer obtained from the restart file
+!     into the variable the model expects
+!-
+  jl=0
+  DO jk=1,siz3
+    DO jj=1,siz2
+      DO ji=1,siz1
+        jl=jl+1
+        buff_tmp1(jl) = var(ji,jj,jk)
+      ENDDO
+    ENDDO
+  ENDDO
+!-
+  CALL restput_real(fid,vname_q,list_dims,itau,buff_tmp1)
+!-------------------------
+  IF (ALLOCATED(buff_tmp1)) DEALLOCATE(buff_tmp1)
+  IF (ALLOCATED(buff_tmp2)) DEALLOCATE(buff_tmp2)
+END SUBROUTINE restput_r3d
+!===
+SUBROUTINE restput_real (fid,vname_q,list_dims,itau,var)
+!---------------------------------------------------------------------
+!- This subroutine will put a variable into the restart file.
+!- But it will do a lot of other things if needed :
+!- - Open a file if non is opened for this time-step
+!-   and all variables were written.
+!- - Add an axis if needed
+!- - verify that the variable has the right time step for this file
+!- - If it is time for a new file then it is opened
+!-   and the old one closed
+!- This requires that variables read from the last restart file were all
+!- written
+!-
+!- INPUT
+!-
+!- fid         : Id of the file in which we will write the variable
+!- vname_q     : Name of the variable to be written
+!- list_dim    : Size of the variable (1 to RESTART_MAX_DIMS)
+!- itau        : Time step at which the variable is written
+!- var         : Variable
+!-
+!- OUTPUT
+!-
+!- NONE
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*) :: vname_q
+  INTEGER :: fid,itau
+  INTEGER,DIMENSION(:),INTENT(in) :: list_dims
+  REAL :: var(:)
+!-
+  INTEGER :: iret,vid,ncid,iv,vnb,itedim
+  INTEGER :: ierr
+  REAL :: secsince,one_day,one_year
+  INTEGER :: ndims
+  INTEGER,DIMENSION(RESTART_MAX_DIMS) :: corner,edge
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+! 0.0 Get some variables
+!-
+  ncid = netcdf_id(fid,2)
+  IF (netcdf_id(fid,2) < 0) THEN
+    CALL ipslerr (3,'restput', &
+ &    'The output restart file is undefined.',' ',' ')
+  ENDIF
+  CALL ioget_calendar (one_year,one_day)
+!-
+! 0.0 show arguments
+  IF (l_dbg) WRITE(ipslout,*) 'RESTPUT 0.0 : ',netcdf_name(fid,2),vname_q,list_dims,itau
+!-
+! 1.0 Check if the variable is already present
+!-
+  IF (l_dbg) WRITE(ipslout,*) 'RESTPUT 1.0 : ',TRIM(vname_q)
+!-
+  CALL find_str (varname_out(fid,1:nbvar_out(fid)),vname_q,vnb)
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) 'RESTPUT 1.1 : ',varname_out(fid,1:nbvar_out(fid)),vnb
+  ENDIF
+!-
+! 2.0 If variable is not present then declare it
+!     and add extra dimensions if needed.
+!-
+  IF (vnb <= 0) THEN
+    CALL restdefv (fid,vname_q,list_dims,.TRUE.)
+    vnb = nbvar_out(fid)
+  ENDIF
+  vid = varid_out(fid,vnb)
+!-
+  IF (l_dbg) WRITE(ipslout,*) 'RESTPUT 2.0 : ',vnb,vid
+!-
+! 2.1 Is this file already in write mode ?
+!     If itau_out is still negative then we have
+!     never written to it and we need to go into write mode.
+!-
+  IF (itau_out(fid) < 0) THEN
+    iret = NF90_ENDDEF(ncid)
+  ENDIF
+!-
+! 3.0 Is this itau already on the axis ?
+!     If not then check that all variables of previous time is OK.
+!-
+  IF (l_dbg) WRITE(ipslout,*) 'RESTPUT 3.0 : ',itau,itau_out(fid)
+!-
+  IF (itau /= itau_out(fid)) THEN
+!---
+!-- If it is the first time step written on the restart
+!-- then we only check the number
+!-- Else we see if every variable was written
+!---
+    IF (tstp_out(fid) == 0) THEN
+      IF (nbvar_out(fid) < nbvar_read(fid)) THEN
+        WRITE(ipslout,*) "ERROR :",tstp_out(fid), &
+                   nbvar_out(fid),nbvar_read(fid)
+        CALL ipslerr (1,'restput', &
+ &        'There are fewer variables read from the output file', &
+ &        'than written onto the input file.', &
+ &        'We trust you know what you are doing')
+      ENDIF
+    ELSE
+      ierr = 0
+      DO iv=1,nbvar_out(fid)
+        IF (.NOT.touched_out(fid,iv)) ierr = ierr+1
+      ENDDO
+      IF (ierr > 0) THEN
+        WRITE(ipslout,*) "ERROR :",nbvar_out(fid)
+        CALL ipslerr (1,'restput', &
+ &        'There are fewer variables in the output file for this', &
+ &        'time step than for the previous one',' ')
+      ELSE
+        touched_out(fid,:) = .FALSE.
+      ENDIF
+    ENDIF
+!---
+    secsince = itau*deltat(fid)
+    corner(1) =  tstp_out(fid)+1
+    edge(1) = 1
+!---
+!-- 3.1 Here we add the values to the time axes
+!---
+    IF (l_dbg) THEN
+      WRITE(ipslout,*) 'RESTPUT 3.1 : ',itau,secsince,corner(1),edge(1)
+    ENDIF
+!---
+    iret = NF90_PUT_VAR(ncid,tind_varid_out(fid),itau, &
+ &                      start=corner(1:1))
+    iret = NF90_PUT_VAR(ncid,tax_varid_out(fid),secsince, &
+ &                      start=corner(1:1))
+!---
+    tstp_out(fid) = tstp_out(fid)+1
+    itau_out(fid) = itau
+  ENDIF
+!-
+! 4.0 Variable and time step should be present
+!     now so we can dump variable
+!-
+  ndims = 0
+  DO itedim=1, SIZE(list_dims,1)
+    ndims = ndims+1
+    corner(ndims) = 1
+    edge(ndims) = list_dims(itedim)
+  ENDDO
+
+  ndims = ndims+1
+  corner(ndims) = tstp_out(fid)
+  edge(ndims) = 1
+!-
+  iret = NF90_PUT_VAR(ncid,vid,var, &
+ &                    start=corner(1:ndims),count=edge(1:ndims))
+!-
+  IF (iret /= NF90_NOERR) THEN
+    CALL ipslerr (3,'restput_real',NF90_STRERROR(iret), &
+ &    'Bug in restput.',&
+ &    'Please, verify compatibility between get and put commands.')
+  ENDIF
+!-
+!  5.0 Note that the variables was treated
+!-
+  touched_out(fid,vnb) = .TRUE.
+!---------------------------
+END  SUBROUTINE restput_real
+!===
+SUBROUTINE restdefv (fid,varname,list_dims,write_att)
+!---------------------------------------------------------------------
+! This subroutine adds a variable to the output file.
+! The attributes are either taken from.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER ::fid
+  CHARACTER(LEN=*) :: varname
+! define its dimension size, this must respect same order as RESTART_DIMS_NAMES
+! x,y,z,....
+  INTEGER, DIMENSION(:),INTENT(in) :: list_dims  ! Dimensions input
+  LOGICAL :: write_att
+!-
+  INTEGER :: dims(RESTART_MAX_DIMS),ic,xloc,ndim,ncfid
+  INTEGER :: iret,ax_id,ite
+  CHARACTER(LEN=3) :: str
+  LOGICAL :: l_dbg
+
+  CHARACTER(len=clen+10) :: str1, str2
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!- Check consistency
+!-
+  IF (ANY(list_dims < 1)) THEN
+    CALL ipslerr (3,'restdefv', &
+      'All defined dimension sizes must be bigger than 0', &
+      'Please check list_dims argument',' ')
+  ENDIF
+!-
+  IF (SIZE(list_dims,1) == 0) THEN
+    CALL ipslerr (3,'restdefv', &
+      'You must add at least one dimension', &
+      'Please check list_dims argument',' ')
+  ENDIF
+!-
+  IF (SIZE(list_dims,1) > RESTART_MAX_DIMS) THEN
+    CALL ipslerr (3,'restdefv', &
+      'Maximum limit of dimensions in 1 variable reached', &
+      'Please increase RESTART_MAX_DIM value',' ')
+  ENDIF
+!-
+  ncfid = netcdf_id(fid,2)
+  IF (nbvar_out(fid) >= max_var) THEN
+    CALL ipslerr (3,'restdefv', &
+      'Too many variables for the restcom module', &
+      'Please increase the value of max_var',' ')
+  ENDIF
+  nbvar_out(fid) = nbvar_out(fid)+1
+  IF (len(trim(varname)) .GT. clen) THEN
+    write(str1,*) "Maximum length allowed: ", clen
+    write(str1,*) "But found: ", len(trim(varname))
+    CALL ipslerr (3,'restdefv', &
+      'Variable name too long for variable '//trim(varname), &
+        str1, str2)
+  ENDIF
+
+  varname_out(fid,nbvar_out(fid)) = varname
+!-
+! 0.0 Put the file in define mode if needed
+!-
+  IF (itau_out(fid) >= 0) THEN
+    iret = NF90_REDEF(ncfid)
+    IF (l_dbg) THEN
+       WRITE(ipslout,*) 'restdefv 0.0 : REDEF',itau_out(fid)
+    ENDIF
+  ENDIF
+!-
+! 1.0 Do we have all dimensions and can we go ahead
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) 'restdefv 1.0 :',TRIM(varname),nbvar_out(fid)
+  ENDIF
+!-
+  ndim = 0
+!-
+! 1.1 Work on each dimension
+!-
+  DO ite=1, SIZE(list_dims, 1)  
+    IF (list_dims(ite) > 0) THEN
+      ndim = ndim+1
+      xloc = 0
+      DO ic=1,ax_nb(fid, ite)
+        IF (ax_infs(fid,ite,ic,1) == list_dims(ite)) xloc = ic
+      ENDDO
+!---
+      IF (xloc > 0) THEN
+        dims(ndim) = ax_infs(fid,ite,xloc,2)
+      ELSE
+        str=RESTART_DIMS_NAMES(ite)//'_'//CHAR(96+ax_nb(fid,ite))
+        iret = NF90_DEF_DIM(ncfid,str,list_dims(ite),ax_id)
+        ax_nb(fid,ite) = ax_nb(fid,ite)+1
+        ax_infs(fid,ite,ax_nb(fid,ite),1) = list_dims(ite)
+        ax_infs(fid,ite,ax_nb(fid,ite),2) = ax_id
+        dims(ndim) = ax_id
+      ENDIF
+    ENDIF
+  ENDDO
+!-
+! 1.4  Time needs to be added
+!-
+  ndim = ndim+1
+  dims(ndim) = tdimid_out(fid)
+!-
+! 2.0  Declare the variable
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) 'restdefv 2.0 :',ndim,' :: ',dims(1:ndim),tdimid_out(fid)
+  ENDIF
+!-
+  iret = NF90_DEF_VAR(ncfid,varname,NF90_DOUBLE,dims(1:ndim), &
+ &                    varid_out(fid,nbvar_out(fid)))
+  IF (iret /= NF90_NOERR) THEN
+    CALL ipslerr (3,'restdefv', &
+      'Could not define new variable in file', &
+      NF90_STRERROR(iret),varname)
+  ENDIF
+!-
+! 3.0 Add the attributes if requested
+!-
+  IF (write_att) THEN
+    IF (rest_units /= 'XXXXX') THEN
+      iret =  NF90_PUT_ATT(ncfid,varid_out(fid,nbvar_out(fid)), &
+ &                         'units',TRIM(rest_units))
+      rest_units = 'XXXXX'
+    ENDIF
+!---
+    IF (rest_lname /= 'XXXXX') THEN
+      iret =  NF90_PUT_ATT(ncfid,varid_out(fid,nbvar_out(fid)), &
+ &                         'long_name',TRIM(rest_lname))
+      rest_lname = 'XXXXX'
+    ENDIF
+!---
+    iret = NF90_PUT_ATT(ncfid,varid_out(fid,nbvar_out(fid)), &
+ &                      'missing_value',REAL(missing_val,KIND=4))
+!---
+!   Compression
+    IF (IS_COMPRESSION_ENABLED) THEN
+      iret = NF90_DEF_VAR_DEFLATE(ncfid,varid_out(fid,nbvar_out(fid)),0,1,1) !DEFLATE_LEVEL_1
+      IF (iret /= NF90_NOERR) CALL ipslerr (3,'restdefv', &
+        'Could not add compression to new variable in file', &
+        NF90_STRERROR(iret),varname)
+    ENDIF
+!---
+    IF (itau_out(fid) >= 0) THEN
+      iret = NF90_ENDDEF(ncfid)
+    ENDIF
+  ENDIF
+!-
+  IF (l_dbg) THEN
+    WRITE(ipslout,*) &
+ &    'restdefv 3.0 : LIST OF VARS ',varname_out(fid,1:nbvar_out(fid))
+  ENDIF
+!----------------------
+END SUBROUTINE restdefv
+!===
+SUBROUTINE rest_atim (l_msg,c_p)
+!---------------------------------------------------------------------
+! Called by "c_p", [re]allocate the time axes
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  LOGICAL,INTENT(IN) :: l_msg
+  CHARACTER(LEN=*),INTENT(IN) :: c_p
+!-
+  INTEGER :: i_err,tszij
+  INTEGER,ALLOCATABLE :: tmp_index(:,:)
+  REAL,ALLOCATABLE :: tmp_julian(:,:)
+!---------------------------------------------------------------------
+!-
+!  Allocate the space we need for the time axes
+!-
+  IF (.NOT.ALLOCATED(t_index) .AND. .NOT.ALLOCATED(t_julian)) THEN
+    IF (l_msg) THEN
+      WRITE(ipslout,*) TRIM(c_p)//' : Allocate times axes at :', &
+ &               max_file,tax_size_in(nb_fi)
+    ENDIF
+!---
+    ALLOCATE(t_index(max_file,tax_size_in(nb_fi)),STAT=i_err)
+    IF (i_err/=0) THEN
+      WRITE(ipslout,*) "ERROR IN ALLOCATION of t_index : ",i_err
+      CALL ipslerr (3,TRIM(c_p), &
+ &      'Problem in allocation of t_index','', &
+ &      '(you must increase memory)')
+    ENDIF
+    t_index (:,:) = 0
+!---
+    ALLOCATE(t_julian(max_file,tax_size_in(nb_fi)),STAT=i_err)
+    IF (i_err/=0) THEN
+      WRITE(ipslout,*) "ERROR IN ALLOCATION of t_julian : ",i_err
+      CALL ipslerr (3,TRIM(c_p), &
+ &      'Problem in allocation of max_file,tax_size_in','', &
+ &      '(you must increase memory)')
+    ENDIF
+    t_julian (:,:) = 0.0
+  ELSE IF (    (SIZE(t_index,DIM=2)  < tax_size_in(nb_fi)) &
+ &         .OR.(SIZE(t_julian,DIM=2) < tax_size_in(nb_fi)) ) THEN
+    IF (l_msg) THEN
+      WRITE(ipslout,*) TRIM(c_p)//' : Reallocate times axes at :', &
+ &               max_file,tax_size_in(nb_fi)
+    ENDIF
+!---
+    ALLOCATE (tmp_index(max_file,tax_size_in(nb_fi)),STAT=i_err)
+    IF (i_err/=0) THEN
+      WRITE(ipslout,*) "ERROR IN ALLOCATION of tmp_index : ",i_err
+      CALL ipslerr (3,TRIM(c_p), &
+ &      'Problem in allocation of tmp_index','', &
+ &      '(you must increase memory)')
+    ENDIF
+    tszij = SIZE(t_index,DIM=2)
+    tmp_index(:,1:tszij) = t_index(:,1:tszij)
+    DEALLOCATE(t_index)
+    ALLOCATE (t_index(max_file,tax_size_in(nb_fi)),STAT=i_err)
+    IF (i_err/=0) THEN
+      WRITE(ipslout,*) "ERROR IN ALLOCATION of t_index : ",i_err
+      CALL ipslerr (3,TRIM(c_p), &
+ &     'Problem in reallocation of t_index','', &
+ &     '(you must increase memory)')
+    ENDIF
+    t_index(:,1:tszij) = tmp_index(:,1:tszij)
+!---
+    ALLOCATE (tmp_julian(max_file,tax_size_in(nb_fi)),STAT=i_err)
+    IF (i_err/=0) THEN
+      WRITE(ipslout,*) "ERROR IN ALLOCATION of tmp_julian : ",i_err
+      CALL ipslerr (3,TRIM(c_p), &
+ &     'Problem in allocation of tmp_julian','', &
+ &     '(you must increase memory)')
+    ENDIF
+    tszij = SIZE(t_julian,DIM=2)
+    tmp_julian(:,1:tszij) = t_julian(:,1:tszij)
+    DEALLOCATE(t_julian)
+    ALLOCATE (t_julian(max_file,tax_size_in(nb_fi)),STAT=i_err)
+    IF (i_err/=0) THEN
+      WRITE(ipslout,*) "ERROR IN ALLOCATION of t_julian : ",i_err
+      CALL ipslerr (3,TRIM(c_p), &
+ &      'Problem in reallocation of t_julian','', &
+ &      '(you must increase memory)')
+    ENDIF
+    t_julian(:,1:tszij) = tmp_julian(:,1:tszij)
+  ENDIF
+!-----------------------
+END SUBROUTINE rest_atim
+!===
+SUBROUTINE rest_alloc (i_buff,i_qsz,l_msg,c_p)
+!---------------------------------------------------------------------
+! Called by "c_p", allocate a temporary buffer
+! (buff_tmp[1/2] depending on "i_buff" value) to the size "i_qsz".
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: i_buff,i_qsz
+  LOGICAL,INTENT(IN) :: l_msg
+  CHARACTER(LEN=*),INTENT(IN) :: c_p
+!-
+  INTEGER :: i_bsz,i_err
+  LOGICAL :: l_alloc1,l_alloc2
+  CHARACTER(LEN=9) :: cbn
+  CHARACTER(LEN=5) :: c_err
+!---------------------------------------------------------------------
+  IF      (i_buff == 1) THEN
+    IF (ALLOCATED(buff_tmp1)) THEN
+      i_bsz = SIZE(buff_tmp1)
+    ELSE
+      i_bsz = 0
+    ENDIF
+    l_alloc1 =    (.NOT.ALLOCATED(buff_tmp1)) &
+ &            .OR.((ALLOCATED(buff_tmp1)).AND.(i_qsz > i_bsz))
+    l_alloc2 = .FALSE.
+    cbn = 'buff_tmp1'
+  ELSE IF (i_buff == 2) THEN
+    IF (ALLOCATED(buff_tmp2)) THEN
+      i_bsz = SIZE(buff_tmp2)
+    ELSE
+      i_bsz = 0
+    ENDIF
+    l_alloc1 = .FALSE.
+    l_alloc2 =    (.NOT.ALLOCATED(buff_tmp2)) &
+ &            .OR.((ALLOCATED(buff_tmp2)).AND.(i_qsz > i_bsz))
+    cbn = 'buff_tmp2'
+  ELSE
+    CALL ipslerr (3,'rest_alloc', &
+ &    'Called by '//TRIM(c_p),'with a wrong value of i_buff','')
+  ENDIF
+!-
+!-
+  IF (l_alloc1.OR.l_alloc2) THEN
+    IF (l_msg) THEN
+      IF (    (l_alloc1.AND.ALLOCATED(buff_tmp1)) &
+ &        .OR.(l_alloc2.AND.ALLOCATED(buff_tmp2)) ) THEN
+        WRITE(ipslout,*) TRIM(c_p)//" : re_allocate "//TRIM(cbn)//"=",i_qsz
+      ELSE
+        WRITE(ipslout,*) TRIM(c_p)//" : allocate "//TRIM(cbn)//"=",i_qsz
+      ENDIF
+    ENDIF
+    IF (l_alloc1) THEN
+      IF (ALLOCATED(buff_tmp1)) THEN
+        DEALLOCATE(buff_tmp1)
+      ENDIF
+      ALLOCATE (buff_tmp1(i_qsz),STAT=i_err)
+    ELSE
+      IF (ALLOCATED(buff_tmp2)) THEN
+        DEALLOCATE(buff_tmp2)
+      ENDIF
+      ALLOCATE (buff_tmp2(i_qsz),STAT=i_err)
+    ENDIF
+    IF (i_err /= 0) THEN
+      WRITE (UNIT=c_err,FMT='(I5)') i_err
+      CALL ipslerr (3,TRIM(c_p), &
+ &      'Problem in allocation of',TRIM(cbn), &
+ &      'Error : '//TRIM(c_err)//' (you must increase memory)')
+    ENDIF
+  ENDIF
+!------------------------
+END SUBROUTINE rest_alloc
+!===
+SUBROUTINE ioconf_setatt (attname,value)
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*) :: attname,value
+!-
+  CHARACTER(LEN=LEN_TRIM(attname)) :: tmp_str
+!---------------------------------------------------------------------
+  tmp_str = attname
+  CALL strlowercase (tmp_str)
+!-
+  SELECT CASE(tmp_str)
+    CASE('units')
+      rest_units = value
+    CASE('long_name')
+      rest_lname = value
+    CASE DEFAULT
+      CALL ipslerr (2,'ioconf_restatt', &
+        'The attribute name provided is unknown',attname,' ')
+  END SELECT
+!---------------------------
+END SUBROUTINE ioconf_setatt
+!===
+SUBROUTINE ioget_vdim (fid,vname_q,varnbdim_max,varnbdim,vardims)
+!---------------------------------------------------------------------
+!- This routine allows the user to get the dimensions
+!- of a field in the restart file.
+!- This is the file which is read.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: fid
+  CHARACTER(LEN=*) :: vname_q
+  INTEGER,INTENT(IN) :: varnbdim_max
+  INTEGER,INTENT(OUT) ::  varnbdim
+  INTEGER,DIMENSION(varnbdim_max),INTENT(OUT) :: vardims
+!-
+  INTEGER :: vnb
+!---------------------------------------------------------------------
+! Find the index of the variable
+  CALL find_str (varname_in(fid,1:nbvar_in(fid)),vname_q,vnb)
+!-
+  IF (vnb > 0) THEN
+    varnbdim = varnbdim_in(fid,vnb)
+    IF (varnbdim_max < varnbdim) THEN
+      CALL ipslerr (3,'ioget_vdim', &
+        'The provided array for the variable dimensions is too small', &
+        '','')
+    ELSE
+      vardims(1:varnbdim) = vardims_in(fid,vnb,1:varnbdim)
+    ENDIF
+  ELSE
+    varnbdim = 0
+    CALL ipslerr (2,'ioget_vdim', &
+      'Variable '//TRIM(vname_q)//' not found','','')
+  ENDIF
+!------------------------
+END SUBROUTINE ioget_vdim
+!===
+SUBROUTINE ioget_vname (fid,nbvar,varnames)
+!---------------------------------------------------------------------
+!- This routine allows the user to extract the list
+!- of variables in an opened restart file.
+!- This is the file which is read
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(IN) :: fid
+  INTEGER,INTENT(OUT) ::  nbvar
+  CHARACTER(LEN=*),INTENT(OUT) :: varnames(:)
+!---------------------------------------------------------------------
+  nbvar = nbvar_in(fid)
+!-
+  IF (SIZE(varnames) < nbvar) THEN
+    CALL ipslerr (3,'ioget_vname', &
+      'The provided array for the variable names is too small','','')
+  ELSE
+    varnames(1:nbvar) = varname_in(fid,1:nbvar)
+  ENDIF
+!-------------------------
+END SUBROUTINE ioget_vname
+!===
+SUBROUTINE ioconf_expval (new_exp_val)
+!---------------------------------------------------------------------
+!- The default value written into the variables which are not
+!- in the restart file can only be changed once.
+!- This avoids further complications.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  REAL :: new_exp_val
+!---------------------------------------------------------------------
+  IF (.NOT.lock_valexp) THEN
+    lock_valexp = .TRUE.
+    val_exp = new_exp_val
+  ELSE
+    CALL ipslerr (2,'ioconf_expval', &
+     'The default value for variable' &
+   //'not available in the restart file ', &
+     'has already been locked and can not be changed at this point', &
+     ' ')
+  ENDIF
+!---------------------------
+END SUBROUTINE ioconf_expval
+!===
+SUBROUTINE ioget_expval (get_exp_val)
+!---------------------------------------------------------------------
+!- Once the user has extracted the default value,
+!- we lock it so that it can not be changed anymore.
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  REAL :: get_exp_val
+!---------------------------------------------------------------------
+  get_exp_val = val_exp
+  lock_valexp = .TRUE.
+!--------------------------
+END SUBROUTINE ioget_expval
+!===
+SUBROUTINE restclo (fid)
+!---------------------------------------------------------------------
+!- This subroutine closes one or any opened restart file.
+!-
+!- INPUT
+!-
+!- fid    : File ID in the restcom system (not the netCDF ID)(optional)
+!-
+!- OUTPUT
+!-
+!- NONE
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  INTEGER,INTENT(in),OPTIONAL :: fid
+!-
+  INTEGER :: iret,ifnc
+  CHARACTER(LEN=6) :: n_e
+  CHARACTER(LEN=3) :: n_f
+  LOGICAL :: l_dbg
+!---------------------------------------------------------------------
+  CALL ipsldbg (old_status=l_dbg)
+!-
+  IF (PRESENT(fid)) THEN
+!---
+    IF (l_dbg) THEN
+      WRITE(ipslout,*) &
+        'restclo : Closing specified restart file number :', &
+        fid,netcdf_id(fid,1:2),netcdf_name(fid,1:2)
+    ENDIF
+!---
+    IF (netcdf_id(fid,1) > 0) THEN
+      iret = NF90_CLOSE(netcdf_id(fid,1))
+      IF (iret /= NF90_NOERR) THEN
+        WRITE (n_e,'(I6)') iret
+        WRITE (n_f,'(I3)') netcdf_id(fid,1)
+        CALL ipslerr (2,'restclo', &
+          "Error "//n_e//" in closing file : "//n_f,netcdf_name(fid,1),' ')
+      ENDIF
+      IF (netcdf_id(fid,1) == netcdf_id(fid,2)) THEN
+        netcdf_id(fid,2) = -1
+        netcdf_name(fid,2) = 'NONE'
+      ENDIF
+      netcdf_id(fid,1) = -1
+      netcdf_name(fid,1) = 'NONE'
+    ENDIF
+!---
+    IF (netcdf_id(fid,2) > 0)  THEN
+      iret = NF90_CLOSE(netcdf_id(fid,2))
+      IF (iret /= NF90_NOERR) THEN
+        WRITE (n_e,'(I6)') iret
+        WRITE (n_f,'(I3)') netcdf_id(fid,2)
+        CALL ipslerr (2,'restclo', &
+          "Error "//n_e//" in closing file : "//n_f,netcdf_name(fid,2),' ')
+      ENDIF
+      netcdf_id(fid,2) = -1
+      netcdf_name(fid,2) = 'NONE'
+    ENDIF
+!---
+  ELSE
+!---
+    IF (l_dbg) WRITE(ipslout,*) 'restclo : Closing all files'
+!---
+    DO ifnc=1,nb_fi
+      IF (netcdf_id(ifnc,1) > 0) THEN
+        iret = NF90_CLOSE(netcdf_id(ifnc,1))
+        IF (iret /= NF90_NOERR) THEN
+          WRITE (n_e,'(I6)') iret
+          WRITE (n_f,'(I3)') netcdf_id(ifnc,1)
+          CALL ipslerr (2,'restclo', &
+            "Error "//n_e//" in closing file : "//n_f,netcdf_name(ifnc,1),' ')
+        ENDIF
+        IF (l_dbg) THEN
+           WRITE(ipslout,*) &
+                'restclo : Closing specified restart file number :', &
+                ifnc,netcdf_id(ifnc,1:2),netcdf_name(ifnc,1:2)
+        ENDIF
+        IF (netcdf_id(ifnc,1) == netcdf_id(ifnc,2)) THEN
+          netcdf_id(ifnc,2) = -1
+          netcdf_name(ifnc,2) = 'NONE'
+        ENDIF
+        netcdf_id(ifnc,1) = -1
+        netcdf_name(ifnc,1) = 'NONE'
+      ENDIF
+!-----
+      IF (netcdf_id(ifnc,2) > 0) THEN
+        iret = NF90_CLOSE(netcdf_id(ifnc,2))
+        IF (iret /= NF90_NOERR) THEN
+          WRITE (n_e,'(I6)') iret
+          WRITE (n_f,'(I3)') netcdf_id(ifnc,2)
+          CALL ipslerr (2,'restclo', &
+            "Error "//n_e//" in closing file : "//n_f,netcdf_name(ifnc,2),' ')
+        END IF
+        IF (l_dbg) THEN
+           WRITE(ipslout,*) &
+                'restclo : Closing specified restart file number :', &
+                ifnc,netcdf_id(ifnc,1:2),netcdf_name(ifnc,1:2)
+        END IF
+        netcdf_id(ifnc,2) = -1
+        netcdf_name(ifnc,2) = 'NONE'
+      ENDIF
+    ENDDO
+  ENDIF
+!---------------------
+END SUBROUTINE restclo
+!===
+!-----------------
+END MODULE restcom
Index: /IOIPSL/tags/v2_2_5/src/stringop.f90
===================================================================
--- /IOIPSL/tags/v2_2_5/src/stringop.f90	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/src/stringop.f90	(revision 5058)
@@ -0,0 +1,212 @@
+MODULE stringop
+!-
+!$Id$
+!-
+! This software is governed by the CeCILL license
+! See IOIPSL/IOIPSL_License_CeCILL.txt
+!---------------------------------------------------------------------
+CHARACTER(LEN=1), PARAMETER :: COMMENT_TAG = "#" ! Comment symbol
+
+CONTAINS
+!=
+SUBROUTINE cmpblank (str)
+!---------------------------------------------------------------------
+!- Compact blanks
+!---------------------------------------------------------------------
+  CHARACTER(LEN=*),INTENT(inout) :: str
+!-
+  INTEGER :: lcc,ipb
+!---------------------------------------------------------------------
+  lcc = LEN_TRIM(str)
+  ipb = 1
+  DO
+    IF (ipb >= lcc)   EXIT
+    IF (str(ipb:ipb+1) == '  ') THEN
+      str(ipb+1:) = str(ipb+2:lcc)
+      lcc = lcc-1
+    ELSE
+      ipb = ipb+1
+    ENDIF
+  ENDDO
+!----------------------
+END SUBROUTINE cmpblank
+!===
+INTEGER FUNCTION cntpos (c_c,l_c,c_r,l_r)
+!---------------------------------------------------------------------
+!- Finds number of occurences of c_r in c_c
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*),INTENT(in) :: c_c
+  INTEGER,INTENT(IN) :: l_c
+  CHARACTER(LEN=*),INTENT(in) :: c_r
+  INTEGER,INTENT(IN) :: l_r
+!-
+  INTEGER :: ipos,indx
+!---------------------------------------------------------------------
+  cntpos = 0
+  ipos   = 1
+  DO
+    indx = INDEX(c_c(ipos:l_c),c_r(1:l_r))
+    IF (indx > 0) THEN
+      cntpos = cntpos+1
+      ipos   = ipos+indx+l_r-1
+    ELSE
+      EXIT
+    ENDIF
+  ENDDO
+!------------------
+END FUNCTION cntpos
+!===
+INTEGER FUNCTION findpos (c_c,l_c,c_r,l_r)
+!---------------------------------------------------------------------
+!- Finds position of c_r in c_c
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*),INTENT(in) :: c_c
+  INTEGER,INTENT(IN) :: l_c
+  CHARACTER(LEN=*),INTENT(in) :: c_r
+  INTEGER,INTENT(IN) :: l_r
+!---------------------------------------------------------------------
+  findpos = INDEX(c_c(1:l_c),c_r(1:l_r))
+  IF (findpos == 0)  findpos=-1
+!-------------------
+END FUNCTION findpos
+!===
+SUBROUTINE find_str (str_tab,str,pos)
+!---------------------------------------------------------------------
+!- This subroutine looks for a string in a table
+!---------------------------------------------------------------------
+!- INPUT
+!-   str_tab  : Table  of strings
+!-   str      : Target we are looking for
+!- OUTPUT
+!-   pos      : -1 if str not found, else value in the table
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*),DIMENSION(:),INTENT(in) :: str_tab
+  CHARACTER(LEN=*),INTENT(in) :: str
+  INTEGER,INTENT(out) :: pos
+!-
+  INTEGER :: nb_str,i
+!---------------------------------------------------------------------
+  pos = -1
+  nb_str=SIZE(str_tab)
+  IF ( nb_str > 0 ) THEN
+    DO i=1,nb_str
+      IF ( TRIM(str_tab(i)) == TRIM(str) ) THEN
+        pos = i
+        EXIT
+      ENDIF
+    ENDDO
+  ENDIF
+!----------------------
+END SUBROUTINE find_str
+!===
+SUBROUTINE nocomma (str)
+!---------------------------------------------------------------------
+!- Replace commas with blanks
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*) :: str
+!-
+  INTEGER :: i
+!---------------------------------------------------------------------
+  DO i=1,LEN_TRIM(str)
+    IF (str(i:i) == ',')   str(i:i) = ' '
+  ENDDO
+!---------------------
+END SUBROUTINE nocomma
+!===
+SUBROUTINE nocomment (str)
+!---------------------------------------------------------------------
+!- Delete comment part from a line
+!
+!- line: TIME_SKIP=1D   # skip one day
+! to
+!  line: TIME_SKIP=1D
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*), INTENT(INOUT) :: str
+!-
+  INTEGER :: pos
+!---------------------------------------------------------------------
+  pos = INDEX(str, COMMENT_TAG)
+  IF (pos > 0) THEN
+    IF (pos == 1) THEN
+      str=""
+    ELSE
+      str=TRIM(str(1:pos-1))
+    ENDIF
+  ENDIF
+!---------------------
+END SUBROUTINE nocomment
+!===
+SUBROUTINE strlowercase (str)
+!---------------------------------------------------------------------
+!- Converts a string into lowercase
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*) :: str
+!-
+  INTEGER :: i,ic
+!---------------------------------------------------------------------
+  DO i=1,LEN_TRIM(str)
+    ic = IACHAR(str(i:i))
+    IF ( (ic >= 65).AND.(ic <= 90) )  str(i:i) = ACHAR(ic+32)
+  ENDDO
+!--------------------------
+END SUBROUTINE strlowercase
+!===
+SUBROUTINE struppercase (str)
+!---------------------------------------------------------------------
+!- Converts a string into uppercase
+!---------------------------------------------------------------------
+  IMPLICIT NONE
+!-
+  CHARACTER(LEN=*) :: str
+!-
+  INTEGER :: i,ic
+!---------------------------------------------------------------------
+  DO i=1,LEN_TRIM(str)
+    ic = IACHAR(str(i:i))
+    IF ( (ic >= 97).AND.(ic <= 122) )  str(i:i) = ACHAR(ic-32)
+  ENDDO
+!--------------------------
+END SUBROUTINE struppercase
+!===
+SUBROUTINE str_xfw (c_string,c_word,l_ok)
+!---------------------------------------------------------------------
+!- Given a character string "c_string", of arbitrary length,
+!- returns a logical flag "l_ok" if a word is found in it,
+!- the first word "c_word" if found and the new string "c_string"
+!- without the first word "c_word"
+!---------------------------------------------------------------------
+  CHARACTER(LEN=*),INTENT(INOUT) :: c_string
+  CHARACTER(LEN=*),INTENT(OUT) :: c_word
+  LOGICAL,INTENT(OUT) :: l_ok
+!-
+  INTEGER :: i_b,i_e
+!---------------------------------------------------------------------
+  l_ok = (LEN_TRIM(c_string) > 0)
+  IF (l_ok) THEN
+    i_b = VERIFY(c_string,' ')
+    i_e = INDEX(c_string(i_b:),' ')
+    IF (i_e == 0) THEN
+      c_word = c_string(i_b:)
+      c_string = ""
+    ELSE
+      c_word = c_string(i_b:i_b+i_e-2)
+      c_string = ADJUSTL(c_string(i_b+i_e-1:))
+    ENDIF
+  ENDIF
+!---------------------
+END SUBROUTINE str_xfw
+!===
+!------------------
+END MODULE stringop
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/COPYRIGHT.txt
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/COPYRIGHT.txt	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/COPYRIGHT.txt	(revision 5058)
@@ -0,0 +1,109 @@
+!------------------------------------------------------------------------------!
+!              Flexible Configuration Management Software License              !
+!                                                                              !
+!     Please read this Software Licence as you will be bound by its terms      !
+!                           if you use the Software                            !
+!------------------------------------------------------------------------------!
+
+The Licensor:
+-------------
+
+The Met Office of FitzRoy Road, Exeter EX1 3PB, United Kingdom
+--------------------------------------------------------------------------------
+
+1. Licence.
+-----------
+
+The Met Office grants you a non-exclusive, royalty free; world-wide,
+transferable Licence to use, modify, copy and distribute the Flexible
+Configuration Management software ("the software") accompanying this License
+providing:
+
+a. you undertake to provide to the Met Office a copy of any modifications made
+   by you on the same terms contained within this licence agreement;
+
+b. modified files carry prominent notices stating that you changed the files
+   and the date of change;
+
+c. distribution of original or modified files is made free of charge under the
+   terms of this Licence;
+
+d. the appropriate copyright notices, the above copyright notice and a
+   disclaimer of warranty is included with the distribution.
+
+2. Ownership.
+-------------
+
+The Flexible Configuration Management software is Crown copyright and is
+reproduced with the permission of Met Office under delegated authority from
+the Controller of HMSO. The software and documentation are provided to you to
+allow you to exercise your rights under this License, which is granted to you.
+
+3. Duration.
+------------
+
+This license will remain in effect until terminated.
+ 
+4. Termination.
+---------------
+
+You may terminate this license at any time by removing all copies of the
+software from your system.  This License will terminate immediately without
+notice from us if you fail to comply with any of the provisions of this
+License or in the event of your breaching the terms of this licence you are
+given notice that the license has been terminated.  Upon termination you will
+delete all copies of the software and any related documentation.
+
+5. Disclaimer of Warranty.
+--------------------------
+
+a. THE MET OFFICE DISCLAIMS ALL WARRANTIES, REPRESENTATIONS AND PROMISES,
+   INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF SATISFACTORY QUALITY
+   AND FIT FOR THE PURPOSE.  NEITHER DOES THE MET OFFICE MAKE ANY
+   REPRESENTATIONS AS TO COMPATABILITY WITH YOUR OPERATING SYSTEMS AND
+   PLATFORMS.
+
+b. In no event does the Met Office warrant that the software or related
+   documentation will satisfy your requirements, that the software and
+   documentation will be without errors or defects or that the operation of
+   the software will be uninterrupted.
+
+c. IN NO EVENT WILL THE MET OFFICE BE LIABLE FOR ANY OTHER DAMAGES, INCLUDING
+   BUT NOT LIMITED TO DAMAGES FOR LOSS OF PROFITS DATA OR USE OF THE SOFTWARE
+   OR FOR ANY INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES, EVEN IF THE MET
+   OFFICE HAS BEEN SPECIFICALLY ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+6. General Provisions.
+----------------------
+
+a. You will not do anything, relating to this software that will bring the Met
+   Office into disrepute.
+
+b. You will not use the name of the Met Office or any other contributor to
+   endorse or promote any products derived from the software without the
+   written permission of the Met Office.
+
+7. Acknowledgements.
+--------------------
+
+The Fortran 9X Interface Generator found in file
+./src/lib/Ecmwf/Fortran90_stuff.pm is adapted from a script developed at ECMWF
+and is provided by kind permission of ECMWF under the same terms of this
+Licence.
+
+8. Entire Agreement.
+--------------------
+
+This License constitutes the entire agreement between us with respect to your
+rights or warranties for using the software and related documentation.  If any
+provision of this agreement is determined to be invalid or unenforceable the
+remaining provisions shall continue in full force.
+
+9. Governing Law.
+-----------------
+
+This Agreement is governed by and construed in accordance with the Laws of
+England.
+
+--------------------------------------------------------------------------------
+                                               © British Crown copyright 2006-7.
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/LICENSE.html
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/LICENSE.html	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/LICENSE.html	(revision 5058)
@@ -0,0 +1,150 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+
+<html>
+<head>
+  <title>Flexible Configuration Management Software License</title>
+  <meta name="copyright" content="&copy; British Crown Copyright">
+  <style type="text/css">
+  <!--
+  body {
+    font: normal normal 10pt Arial, sans-serif;
+  }
+
+  h1 {
+    text-align: center;
+    text-decoration: underline;
+    font: normal bold 14pt Arial, sans-serif;
+  }
+
+  h2 {
+    text-align: left;
+    text-decoration: none;
+    font: normal normal 14pt Arial, sans-serif;
+  }
+
+  h3 {
+    text-align: left;
+    text-decoration: none;
+    font: normal bold 10pt Arial, sans-serif;
+  }
+
+  .copyright {
+    text-align: right;
+    font: normal bold 11pt Arial, sans-serif;
+  }
+
+  ol li {
+    list-style: lower-alpha;
+  }
+  //-->
+  </style>
+</head>
+
+<body>
+  <h1>Flexible Configuration Management Software License</h1>
+
+  <h1>Please read this Software Licence as you will be bound by its terms if
+  you use the Software</h1>
+
+  <h2>The Licensor:</h2>
+
+  <h2>The Met Office of FitzRoy Road, Exeter EX1 3PB, United Kingdom</h2>
+
+  <h3>1. Licence.</h3>
+
+  <p>The Met Office grants you a non-exclusive, royalty free; world-wide,
+  transferable Licence to use, modify, copy and distribute the Flexible
+  Configuration Management software ("the software") accompanying this License
+  providing:</p>
+
+  <ol>
+    <li>you undertake to provide to the Met Office a copy of any modifications
+    made by you on the same terms contained within this licence agreement;</li>
+
+    <li>modified files carry prominent notices stating that you changed the
+    files and the date of change;</li>
+
+    <li>distribution of original or modified files is made free of charge under
+    the terms of this Licence;</li>
+
+    <li>the appropriate copyright notices, the above copyright notice and a
+    disclaimer of warranty is included with the distribution.</li>
+  </ol>
+
+  <h3>2. Ownership.</h3>
+
+  <p>The Flexible Configuration Management software is Crown copyright and is
+  reproduced with the permission of Met Office under delegated authority from
+  the Controller of HMSO. The software and documentation are provided to you to
+  allow you to exercise your rights under this License, which is granted to
+  you.</p>
+
+  <h3>3. Duration.</h3>
+
+  <p>This license will remain in effect until terminated.</p>
+
+  <h3>4. Termination.</h3>
+
+  <p>You may terminate this license at any time by removing all copies of the
+  software from your system. This License will terminate immediately without
+  notice from us if you fail to comply with any of the provisions of this
+  License or in the event of your breaching the terms of this licence you are
+  given notice that the license has been terminated. Upon termination you will
+  delete all copies of the software and any related documentation.</p>
+
+  <h3>5. Disclaimer of Warranty.</h3>
+
+  <ol>
+    <li>THE MET OFFICE DISCLAIMS ALL WARRANTIES, REPRESENTATIONS AND PROMISES,
+    INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF SATISFACTORY QUALITY
+    AND FIT FOR THE PURPOSE. NEITHER DOES THE MET OFFICE MAKE ANY
+    REPRESENTATIONS AS TO COMPATABILITY WITH YOUR OPERATING SYSTEMS AND
+    PLATFORMS.</li>
+
+    <li>In no event does the Met Office warrant that the software or related
+    documentation will satisfy your requirements, that the software and
+    documentation will be without errors or defects or that the operation of
+    the software will be uninterrupted.</li>
+
+    <li>IN NO EVENT WILL THE MET OFFICE BE LIABLE FOR ANY OTHER DAMAGES,
+    INCLUDING BUT NOT LIMITED TO DAMAGES FOR LOSS OF PROFITS DATA OR USE OF THE
+    SOFTWARE OR FOR ANY INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES, EVEN IF
+    THE MET OFFICE HAS BEEN SPECIFICALLY ADVISED OF THE POSSIBILITY OF SUCH
+    DAMAGES.</li>
+  </ol>
+
+  <h3>6. General Provisions.</h3>
+
+  <ol>
+    <li>You will not do anything, relating to this software that will bring the
+    Met Office into disrepute.</li>
+
+    <li>You will not use the name of the Met Office or any other contributor to
+    endorse or promote any products derived from the software without the
+    written permission of the Met Office.</li>
+  </ol>
+
+  <h3>7. Acknowledgements.</h3>
+
+  <p>The Fortran 9X Interface Generator found in file
+  ./src/lib/Ecmwf/Fortran90_stuff.pm is adapted from a script developed at
+  ECMWF and is provided by kind permission of ECMWF under the same terms of
+  this Licence.</p>
+
+  <h3>8. Entire Agreement.</h3>
+
+  <p>This License constitutes the entire agreement between us with respect to
+  your rights or warranties for using the software and related documentation.
+  If any provision of this agreement is determined to be invalid or
+  unenforceable the remaining provisions shall continue in full force.</p>
+
+  <h3>9. Governing Law.</h3>
+
+  <p>This Agreement is governed by and construed in accordance with the Laws of
+  England.</p>
+
+  <div class="copyright">
+    <p>&copy; British Crown copyright 2006-7.</p>
+  </div>
+</body>
+</html>
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/README
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/README	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/README	(revision 5058)
@@ -0,0 +1,4 @@
+FCM release 1-2 created from revision 2131
+
+For further details please refer to the release notes
+which can be found in the directory doc/release_notes.
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/bin/fcm
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/bin/fcm	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/bin/fcm	(revision 5058)
@@ -0,0 +1,794 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# NAME
+#   fcm
+#
+# SYNOPSIS
+#   fcm SUBCOMMAND [OPTIONS...] ARGS...
+#
+# DESCRIPTION
+#   The fcm command is the frontend of the FCM system. The first argument to the
+#   command must be a recognised subcommand. See "fcm help" for a full list of
+#   functionalities.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+# Standard pragmas:
+use warnings;
+use strict;
+
+# Standard modules:
+use File::Basename;
+use File::Spec;
+use Getopt::Long;
+use Cwd;
+
+# FCM component modules:
+use lib File::Spec->catfile (dirname (dirname ($0)), 'lib');
+use Fcm::Config;
+use Fcm::Extract;
+use Fcm::Build;
+use Fcm::Util;
+
+BEGIN {
+  eval {
+    require Fcm::Cm;
+    import Fcm::Cm;
+
+    require Fcm::CmUrl;
+    import Fcm::CmUrl;
+  }
+}
+
+# Function declaration:
+sub cmp_ext_cfg;
+sub invoke_build_system;
+sub invoke_extract_system;
+sub invoke_cfg_printer;
+sub invoke_cm_system;
+sub invoke_www_browser;
+sub invoke_help;
+
+# ------------------------------------------------------------------------------
+
+my $prog      = basename $0;
+my $year      = (localtime)[5] + 1900;
+my $copyright = <<EOF;
+
+(C) Crown copyright $year Met Office. All rights reserved.
+EOF
+
+# List of sub-commands recognised by FCM
+my %subcommand = (
+  HLP => [qw/help ? h/],
+  BLD => [qw/build bld/],
+  EXT => [qw/extract ext/],
+  CFG => [qw/cfg/],
+  GUI => [qw/gui/],
+  CM  => [qw/
+    branch    br
+    conflicts cf
+    add
+    blame     praise annotate ann
+    cat
+    checkout  co
+    cleanup
+    commit    ci
+    copy      cp
+    delete    del    remove   rm
+    diff      di
+    export
+    import
+    info
+    list      ls
+    lock
+    log
+    merge
+    mkdir
+    mkpatch
+    move      mv     rename   ren
+    propdel   pdel   pd
+    propedit  pedit  pe
+    propget   pget   pg
+    proplist  plist  pl
+    propset   pset   ps
+    resolved
+    revert
+    status    stat   st
+    switch    sw
+    unlock
+    update    up
+  /],
+  CMP => [qw/cmp-ext-cfg/],
+  WWW => [qw/www trac/],
+);
+
+# Get configuration settings
+my $config = Fcm::Config->new ();
+$config->get_config ();
+
+# Determine the functionality of this invocation of the command
+my $function = @ARGV ? shift @ARGV : '';
+
+# Run command accordingly
+if (grep {$_ eq $function} @{ $subcommand{BLD} }) {
+  invoke_build_system;
+
+} elsif (grep {$_ eq $function} @{ $subcommand{EXT} }) {
+  invoke_extract_system;
+
+} elsif (grep {$_ eq $function} @{ $subcommand{CFG} }) {
+  invoke_cfg_printer;
+
+} elsif (grep {$_ eq $function} @{ $subcommand{GUI} }) {
+  &run_command (['fcm_gui', @ARGV], METHOD => 'exec');
+
+} elsif (grep {$_ eq $function} @{ $subcommand{CM} }) {
+  invoke_cm_system;
+
+} elsif (grep {$_ eq $function} @{ $subcommand{CMP} }) {
+  cmp_ext_cfg;
+
+} elsif (grep {$_ eq $function} @{ $subcommand{WWW} }) {
+  invoke_www_browser;
+
+} elsif ($function =~ /^\s*$/ or grep {$_ eq $function} @{ $subcommand{HLP} }) {
+  invoke_help;
+
+} else {
+  w_report 'Unknown command: ', $function;
+  e_report 'Type "', $prog, ' help" for usage';
+}
+
+exit;
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $cfg = &main::cfg ();
+#
+# DESCRIPTION
+#   Return the $config variable.
+# ------------------------------------------------------------------------------
+
+sub cfg {
+  return $config;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &cmp_ext_cfg ();
+#
+# DESCRIPTION
+#   Compare two similar extract configuration files.
+# ------------------------------------------------------------------------------
+
+sub cmp_ext_cfg {
+  # Check options
+  # ----------------------------------------------------------------------------
+  my ($wiki, $verbose);
+
+  GetOptions ('wiki|w=s' => \$wiki, 'verbose|v' => \$verbose);
+
+  # Check arguments
+  # ----------------------------------------------------------------------------
+  e_report $prog, ' ', $function,
+           ': 2 extract config files must be specified, abort.'
+    if @ARGV < 2;
+
+  # Invoke 2 new instances of the Fcm::Extract class
+  # ----------------------------------------------------------------------------
+  my (@cfg, $rc);
+  for my $i (0 .. 1) {
+    $cfg[$i] = Fcm::Extract->new (CFG_SRC => $ARGV[$i]);
+
+    # Read the extract configuration file
+    $rc = $cfg[$i]->decipher_cfg;
+    $rc = $cfg[$i]->expand_cfg if $rc;
+
+    last if not $rc;
+  }
+
+  # Throw error if command has failed
+  # ----------------------------------------------------------------------------
+  e_report $prog, ' ', $function,
+           ': cannot read extract configuration file, abort' if not $rc;
+
+  # Get list of URLs
+  # ----------------------------------------------------------------------------
+  my @urls = ();
+  for my $i (0 .. 1) {
+    # List of branches in each extract configuration file
+    my @branches = $cfg[$i]->branches;
+
+    for my $branch (@branches) {
+      # Ignore declarations of local directories
+      next if $branch->type eq 'user';
+
+      # List of SRC declarations in each branch
+      my %dirs = $branch->dirs;
+
+      for my $dir (values %dirs) {
+        # Set up a new instance of Fcm::CmUrl object for each SRC declaration
+        my $cm_url = Fcm::CmUrl->new (
+          URL => $dir . ($branch->version ? '@' . $branch->version : ''),
+        );
+
+        $urls[$i]{$cm_url->branch_url}{$dir} = $cm_url;
+      }
+    }
+  }
+
+  # Compare
+  # ----------------------------------------------------------------------------
+  my %log;
+  for my $i (0 .. 1) {
+    # Compare the first file with the second one and then vice versa
+    my $j = ($i == 0) ? 1 : 0;
+
+    for my $branch (sort keys %{ $urls[$i] }) {
+      if (exists $urls[$j]{$branch}) {
+        # Same REPOS declarations in both files
+        for my $dir (sort keys %{ $urls[$i]{$branch} }) {
+          if (exists $urls[$j]{$branch}{$dir}) {
+            # Same SRC declarations in both files, only need to compare once
+            next if $i == 1;
+
+            my $this_url = $urls[$i]{$branch}{$dir};
+            my $that_url = $urls[$j]{$branch}{$dir};
+
+            # Check whether their last changed revisions are the same
+            my $this_rev = $this_url->svninfo (FLAG => 'Last Changed Rev');
+            my $that_rev = $that_url->svninfo (FLAG => 'Last Changed Rev');
+
+            # Make sure last changed revisions differ
+            next if $this_rev eq $that_rev;
+
+            # Not interested in the log before the minimum revision
+            my $min_rev = ($this_url->pegrev > $that_url->pegrev)
+                          ? $that_url->pegrev : $this_url->pegrev;
+
+            $this_rev = $min_rev if $this_rev < $min_rev;
+            $that_rev = $min_rev if $that_rev < $min_rev;
+
+            # Get list of changed revisions using the commit log
+            my $u   = ($this_rev > $that_rev) ? $this_url : $that_url;
+            my %revs = $u->svnlog (REV => [$this_rev, $that_rev]);
+
+            for my $rev (keys %revs) {
+              # Check if revision is already in the list
+              next if exists $log{$branch}{$rev};
+
+              # Not interested in the minimum revision
+              next if $rev == $min_rev;
+
+              # Get list of changed paths. Accept this revision only if it
+              # contains changes in the current branch
+              my %paths  = %{ $revs{$rev}{paths} };
+
+              for my $path (keys %paths) {
+                my $change_url = Fcm::CmUrl->new (URL => $u->root . $path);
+
+                if ($change_url->branch eq $u->branch) {
+                  $log{$branch}{$rev} = $u;
+                  last;
+                }
+              }
+            }
+
+          } else {
+            # Report SRC declaration in one file but not in another
+            print $urls[$i]{$branch}{$dir}->url_peg, ':', "\n";
+            print '  in    : ', $ARGV[$i], "\n";
+            print '  not in: ', $ARGV[$j], "\n\n";
+          }
+        }
+
+      } else {
+        # Report REPOS declaration in one file but not in another
+        print $branch, ':', "\n";
+        print '  in    : ', $ARGV[$i], "\n";
+        print '  not in: ', $ARGV[$j], "\n\n";
+      }
+    }
+  }
+
+  # Report modifications
+  # ----------------------------------------------------------------------------
+  print 'Revisions at which declared source directories are modified:', "\n\n"
+    if keys %log;
+
+  if (defined $wiki) {
+    # Output in wiki format
+    my $wiki_url  = Fcm::CmUrl->new (URL => &expand_url_keyword (URL => $wiki));
+    my $base_trac = $wiki
+                    ? &get_browser_url (URL => $wiki_url->project_url)
+                    : $wiki_url;
+    $base_trac    = $wiki_url if not $base_trac;
+
+    for my $branch (sort keys %log) {
+      # Name of the branch
+      my $branch_trac = &get_browser_url (URL => $branch);
+      $branch_trac =~ s#^$base_trac(?:/*|$)#source:#;
+
+      print '[', $branch_trac, ']:', "\n";
+
+      # Revision table
+      for my $rev (sort {$b <=> $a} keys %{ $log{$branch} }) {
+        print $log{$branch}{$rev}->display_svnlog ($rev, $base_trac), "\n";
+      }
+
+      print "\n";
+    }
+
+  } else {
+    my $separator = '-' x 80 . "\n";
+
+    for my $branch (sort keys %log) {
+      # Output in plain text format
+      print $branch, ':', "\n";
+
+      if ($verbose or &cfg->verbose > 1) {
+        # Verbose mode, print revision log
+        for my $rev (sort {$b <=> $a} keys %{ $log{$branch} }) {
+          print $separator, $log{$branch}{$rev}->display_svnlog ($rev), "\n";
+        }
+
+      } else {
+        # Normal mode, print list of revisions
+        print join (' ', sort {$b <=> $a} keys %{ $log{$branch} }), "\n";
+      }
+
+      print $separator, "\n";
+    }
+  }
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &invoke_build_system ();
+#
+# DESCRIPTION
+#   Invoke the build system.
+# ------------------------------------------------------------------------------
+
+sub invoke_build_system {
+  my ($archive, $full, $ignore_lock, $jobs, $stage, @targets, $verbose);
+
+  GetOptions (
+    'archive|a'   => \$archive,     # switch on archive mode?
+    'full|f'      => \$full,        # full build?
+    'ignore-lock' => \$ignore_lock, # ignore lock file?
+    'jobs|j=i'    => \$jobs,        # number of parallel jobs in make
+    'stage|s=s'   => \$stage,       # build up to and including this stage
+    'targets|t=s' => \@targets,     # make targets
+    'verbose|v=i' => \$verbose,     # verbose level
+  );
+
+  # Verbose level
+  $config->verbose ($verbose) if defined $verbose;
+
+  # Invoke a new instance of the Fcm::Build class
+  my $bld = Fcm::Build->new (CFG_SRC  => @ARGV ? join (' ', @ARGV) : cwd ());
+
+  # Perform build
+  $bld->build (
+    ARCHIVE     => $archive,
+    FULL        => $full,
+    IGNORE_LOCK => $ignore_lock,
+    JOBS        => $jobs ? $jobs : 1,
+    STAGE       => $stage ? $stage : 5,
+    TARGETS     => (@targets ? [split (/:/, join (':', @targets))] : [qw/all/]),
+  );
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &invoke_extract_system ();
+#
+# DESCRIPTION
+#   Invoke the extract system.
+# ------------------------------------------------------------------------------
+
+sub invoke_extract_system {
+  my ($full, $ignore_lock, $verbose);
+
+  GetOptions (
+    'full|f'      => \$full,        # full extract?
+    'ignore-lock' => \$ignore_lock, # ignore lock file?
+    'verbose|v=i' => \$verbose,     # verbose level
+  );
+
+  $config->verbose ($verbose) if defined $verbose;
+
+  # Invoke a new instance of the Fcm::Extract class
+  my $ext = Fcm::Extract->new (CFG_SRC => @ARGV ? join (' ', @ARGV) : cwd ());
+
+  # Perform extract
+  $ext->extract (FULL => $full, IGNORE_LOCK => $ignore_lock);
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &invoke_cfg_printer ();
+#
+# DESCRIPTION
+#   Invoke the CFG file pretty printer.
+# ------------------------------------------------------------------------------
+
+sub invoke_cfg_printer {
+
+  use Fcm::CfgFile;
+
+  my $out_file;
+  GetOptions (
+    'output|o=s'  => \$out_file,  # output file for print
+  );
+
+  my $file = join (' ', @ARGV);
+  e_report $prog, ' ', $function, ': file not specified, abort.' if ! $file;
+
+  # Invoke a new Fcm::CfgFile instance
+  my $cfg = Fcm::CfgFile->new (SRC => $file);
+
+  # Read the cfg file
+  my $read = $cfg->read_cfg;
+  e_report if not $read;
+
+  # Pretty print CFG file
+  $cfg->print_cfg ($out_file);
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &invoke_cm_system ();
+#
+# DESCRIPTION
+#   Invoke a code management system command.
+# ------------------------------------------------------------------------------
+
+sub invoke_cm_system {
+
+  &cm_command ($function);
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &invoke_www_browser ();
+#
+# DESCRIPTION
+#   Invoke a web browser on the specified PATH.
+# ------------------------------------------------------------------------------
+
+sub invoke_www_browser {
+
+  # Options
+  my ($browser);
+  GetOptions (
+    'browser|b=s' => \$browser, # browser command
+  );
+
+  $browser = &cfg->setting (qw/MISC WEB_BROWSER/) unless $browser;
+
+  # Arguments
+  my ($arg) = @ARGV ? $ARGV[0] : (&is_wc () ? '.' : '');
+  e_report $prog, ' ', $function,
+           ': input URL not specified and . not a working copy, abort.'
+    if not $arg;
+
+  # Local PATH?
+  $arg = &expand_tilde ($arg);
+  $arg = &get_url_of_wc ($arg) if -e $arg;
+
+  # Expand URL and revision keywords
+  my $www_url = &expand_url_keyword (URL => $arg);
+  my $rev     = 'HEAD';
+
+  if ($www_url =~ m#^(\w+://\S+)@(\S+)$#) {
+    $www_url = $1;
+    $rev     = $2;
+  }
+
+  $rev = &expand_rev_keyword (URL => $www_url, REV => $rev, HEAD => 1)
+    unless uc ($rev) eq 'HEAD';
+
+  # Get web browser URL
+  $www_url = &get_browser_url (URL => $www_url);
+  die 'WWW URL not defined for "', $arg, '", abort' unless $www_url;
+
+  $www_url = $www_url . '?rev=' . $rev;
+
+  # Execute command
+  my @command = (split (/\s+/, $browser), $www_url);
+  &run_command (\@command, METHOD => 'exec', PRINT => 1);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &invoke_help ();
+#
+# DESCRIPTION
+#   Invoke help.
+# ------------------------------------------------------------------------------
+
+sub invoke_help {
+
+  my $cmd = @ARGV ? shift @ARGV : undef;
+
+  if ($cmd) {
+    if (grep {$_ eq $cmd} @{ $subcommand{BLD} }) {
+      print <<EOF;
+$prog $cmd: invoke the build system.
+usage: $prog $cmd [OPTIONS...] [CFGFILE]
+
+  The path to a CFG file may be provided. Otherwise, the build system
+  searches the default locations for a bld cfg file.
+
+  If no option is specified, the options "-s 5 -t all -j 1 -v 1" are assumed.
+
+  If the option for full build is specified, the sub-directories created by
+  previous builds will be removed, so that the current build can start cleanly.
+
+  The -s option can be used to limit the actions performed by the build system
+  up to a named stage. The stages are:
+    "1", "s" or "setup"                - stage 1, setup
+    "2", "pp" or "pre_process"         - stage 2, pre-process
+    "3", "gd" or "generate_dependency" - stage 3, generate dependency
+    "4", "gi" or "generate_interface"  - stage 4, generate Fortran 9X interface
+    "5", "m", "make"                   - stage 5, make
+
+  If a colon separated list of targets is specified using the -t option, the
+  default targets specified in the configuration file will not be used.
+
+  If archive mode is switched on, build sub-directories that are only used
+  in the build process will be archived to TAR files. The default is off.
+
+  If specified, the verbose level must be an integer greater than 0. Verbose
+  level 0 is the quiet mode. Increasing the verbose level will increase the
+  amount of diagnostic output.
+
+  When a build is invoked, it sets up a lock file in the build root directory.
+  The lock is normally removed at the end of the build. While the lock file is
+  in place, othe build commands invoked in the same root directory will fail.
+  If you need to bypass this check for whatever reason, you can invoke the
+  build system with the --ignore-lock option.
+
+Valid options:
+  -a [--archive]     : archive build sub-directories?
+  -f [--full]        : full build
+  --ignore-lock      : ignore lock files in build root directory
+  -j [--jobs] arg    : number of parallel jobs that "make" can handle
+  -s [--stage] arg   : perform build up to a named stage
+  -t [--targets] arg : build a colon (:) separated list of targets
+  -v [--verbose] arg : verbose level
+$copyright
+EOF
+
+    } elsif (grep {$_ eq $cmd} @{ $subcommand{EXT} }) {
+      print <<EOF;
+$prog $cmd: invoke the extract system.
+usage: $prog $cmd [OPTIONS...] [CFGFILE]
+
+  The path to a CFG file may be provided. Otherwise, the extract system
+  searches the default locations for an ext cfg file.
+
+  If no option is specified, the system will attempt an incremental extract
+  where appropriate.
+
+  If specified, the verbose level must be an integer greater than 0. Verbose
+  level 0 is the quiet mode. Increasing the verbose level will increase the
+  amount of diagnostic output.
+
+  When an extract is invoked, it sets up a lock file in the extract destination
+  root directory. The lock is normally removed at the end of the extract. While
+  the lock file is in place, othe extract commands invoked in the same
+  destination root directory will fail. If you need to bypass this check for
+  whatever reason, you can invoke the extract system with the --ignore-lock
+  option.
+
+Valid options:
+  -f [--full]        : perform a full/clean extract
+  --ignore-lock      : ignore lock files in build root directory
+  -v [--verbose] arg : verbose level
+$copyright
+EOF
+
+    } elsif (grep {$_ eq $cmd} @{ $subcommand{CFG} }) {
+      print <<EOF;
+$prog $cmd: invoke the CFG file pretty printer.
+usage: $prog $cmd [OPTIONS...] FILE
+
+  If no option is specified, the output will be sent to standard output.
+
+Valid options:
+  -o [--output] arg : send output to a file as specified by arg.
+$copyright
+EOF
+
+    } elsif (grep {$_ eq $cmd} @{ $subcommand{GUI} }) {
+      print <<EOF;
+$prog $cmd: invoke the GUI wrapper for CM commands.
+usage: $prog $cmd DIR
+
+  The optional argument DIR modifies the initial working directory.
+$copyright
+EOF
+
+    } elsif (grep {$_ eq $cmd} @{ $subcommand{CM} }) {
+      @ARGV = qw(--help);
+      cm_command ($cmd);
+
+    } elsif (grep {$_ eq $cmd} @{ $subcommand{CMP} }) {
+      print <<EOF;
+$prog $cmd: compare two similar extract configuration files.
+usage: $prog $cmd [OPTIONS...] CFG1 CFG2
+
+  Compares the extract configurations of two similar extract configuration
+  files CFG1 and CFG2.
+
+Valid options:
+  -v [--verbose]  : print revision tables in verbose mode. In particular,
+                    display the change log of each revision.
+  -w [--wiki] arg : print revision tables in wiki format. The argument to this
+                    option must be the Subversion URL or FCM URL keyword of a
+                    FCM project associated with the intended Trac system. This
+                    option overrides the -v option.
+$copyright
+EOF
+
+    } elsif (grep {$_ eq $cmd} @{ $subcommand{WWW} }) {
+      print <<EOF;
+$prog $cmd: invoke the web repository browser on a Subversion URL.
+usage: $prog $cmd [OPTIONS...] [PATH]
+
+  If PATH is specified, it must be a FCM URL keyword, a Subversion URL or the
+  PATH to a local working copy. If not specified, the current working directory
+  is assumed to be a working copy. If the --browser option is specified, the
+  specified web browser command is used to launch the repository browser.
+  Otherwise, it attempts to use the default browser from the configuration
+  setting.
+
+Valid options:
+  -b [--browser] arg : specify a command arg for the web browser.
+$copyright
+EOF
+
+    } elsif (grep {$_ eq $cmd} @{ $subcommand{HLP} }) {
+      print <<EOF;
+help (?, h): Describe the usage of $prog or its subcommands.
+usage: $prog help [SUBCOMMAND...]
+$copyright
+EOF
+
+      &run_command ([qw/svn help/, $cmd, @ARGV], PRINT => 1);
+
+    } else {
+      warn $prog, ' help: "', $cmd, '" not recognised';
+      $cmd = undef;
+    }
+  }
+
+  if (not $cmd) {
+    # Get output from "svn help"
+    my @lines = &run_command (
+      [qw/svn help/], DEVNULL => 1, METHOD => 'qx', ERROR => 'ignore',
+    );
+
+    # Get release number, (and revision number from revision number file)
+    my $release  = &cfg->setting ('RELEASE');
+    my $rev_file = &cfg->setting ('REV_FILE');
+
+    if (-r $rev_file) {
+      open FILE, '<', $rev_file;
+      my $rev = readline 'FILE';
+      close FILE;
+
+      chomp $rev;
+      $release .= '-dev (r' . $rev . ')' if $rev;
+    }
+
+    # Print common help
+    print <<EOF;
+usage: $prog <subcommand> [options] [args]
+Flexible configuration management system, release $release.
+Type "$prog help <subcommand>" for help on a specific subcommand.
+
+Available subcommands:
+  help       (h, ?) - help
+  build      (bld)  - build system
+EOF
+
+    # The following are only available on platforms with "svn" installed
+    if (@lines) {
+      print <<EOF;
+  branch     (br)   - cm system: branch info & creation
+  cfg               - CFG file pretty printer
+  cmp-ext-cfg       - compare two similar extract configuration files
+  conflicts  (cf)   - cm system: resolve conflicts
+  extract    (ext)  - extract system
+  mkpatch           - create patches from specified revisions of a URL
+  trac       (www)  - cm system: browse a path using the web browser
+  <SVN COMMANDS>    - any Subversion sub-commands
+EOF
+    }
+
+    # Print FCM copyright notice
+    print $copyright;
+
+    # Print output from "svn help"
+    if (@lines) {
+      print "\n";
+      &print_command ([qw/svn help/]);
+      print @lines;
+    }
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $ans = &main::get_input (MESSAGE => $mesg, TYPE => $type, DEFAULT => $def);
+#
+# DESCRIPTION
+#   Get an input string from the user and return it as $ans. MESSAGE is the
+#   main message printed on screen to prompt the user for an input.  If TYPE is
+#   'YN', print message to prompt user to enter either 'y' or 'n'. If TYPE is
+#   'YNA', then 'a' is given as a third option. If DEFAULT is set, print message
+#   to inform user that the return value will be set to the $def (if nothing is
+#   entered).
+# ------------------------------------------------------------------------------
+
+sub get_input {
+  my %args = @_;
+  my $type  = exists $args{TYPE}    ? $args{TYPE}    : '';
+  my $mesg  = exists $args{MESSAGE} ? $args{MESSAGE} : '';
+  my $def   = exists $args{DEFAULT} ? $args{DEFAULT} : '';
+
+  my $ans;
+
+  while (1) {
+    # Print the prompt
+    print $mesg;
+    print "\n", 'Enter "y" or "n"' if uc ($type) eq 'YN';
+    print "\n", 'Enter "y", "n" or "a"' if uc ($type) eq 'YNA';
+    print ' (or just press <return> for "', $def, '")' if $def;
+    print ': ';
+
+    # Get answer from STDIN
+    $ans = <STDIN>;
+    chomp $ans;
+
+    # Set answer to default, if necessary
+    $ans = $def if ($def and not $ans);
+
+    if ($type =~ /^yna?$/i) {
+      # For YN and YNA type dialog boxes,
+      # check that the answer is in the correct form
+      my $pat = (uc ($type) eq 'YN' ? 'y|n' : 'y|n|a');
+      last if $ans =~ /^(?:$pat)/i;
+
+    } else {
+      last;
+    }
+  }
+
+  return $ans;
+}
+
+# ------------------------------------------------------------------------------
+
+__END__
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/bin/fcm_graphic_diff
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/bin/fcm_graphic_diff	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/bin/fcm_graphic_diff	(revision 5058)
@@ -0,0 +1,90 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# NAME
+#   fcm_graphic_diff
+#
+# SYNOPSIS
+#   fcm_graphic_diff [-u] [-L OLD_DESC] [-L NEW_DESC] OLD NEW
+#
+# DESCRIPTION
+#   Wrapper script which invokes a graphical diff tool. Its interface is
+#   compatible with the "svn diff" command and can be used in combination with
+#   its "--diff-cmd" option. The command prints the OLD_DESC and NEW_DESC if
+#   they are both set. The two arguments OLD and NEW must be set and are the
+#   files to compare. The graphical diff tool invoked depends on the value of
+#   the FCM_GRAPHIC_DIFF environment variable. The command exits if the
+#   environment variable is not set.
+#
+# COPYRIGHT
+#   This program is part of the FCM system.
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+# Standard pragmas:
+use warnings;
+use strict;
+
+use Getopt::Long;
+
+# ------------------------------------------------------------------------------
+
+my ($u, @label);
+GetOptions ('u' => \$u, 'L=s' => \@label);
+
+# Check existence of files
+for my $i (0 .. 1) {
+  die $ARGV[$i], ': not found, abort' unless $ARGV[$i] and -f $ARGV[$i];
+}
+
+my ($old, $new) = @ARGV;
+
+if ($old =~ m#.svn/empty-file$#) {
+  print 'Skipping new file', "\n\n";
+
+} elsif ($new =~ m#.svn/empty-file$#) {
+  print 'Skipping deleted file', "\n\n";
+
+} elsif (-z $old) {
+  print 'Skipping as old file is empty (or does not exist)', "\n\n";
+
+} elsif (-z $new) {
+  print 'Skipping as new file is empty (or deleted)', "\n\n";
+
+} elsif (-B $new) {
+  print 'Skipping binary file', "\n\n";
+
+} else {
+  # Print descriptions of files
+  if (@label >= 2) {
+    print '--- ', $label[0], "\n", '+++ ', $label[1], "\n\n";
+  }
+
+  # FCM_GRAPHIC_DIFF is the graphical diff tool command
+  my $cmd = (exists $ENV{FCM_GRAPHIC_DIFF} ? $ENV{FCM_GRAPHIC_DIFF} : 'xxdiff');
+
+  if ($cmd) {
+    my @options = ();
+
+    # Set options for labels if appropriate
+    if (@label >= 2) {
+      if ($cmd eq 'tkdiff') {
+        # Use tkdiff
+        @options = ('-L', $label[0], '-L', $label[1]);
+
+      } elsif ($cmd eq 'xxdiff') {
+        # Use xxdiff
+        @options = ('--title1', $label[0], '--title2', $label[1]);
+      }
+    }
+
+    # Execute the command
+    my @command = ($cmd, @options, $old, $new);
+    exec (@command) or die 'Cannot execute: ', join (' ', @command);
+  }
+
+  exit;
+}
+
+__END__
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/bin/fcm_gui
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/bin/fcm_gui	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/bin/fcm_gui	(revision 5058)
@@ -0,0 +1,1321 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# NAME
+#   fcm_gui
+#
+# SYNOPSIS
+#   fcm_gui [DIR]
+#
+# DESCRIPTION
+#   The fcm_gui command is a simple graphical user interface for some of the
+#   commands of the FCM system. The optional argument DIR modifies the initial
+#   working directory.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+# Standard pragmas
+use warnings;
+use strict;
+
+# Standard modules
+use File::Basename;
+use File::Spec::Functions;
+use Cwd;
+use Tk;
+use Tk::ROText;
+
+# FCM component modules:
+use lib catfile (dirname (dirname ($0)), 'lib');
+use Fcm::Config;
+use Fcm::Util;
+use Fcm::Timer;
+
+# ------------------------------------------------------------------------------
+
+# Argument
+if (@ARGV) {
+  my $dir = shift @ARGV;
+  chdir $dir if -d $dir;
+}
+
+# Get configuration settings
+my $config = Fcm::Config->new ();
+$config->get_config ();
+
+# ------------------------------------------------------------------------------
+
+# FCM subcommands
+my @subcmds = qw/CHECKOUT BRANCH STATUS DIFF ADD DELETE MERGE CONFLICTS COMMIT
+                 UPDATE SWITCH/;
+
+# Subcommands allowed when CWD is not a WC
+my @nwc_subcmds = qw/CHECKOUT BRANCH/;
+
+# Subcommands allowed, when CWD is a WC
+my @wc_subcmds = qw/STATUS BRANCH DIFF ADD DELETE MERGE CONFLICTS COMMIT UPDATE
+                    SWITCH/;
+
+# Subcommands that apply to WC only
+my @wco_subcmds = qw/BRANCH STATUS DIFF ADD DELETE MERGE CONFLICTS COMMIT UPDATE
+                     SWITCH/;
+
+# Subcommands that apply to top level WC only
+my @wcto_subcmds = qw/BRANCH MERGE COMMIT UPDATE SWITCH/;
+
+# Selected subcommand
+my $selsubcmd = '';
+
+# Selected subcommand is running?
+my $cmdrunning = 0;
+
+# PID of running subcommand
+my $cmdpid = undef;
+
+# List of subcommand frames
+my %subcmd_f;
+
+# List of subcommand buttons
+my %subcmd_b;
+
+# List of subcommand button help strings
+my %subcmd_help = (
+  BRANCH    => 'list information about, create or delete a branch.',
+  CHECKOUT  => 'check out a working copy from a repository.',
+  STATUS    => 'print the status of working copy files and directories.',
+  DIFF      => 'display the differences in modified files.',
+  ADD       => 'put files and directories under version control.',
+  DELETE    => 'remove files and directories from version control.',
+  MERGE     => 'merge changes into your working copy.',
+  CONFLICTS => 'use "xxdiff" to resolve any conflicts within your working copy.',
+  COMMIT    => 'send changes from your working copy to the repository.',
+  UPDATE    => 'bring changes from the repository into your working copy.',
+  SWITCH    => 'update your working copy to a different URL.',
+);
+
+for (keys %subcmd_help) {
+  $subcmd_help{$_} = 'Select the "' . lc ($_) . '" sub-command - ' .
+                     $subcmd_help{$_};
+}
+
+# List of subcommand button bindings (key name and underline position)
+my %subcmd_bind = (
+  BRANCH    => {KEY => '<Alt-Key-b>', U => 0},
+  CHECKOUT  => {KEY => '<Alt-Key-o>', U => 5},
+  STATUS    => {KEY => '<Alt-Key-s>', U => 0},
+  DIFF      => {KEY => '<Alt-Key-d>', U => 0},
+  ADD       => {KEY => '<Alt-Key-a>', U => 0},
+  DELETE    => {KEY => '<Alt-Key-t>', U => 4},
+  MERGE     => {KEY => '<Alt-Key-m>', U => 0},
+  CONFLICTS => {KEY => '<Alt-Key-f>', U => 3},
+  COMMIT    => {KEY => '<Alt-Key-c>', U => 0},
+  UPDATE    => {KEY => '<Alt-Key-u>', U => 0},
+  SWITCH    => {KEY => '<Alt-Key-w>', U => 1},
+);
+
+# List of subcommand variables
+my %subcmdvar = (
+  CWD       => cwd (),
+  WCT       => '',
+  CWD_URL   => '',
+  WCT_URL   => '',
+
+  BRANCH    => {
+    OPT     => 'info',
+    URL     => '',
+    NAME    => '',
+    TYPE    => 'DEV',
+    REVFLAG => 'NORMAL',
+    REV     => '',
+    TICKET  => '',
+    SRCTYPE => 'trunk',
+    S_CHD   => 0,
+    S_SIB   => 0,
+    S_OTH   => 0,
+    VERBOSE => 0,
+    OTHER   => '',
+  },
+
+  CHECKOUT  => {
+    URL     => '',
+    REV     => 'HEAD',
+    PATH    => '',
+    OTHER   => '',
+  },
+
+  STATUS    => {
+    USEWCT  => 0,
+    UPDATE  => 0,
+    VERBOSE => 0,
+    OTHER   => '',
+  },
+
+  DIFF      => {
+    USEWCT  => 0,
+    GRAPHIC => 1,
+    BRANCH  => 0,
+    URL     => '',
+    OTHER   => '',
+  },
+
+  ADD       => {
+    USEWCT  => 0,
+    CHECK   => 1,
+    OTHER   => '',
+  },
+
+  DELETE    => {
+    USEWCT  => 0,
+    CHECK   => 1,
+    OTHER   => '',
+  },
+
+  MERGE     => {
+    USEWCT  => 1,
+    SRC     => '',
+    MODE    => 'automatic',
+    DRYRUN  => 0,
+    VERBOSE => 0,
+    REV     => '',
+    OTHER   => '',
+  },
+
+  CONFLICTS => {
+    USEWCT  => 0,
+    OTHER   => '',
+  },
+
+  COMMIT    => {
+    USEWCT  => 1,
+    DRYRUN  => 0,
+    OTHER   => '',
+  },
+
+  UPDATE    => {
+    USEWCT  => 1,
+    OTHER   => '',
+  },
+
+  SWITCH    => {
+    USEWCT  => 1,
+    URL     => '',
+    OTHER   => '',
+  },
+);
+
+# List of action buttons
+my %action_b;
+
+# List of action button help strings
+my %action_help = (
+  QUIT  => 'Quit fcm gui',
+  HELP  => 'Print help to the output text box for the selected sub-command',
+  CLEAR => 'Clear the output text box',
+  RUN   => 'Run the selected sub-command',
+);
+
+# List of action button bindings
+my %action_bind = (
+  QUIT  => {KEY => '<Control-Key-q>', U => undef},
+  HELP  => {KEY => '<F1>'           , U => undef},
+  CLEAR => {KEY => '<Alt-Key-l>'    , U => 1},
+  RUN   => {KEY => '<Alt-Key-r>'    , U => 0},
+);
+
+# List of branch subcommand options
+my %branch_opt = (
+  INFO   => undef,
+  CREATE => undef,
+  DELETE => undef,
+  LIST   => undef,
+);
+
+# List of branch create types
+my %branch_type = (
+  'DEV'         => undef,
+  'DEV::SHARE'  => undef,
+  'TEST'        => undef,
+  'TEST::SHARE' => undef,
+  'PKG'         => undef,
+  'PKG::SHARE'  => undef,
+  'PKG::CONFIG' => undef,
+  'PKG::REL'    => undef,
+);
+
+# List of branch create source type
+my %branch_srctype = (
+  TRUNK  => undef,
+  BRANCH => undef,
+);
+
+# List of branch create revision prefix option
+my %branch_revflag = (
+  NORMAL => undef,
+  NUMBER => undef,
+  NONE   => undef,
+);
+
+# List of branch info/delete options
+my %branch_info_opt = (
+  S_CHD   => 'Show children',
+  S_SIB   => 'Show siblings',
+  S_OTH   => 'Show other',
+  VERBOSE => 'Print extra information',
+);
+
+# Text in the status bar
+my $statustext = '';
+
+# ------------------------------------------------------------------------------
+
+my $mw = MainWindow->new ();
+
+my $mw_title = 'FCM GUI';
+$mw->title ($mw_title);
+
+# Frame containing subcommand selection buttons
+my $top_f = $mw->Frame ()->grid (
+  '-row'    => 0,
+  '-column' => 0,
+  '-sticky' => 'w',
+);
+
+# Frame containing subcommand options
+my $mid_f = $mw->Frame ()->grid (
+  '-row'    => 1,
+  '-column' => 0,
+  '-sticky' => 'ew',
+);
+
+# Frame containing action buttons
+my $bot_f = $mw->Frame ()->grid (
+  '-row'    => 2,
+  '-column' => 0,
+  '-sticky' => 'ew',
+);
+
+# Text box to display output
+my $out_t  = $mw->Scrolled ('ROText', '-scrollbars' => 'osow')->grid (
+  '-row'    => 3,
+  '-column' => 0,
+  '-sticky' => 'news',
+);
+
+# Text box - allow scroll with mouse wheel
+$out_t->bind (
+  '<4>' => sub {
+    $_[0]->yview ('scroll', -1, 'units') unless $Tk::strictMotif;
+  },
+);
+
+$out_t->bind (
+  '<5>' => sub {
+    $_[0]->yview ('scroll', +1, 'units') unless $Tk::strictMotif;
+  },
+);
+
+# Status bar
+$mw->Label (
+  '-textvariable' => \$statustext,
+  '-relief'       => 'groove',
+)->grid (
+  '-row'    => 4,
+  '-column' => 0,
+  '-sticky' => 'ews',
+);
+
+# Main window grid configure
+{
+  my ($cols, $rows) = $mw->gridSize ();
+  $mw->gridColumnconfigure ($_, '-weight' => 1) for (0 .. $cols - 1);
+  $mw->gridRowconfigure    ( 3, '-weight' => 1);
+}
+
+# Frame grid configure
+{
+  my ($cols, $rows) = $mid_f->gridSize ();
+  $bot_f->gridColumnconfigure (3, '-weight' => 1);
+}
+
+$mid_f->gridRowconfigure    (0, '-weight' => 1);
+$mid_f->gridColumnconfigure (0, '-weight' => 1);
+
+# ------------------------------------------------------------------------------
+
+# Buttons to select subcommands
+{
+  my $col = 0;
+  for my $name (@subcmds) {
+    $subcmd_b{$name} = $top_f->Button (
+      '-text'    => uc (substr ($name, 0, 1)) . lc (substr ($name, 1)),
+      '-command' => [\&button_clicked, $name],
+      '-width'   => 8,
+    )->grid (
+      '-row'    => 0,
+      '-column' => $col++,
+      '-sticky' => 'w',
+    );
+
+    $subcmd_b{$name}->bind ('<Enter>', sub {$statustext = $subcmd_help{$name}});
+    $subcmd_b{$name}->bind ('<Leave>', sub {$statustext = ''});
+
+    $subcmd_b{$name}->configure ('-underline' => $subcmd_bind{$name}{U})
+      if defined $subcmd_bind{$name}{U};
+
+    $mw->bind ($subcmd_bind{$name}{KEY}, sub {$subcmd_b{$name}->invoke});
+  }
+}
+
+# ------------------------------------------------------------------------------
+
+# Frames to contain subcommands options
+{
+  my %row = ();
+
+  for my $name (@subcmds) {
+    $subcmd_f{$name} = $mid_f->Frame ();
+    $subcmd_f{$name}->gridColumnconfigure (1, '-weight' => 1);
+
+    $row{$name} = 0;
+
+    # Widgets common to all sub-commands
+    $subcmd_f{$name}->Label ('-text' => 'Current working directory: ')->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+    $subcmd_f{$name}->Label ('-textvariable' => \($subcmdvar{CWD}))->grid (
+      '-row'    => $row{$name}++,
+      '-column' => 1,
+      '-sticky' => 'w',
+    );
+  }
+
+  # Widgets common to all sub-commands that apply to working copies
+  for my $name (@wco_subcmds) {
+    my @labtxts = (
+      'Corresponding URL: ',
+      'Working copy top: ',
+      'Corresponding URL: ',
+    );
+    my @varrefs = \(
+      $subcmdvar{URL_CWD},
+      $subcmdvar{WCT},
+      $subcmdvar{URL_WCT},
+    );
+
+    for my $i (0 .. $#varrefs) {
+      $subcmd_f{$name}->Label ('-text' => $labtxts[$i])->grid (
+        '-row'    => $row{$name},
+        '-column' => 0,
+        '-sticky' => 'w',
+      );
+      $subcmd_f{$name}->Label ('-textvariable' => $varrefs[$i])->grid (
+        '-row'    => $row{$name}++,
+        '-column' => 1,
+        '-sticky' => 'w',
+      );
+    }
+
+    $subcmd_f{$name}->Checkbutton (
+      '-text'     => 'Apply sub-command to working copy top',
+      '-variable' => \($subcmdvar{$name}{USEWCT}),
+      '-state'    => (grep ({$_ eq $name} @wcto_subcmds) ? 'disabled' : 'normal'),
+    )->grid (
+      '-row'        => $row{$name}++,
+      '-column'     => 0,
+      '-columnspan' => 2,
+      '-sticky'     => 'w',
+    );
+  }
+
+  # Widget for the Branch sub-command
+  {
+    my $name = 'BRANCH';
+
+    # Radio buttons to select the sub-option of the branch sub-command
+    my $opt_f = $subcmd_f{$name}->Frame ()->grid (
+      '-row'        => $row{$name}++,
+      '-column'     => 0,
+      '-columnspan' => 2,
+      '-sticky'     => 'w',
+    );
+
+    my $col = 0;
+    for my $key (sort keys %branch_opt) {
+      my $opt = lc $key;
+
+      $branch_opt{$key} = $opt_f->Radiobutton (
+        '-text'     => $opt,
+        '-value'    => $opt,
+        '-variable' => \($subcmdvar{$name}{OPT}),
+        '-state'    => 'normal',
+      )->grid (
+        '-row'      => 0,
+        '-column'   => $col++,
+        '-sticky'   => 'w',
+      );
+    }
+
+    # Label and entry box for specifying URL
+    $subcmd_f{$name}->Label ('-text' => 'URL: ')->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+    $subcmd_f{$name}->Entry (
+      '-textvariable' => \($subcmdvar{$name}{URL}),
+    )->grid (
+      '-row'    => $row{$name}++,
+      '-column' => 1,
+      '-sticky' => 'ew',
+    );
+
+    # Label and entry box for specifying create branch name
+    $subcmd_f{$name}->Label (
+      '-text' => 'Branch name (create only): ',
+    )->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+    $subcmd_f{$name}->Entry (
+      '-textvariable' => \($subcmdvar{$name}{NAME}),
+    )->grid (
+      '-row'    => $row{$name}++,
+      '-column' => 1,
+      '-sticky' => 'ew',
+    );
+
+    # Label and entry box for specifying create branch source revision
+    $subcmd_f{$name}->Label (
+      '-text' => 'Source revision (create/list only): ',
+    )->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+    $subcmd_f{$name}->Entry (
+      '-textvariable' => \($subcmdvar{$name}{REV}),
+    )->grid (
+      '-row'    => $row{$name}++,
+      '-column' => 1,
+      '-sticky' => 'ew',
+    );
+
+    # Label and radio buttons box for specifying create branch type
+    $subcmd_f{$name}->Label (
+      '-text' => 'Branch type (create only): ',
+    )->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+
+    {
+      my $opt_f = $subcmd_f{$name}->Frame ()->grid (
+        '-row'    => $row{$name}++,
+        '-column' => 1,
+        '-sticky' => 'w',
+      );
+
+      my $col = 0;
+      for my $key (sort keys %branch_type) {
+        my $txt = lc $key;
+        my $opt = $key;
+
+        $branch_opt{$key} = $opt_f->Radiobutton (
+          '-text'     => $txt,
+          '-value'    => $opt,
+          '-variable' => \($subcmdvar{$name}{TYPE}),
+          '-state'    => 'normal',
+        )->grid (
+          '-row'      => 0,
+          '-column'   => $col++,
+          '-sticky'   => 'w',
+        );
+      }
+    }
+
+    # Label and radio buttons box for specifying create source type
+    $subcmd_f{$name}->Label (
+      '-text' => 'Source type (create only): ',
+    )->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+
+    {
+      my $opt_f = $subcmd_f{$name}->Frame ()->grid (
+        '-row'    => $row{$name}++,
+        '-column' => 1,
+        '-sticky' => 'w',
+      );
+
+      my $col = 0;
+      for my $key (sort keys %branch_srctype) {
+        my $txt = lc $key;
+        my $opt = lc $key;
+
+        $branch_opt{$key} = $opt_f->Radiobutton (
+          '-text'     => $txt,
+          '-value'    => $opt,
+          '-variable' => \($subcmdvar{$name}{SRCTYPE}),
+          '-state'    => 'normal',
+        )->grid (
+          '-row'      => 0,
+          '-column'   => $col++,
+          '-sticky'   => 'w',
+        );
+      }
+    }
+
+    # Label and radio buttons box for specifying create prefix option
+    $subcmd_f{$name}->Label (
+      '-text' => 'Prefix option (create only): ',
+    )->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+
+    {
+      my $opt_f = $subcmd_f{$name}->Frame ()->grid (
+        '-row'    => $row{$name}++,
+        '-column' => 1,
+        '-sticky' => 'w',
+      );
+
+      my $col = 0;
+      for my $key (sort keys %branch_revflag) {
+        my $txt = lc $key;
+        my $opt = $key;
+
+        $branch_opt{$key} = $opt_f->Radiobutton (
+          '-text'     => $txt,
+          '-value'    => $opt,
+          '-variable' => \($subcmdvar{$name}{REVFLAG}),
+          '-state'    => 'normal',
+        )->grid (
+          '-row'      => 0,
+          '-column'   => $col++,
+          '-sticky'   => 'w',
+        );
+      }
+    }
+
+    # Label and entry box for specifying ticket number
+    $subcmd_f{$name}->Label (
+      '-text' => 'Related Trac ticket(s) (create only): ',
+    )->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+    $subcmd_f{$name}->Entry (
+      '-textvariable' => \($subcmdvar{$name}{TICKET}),
+    )->grid (
+      '-row'    => $row{$name}++,
+      '-column' => 1,
+      '-sticky' => 'ew',
+    );
+
+    # Check button for info/delete
+    # --show-children, --show-siblings, --show-other, --verbose
+    $subcmd_f{$name}->Label (
+      '-text' => 'Options for info/delete only: ',
+    )->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+
+    {
+      my $opt_f = $subcmd_f{$name}->Frame ()->grid (
+        '-row'    => $row{$name}++,
+        '-column' => 1,
+        '-sticky' => 'w',
+      );
+
+      my $col = 0;
+
+      for my $key (sort keys %branch_info_opt) {
+        $opt_f->Checkbutton (
+          '-text'     => $branch_info_opt{$key},
+          '-variable' => \($subcmdvar{$name}{$key}),
+        )->grid (
+          '-row'    => 0,
+          '-column' => $col++,
+          '-sticky' => 'w',
+        );
+      }
+    }
+  }
+
+  # Widget for the Checkout sub-command
+  {
+    my $name = 'CHECKOUT';
+
+    # Label and entry boxes for specifying URL and revision
+    my @labtxts = (
+      'URL: ',
+      'Revision: ',
+      'Path: ',
+    );
+    my @varrefs = \(
+      $subcmdvar{$name}{URL},
+      $subcmdvar{$name}{REV},
+      $subcmdvar{$name}{PATH},
+    );
+
+    for my $i (0 .. $#varrefs) {
+      $subcmd_f{$name}->Label ('-text' => $labtxts[$i])->grid (
+        '-row'    => $row{$name},
+        '-column' => 0,
+        '-sticky' => 'w',
+      );
+      $subcmd_f{$name}->Entry (
+        '-textvariable' => $varrefs[$i],
+      )->grid (
+        '-row'    => $row{$name}++,
+        '-column' => 1,
+        '-sticky' => 'ew',
+      );
+    }
+  }
+
+  # Widget for the Status sub-command
+  {
+    my $name = 'STATUS';
+
+    # Checkbuttons for various options
+    my @labtxts = (
+      'Display update information',
+      'Print extra information',
+    );
+    my @varrefs = \(
+      $subcmdvar{$name}{UPDATE},
+      $subcmdvar{$name}{VERBOSE},
+    );
+
+    for my $i (0 .. $#varrefs) {
+      $subcmd_f{$name}->Checkbutton (
+        '-text'     => $labtxts[$i],
+        '-variable' => $varrefs[$i],
+      )->grid (
+        '-row'        => $row{$name}++,
+        '-column'     => 0,
+        '-columnspan' => 2,
+        '-sticky'     => 'w',
+      );
+    }
+  }
+
+  # Widget for the Diff sub-command
+  {
+    my $name = 'DIFF';
+
+    # Checkbuttons for various options
+    $subcmd_f{$name}->Checkbutton (
+      '-text'     => 'Use xxdiff to display differences',
+      '-variable' => \($subcmdvar{$name}{GRAPHIC}),
+    )->grid (
+      '-row'        => $row{$name}++,
+      '-column'     => 0,
+      '-columnspan' => 2,
+      '-sticky'     => 'w',
+    );
+
+    my $entry;
+    $subcmd_f{$name}->Checkbutton (
+      '-text'     => 'Show differences relative to the base of the branch',
+      '-variable' => \($subcmdvar{$name}{BRANCH}),
+      '-command'  => sub {
+        $entry->configure (
+          '-state' => ($subcmdvar{$name}{BRANCH} ? 'normal' : 'disabled'),
+        );
+      },
+    )->grid (
+      '-row'        => $row{$name}++,
+      '-column'     => 0,
+      '-columnspan' => 2,
+      '-sticky'     => 'w',
+    );
+
+    $subcmd_f{$name}->Label ('-text' => 'Branch URL')->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+
+    $entry = $subcmd_f{$name}->Entry (
+      '-textvariable' => \($subcmdvar{$name}{URL}),
+      '-state'        => ($subcmdvar{$name}{BRANCH} ? 'normal' : 'disabled'),
+    )->grid (
+      '-row'    => $row{$name}++,
+      '-column' => 1,
+      '-sticky' => 'ew',
+    );
+  }
+
+  # Widget for the Add/Delete sub-command
+  for my $name (qw/ADD DELETE/) {
+
+    # Checkbuttons for various options
+    $subcmd_f{$name}->Checkbutton (
+      '-text'     => 'Check for files or directories not under version control',
+      '-variable' => \($subcmdvar{$name}{CHECK}),
+    )->grid (
+      '-row'        => $row{$name}++,
+      '-column'     => 0,
+      '-columnspan' => 2,
+      '-sticky'     => 'w',
+    );
+  }
+
+  # Widget for the Merge sub-command
+  {
+    my $name = 'MERGE';
+
+    # Label and radio buttons box for specifying merge mode
+    $subcmd_f{$name}->Label (
+      '-text' => 'Mode: ',
+    )->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+
+    {
+      my $opt_f = $subcmd_f{$name}->Frame ()->grid (
+        '-row'    => $row{$name}++,
+        '-column' => 1,
+        '-sticky' => 'w',
+      );
+
+      my $col = 0;
+      for my $key (qw/automatic custom reverse/) {
+        my $txt = lc $key;
+        my $opt = $key;
+
+        $branch_opt{$key} = $opt_f->Radiobutton (
+          '-text'     => $txt,
+          '-value'    => $opt,
+          '-variable' => \($subcmdvar{$name}{MODE}),
+          '-state'    => 'normal',
+        )->grid (
+          '-row'      => 0,
+          '-column'   => $col++,
+          '-sticky'   => 'w',
+        );
+      }
+    }
+
+    # Check buttons for dry-run
+    $subcmd_f{$name}->Checkbutton (
+      '-text'     => 'Dry run',
+      '-variable' => \($subcmdvar{$name}{DRYRUN}),
+    )->grid (
+      '-row'        => $row{$name}++,
+      '-column'     => 0,
+      '-columnspan' => 2,
+      '-sticky'     => 'w',
+    );
+
+    # Check buttons for verbose mode
+    $subcmd_f{$name}->Checkbutton (
+      '-text'     => 'Print extra information',
+      '-variable' => \($subcmdvar{$name}{VERBOSE}),
+    )->grid (
+      '-row'        => $row{$name}++,
+      '-column'     => 0,
+      '-columnspan' => 2,
+      '-sticky'     => 'w',
+    );
+
+    # Label and entry boxes for specifying merge source
+    $subcmd_f{$name}->Label (
+      '-text' => 'Source (automatic/custom only): ',
+    )->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+    $subcmd_f{$name}->Entry (
+      '-textvariable' => \($subcmdvar{$name}{SRC}),
+    )->grid (
+      '-row'    => $row{$name}++,
+      '-column' => 1,
+      '-sticky' => 'ew',
+    );
+
+    # Label and entry boxes for specifying merge revision (range)
+    $subcmd_f{$name}->Label (
+      '-text' => 'Revision (custom/reverse only): ',
+    )->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+    $subcmd_f{$name}->Entry (
+      '-textvariable' => \($subcmdvar{$name}{REV}),
+    )->grid (
+      '-row'    => $row{$name}++,
+      '-column' => 1,
+      '-sticky' => 'ew',
+    );
+  }
+
+  # Widget for the Commit sub-command
+  {
+    my $name = 'COMMIT';
+
+    # Checkbuttons for various options
+    $subcmd_f{$name}->Checkbutton (
+      '-text'     => 'Dry run',
+      '-variable' => \($subcmdvar{$name}{DRYRUN}),
+    )->grid (
+      '-row'        => $row{$name}++,
+      '-column'     => 0,
+      '-columnspan' => 2,
+      '-sticky'     => 'w',
+    );
+  }
+
+  # Widget for the Switch sub-command
+  {
+    my $name = 'SWITCH';
+
+    # Label and entry boxes for specifying switch URL
+    $subcmd_f{$name}->Label ('-text' => 'URL: ')->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+    $subcmd_f{$name}->Entry (
+      '-textvariable' => \($subcmdvar{$name}{URL}),
+    )->grid (
+      '-row'    => $row{$name}++,
+      '-column' => 1,
+      '-sticky' => 'ew',
+    );
+  }
+
+  # Widgets common to all sub-commands
+  for my $name (@subcmds) {
+    $subcmd_f{$name}->Label ('-text' => 'Other options: ')->grid (
+      '-row'    => $row{$name},
+      '-column' => 0,
+      '-sticky' => 'w',
+    );
+    $subcmd_f{$name}->Entry (
+      '-textvariable' => \($subcmdvar{$name}{OTHER}),
+    )->grid (
+      '-row'    => $row{$name}++,
+      '-column' => 1,
+      '-sticky' => 'ew',
+    );
+  }
+}
+
+# ------------------------------------------------------------------------------
+
+# Buttons to perform main actions
+{
+  my $col = 0;
+  for my $name (qw/QUIT HELP CLEAR RUN/) {
+    $action_b{$name} = $bot_f->Button (
+      '-text'    => uc (substr ($name, 0, 1)) . lc (substr ($name, 1)),
+      '-command' => [\&button_clicked, $name],
+      '-width'   => 8,
+    )->grid (
+      '-row'    => 0,
+      '-column' => $col++,
+      '-sticky' => ($name eq 'RUN' ? 'ew' : 'w'),
+    );
+
+    $action_b{$name}->bind ('<Enter>', sub {$statustext = $action_help{$name}});
+    $action_b{$name}->bind ('<Leave>', sub {$statustext = ''});
+
+    $action_b{$name}->configure ('-underline' => $action_bind{$name}{U})
+      if defined $action_bind{$name}{U};
+
+    $mw->bind ($action_bind{$name}{KEY}, sub {$action_b{$name}->invoke});
+  }
+}
+
+&change_cwd ($subcmdvar{CWD});
+
+# ------------------------------------------------------------------------------
+
+# Handle the situation when the user attempts to quit the window while a
+# sub-command is running
+
+$mw->protocol ('WM_DELETE_WINDOW', sub {
+  if (defined $cmdpid) {
+    my $ans = $mw->messageBox (
+      '-title'   => $mw_title,
+      '-message' => $selsubcmd . ' is still running. Really quit?',
+      '-type'    => 'YesNo',
+      '-default' => 'No',
+    );
+
+    if ($ans eq 'Yes') {
+      kill 9, $cmdpid; # Need to kill the sub-process before quitting
+
+    } else {
+      return; # Do not quit
+    }
+  }
+
+  exit;
+});
+
+MainLoop;
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $cfg = &main::cfg ();
+#
+# DESCRIPTION
+#   Return the $config variable.
+# ------------------------------------------------------------------------------
+
+sub cfg {
+  return $config;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &change_cwd ($dir);
+#
+# DESCRIPTION
+#   Change current working directory to $dir
+# ------------------------------------------------------------------------------
+
+sub change_cwd {
+  my $dir = $_[0];
+  my @allowed_subcmds = (&is_wc ($dir) ? @wc_subcmds : @nwc_subcmds);
+
+  for my $subcmd (@subcmds) {
+    if (grep {$_ eq $subcmd} @allowed_subcmds) {
+      $subcmd_b{$subcmd}->configure ('-state' => 'normal');
+
+    } else {
+      $subcmd_b{$subcmd}->configure ('-state' => 'disabled');
+    }
+  }
+
+  &display_subcmd_frame ($allowed_subcmds[0])
+    if not grep {$_ eq $selsubcmd} @allowed_subcmds;
+
+  chdir $dir;
+  $subcmdvar{CWD} = $dir;
+
+  if (&is_wc ($dir)) {
+    $subcmdvar{WCT}     = &get_wct ($dir);
+    $subcmdvar{URL_CWD} = &get_url_of_wc ($dir);
+    $subcmdvar{URL_WCT} = &get_url_of_wc ($subcmdvar{WCT});
+
+    $branch_opt{INFO}  ->configure ('-state' => 'normal');
+    $branch_opt{DELETE}->configure ('-state' => 'normal');
+    $subcmdvar{BRANCH}{OPT} = 'info';
+
+  } else {
+    $branch_opt{INFO}  ->configure ('-state' => 'disabled');
+    $branch_opt{DELETE}->configure ('-state' => 'disabled');
+    $subcmdvar{BRANCH}{OPT} = 'create';
+  }
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &button_clicked ($name);
+#
+# DESCRIPTION
+#   Call back function to handle a click on a command button named $name.
+# ------------------------------------------------------------------------------
+
+sub button_clicked {
+  my $name = $_[0];
+
+  if (grep {$_ eq $name} keys %subcmd_b) {
+    &display_subcmd_frame ($name);
+
+  } elsif ($name eq 'CLEAR') {
+    $out_t->delete ('1.0', 'end');
+
+  } elsif ($name eq 'QUIT') {
+    exit;
+
+  } elsif ($name eq 'HELP') {
+    &invoke_cmd ('help ' . lc ($selsubcmd));
+
+  } elsif ($name eq 'RUN') {
+    &invoke_cmd (&setup_cmd ($selsubcmd));
+
+  } else {
+    $out_t->insert ('end', $name . ': function to be implemented' . "\n");
+    $out_t->yviewMoveto (1);
+  }
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &display_subcmd_frame ($name);
+#
+# DESCRIPTION
+#   Change selected subcommand to $name, and display the frame containing the
+#   widgets for configuring the options and arguments of that subcommand.
+# ------------------------------------------------------------------------------
+
+sub display_subcmd_frame {
+  my $name = $_[0];
+
+  if ($selsubcmd ne $name and not $cmdrunning) {
+    $subcmd_b{$name     }->configure ('-relief' => 'sunken');
+    $subcmd_b{$selsubcmd}->configure ('-relief' => 'raised') if $selsubcmd;
+
+    $subcmd_f{$name     }->grid ('-sticky' => 'new');
+    $subcmd_f{$selsubcmd}->gridForget if $selsubcmd;
+
+    $selsubcmd = $name;
+  }
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $pos = &get_wm_pos ();
+#
+# DESCRIPTION
+#   Returns the position part of the geometry string of the main window.
+# ------------------------------------------------------------------------------
+
+sub get_wm_pos {
+  my $geometry = $mw->geometry ();
+  $geometry =~ /^=?(?:\d+x\d+)?([+-]\d+[+-]\d+)$/;
+  return $1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $command = &setup_cmd ($name);
+#
+# DESCRIPTION
+#   Setup the the system command for the sub-command $name.
+# ------------------------------------------------------------------------------
+
+sub setup_cmd {
+  my $name = $_[0];
+  my $cmd  = '';
+
+  if ($name eq 'BRANCH') {
+    $cmd .= lc ($name);
+    if ($subcmdvar{$name}{OPT} eq 'create') {
+      $cmd .= ' -c --svn-non-interactive';
+      $cmd .= ' -n '     . $subcmdvar{$name}{NAME} if $subcmdvar{$name}{NAME};
+      $cmd .= ' -t '     . $subcmdvar{$name}{TYPE};
+      $cmd .= ' --rev-flag ' . $subcmdvar{$name}{REVFLAG};
+      $cmd .= ' -r ' . $subcmdvar{$name}{REV} if $subcmdvar{$name}{REV};
+      $cmd .= ' -k ' . $subcmdvar{$name}{TICKET} if $subcmdvar{$name}{TICKET};
+      $cmd .= ' --branch-of-branch ' if $subcmdvar{$name}{SRCTYPE} eq 'branch';
+
+    } elsif ($subcmdvar{$name}{OPT} eq 'delete') {
+      $cmd .= ' -v' if $subcmdvar{$name}{VERBOSE};
+      $cmd .= ' -d --svn-non-interactive';
+
+    } elsif ($subcmdvar{$name}{OPT} eq 'list') {
+      $cmd .= ' -l';
+      $cmd .= ' -r ' . $subcmdvar{$name}{REV} if $subcmdvar{$name}{REV};
+
+    } else {
+      $cmd .= ' -i';
+      $cmd .= ' --show-children' if $subcmdvar{$name}{S_CHD};
+      $cmd .= ' --show-siblings' if $subcmdvar{$name}{S_SIB};
+      $cmd .= ' --show-other'    if $subcmdvar{$name}{S_OTH};
+      $cmd .= ' -v' if $subcmdvar{$name}{VERBOSE};
+    }
+    $cmd .= ' ' . $subcmdvar{$name}{URL}   if $subcmdvar{$name}{URL};
+    $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
+
+  } elsif ($name eq 'CHECKOUT') {
+    $cmd .= lc ($name);
+    $cmd .= ' -r' . $subcmdvar{$name}{REV} if $subcmdvar{$name}{REV};
+    $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
+    $cmd .= ' ' . $subcmdvar{$name}{URL};
+    $cmd .= ' ' . $subcmdvar{$name}{PATH} if $subcmdvar{$name}{PATH};
+
+  } elsif ($name eq 'STATUS') {
+    $cmd .= lc ($name);
+    $cmd .= ' -u' if $subcmdvar{$name}{UPDATE};
+    $cmd .= ' -v' if $subcmdvar{$name}{VERBOSE};
+    $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
+
+  } elsif ($name eq 'DIFF') {
+    $cmd .= lc ($name);
+    $cmd .= ' -g' if $subcmdvar{$name}{GRAPHIC};
+
+    if ($subcmdvar{$name}{BRANCH}) {
+      $cmd .= ' -b';
+      $cmd .= ' ' . $subcmdvar{$name}{URL} if $subcmdvar{$name}{URL};
+    }
+
+    $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
+
+  } elsif ($name eq 'ADD' or $name eq 'DELETE') {
+    $cmd .= lc ($name);
+    $cmd .= ' -c' if $subcmdvar{$name}{CHECK};
+    $cmd .= ' --non-interactive'
+      if $name eq 'DELETE' and not $subcmdvar{$name}{CHECK};
+    $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
+
+  } elsif ($name eq 'MERGE') {
+    $cmd .= lc ($name);
+
+    if ($subcmdvar{$name}{MODE} ne 'automatic') {
+      $cmd .= ' --' . $subcmdvar{$name}{MODE};
+      $cmd .= ' --revision ' . $subcmdvar{$name}{REV} if $subcmdvar{$name}{REV};
+    }
+
+    $cmd .= ' --dry-run' if $subcmdvar{$name}{DRYRUN};
+    $cmd .= ' -v'        if $subcmdvar{$name}{VERBOSE};
+    $cmd .= ' ' . $subcmdvar{$name}{SRC}   if $subcmdvar{$name}{SRC};
+    $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
+
+  } elsif ($name eq 'CONFLICTS') {
+    $cmd .= lc ($name);
+    $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
+
+  } elsif ($name eq 'COMMIT') {
+    $cmd .= lc ($name);
+    $cmd .= ' --dry-run' if $subcmdvar{$name}{DRYRUN};
+    $cmd .= ' --svn-non-interactive';
+    $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
+
+  } elsif ($name eq 'SWITCH') {
+    $cmd .= lc ($name);
+    $cmd .= ' ' . $subcmdvar{$name}{URL}   if $subcmdvar{$name}{URL};
+    $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
+
+  } elsif ($name eq 'UPDATE') {
+    $cmd .= lc ($name);
+    $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
+
+  }
+
+  return $cmd;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &invoke_cmd ($cmd);
+#
+# DESCRIPTION
+#   Invoke the command $cmd.
+# ------------------------------------------------------------------------------
+
+sub invoke_cmd {
+  my $cmd      = $_[0];
+  return unless $cmd;
+
+  my $disp_cmd = 'fcm ' . $cmd;
+  $cmd         = (index ($cmd, 'help ') == 0)
+                 ? $disp_cmd
+                 : ('fcm_gui_internal ' . &get_wm_pos () . ' ' . $cmd);
+
+  # Change directory to working copy top if necessary
+  if ($subcmdvar{$selsubcmd}{USEWCT} and $subcmdvar{WCT} ne $subcmdvar{CWD}) {
+    chdir $subcmdvar{WCT};
+    $out_t->insert ('end', 'cd ' . $subcmdvar{WCT} . "\n");
+    $out_t->yviewMoveto (1);
+  }
+
+  # Report start of command
+  $out_t->insert ('end', timestamp_command ($disp_cmd, 'Start'));
+  $out_t->yviewMoveto (1);
+
+  # Open the command as a pipe
+  if ($cmdpid = open CMD, '-|', $cmd . ' 2>&1') {
+    # Disable all action buttons
+    $action_b{$_}->configure ('-state' => 'disabled') for (keys %action_b);
+    $cmdrunning = 1;
+
+    # Set up a file event to read output from the command
+    $mw->fileevent (\*CMD, readable => sub {
+      if (sysread CMD, my ($buf), 1024) {
+        # Insert text into the output text box as it becomes available
+        $out_t->insert ('end', $buf);
+        $out_t->yviewMoveto (1);
+
+      } else {
+        # Delete the file event and close the file when the command finishes
+        $mw->fileevent(\*CMD, readable => '');
+        close CMD;
+        $cmdpid = undef;
+
+        # Check return status
+        if ($?) {
+          $out_t->insert (
+            'end', '"' . $disp_cmd . '" failed (' . $? . ')' . "\n",
+          );
+          $out_t->yviewMoveto (1);
+        }
+
+        # Report end of command
+        $out_t->insert ('end', timestamp_command ($disp_cmd, 'End'));
+        $out_t->yviewMoveto (1);
+
+        # Change back to CWD if necessary
+        if ($subcmdvar{$selsubcmd}{USEWCT} and
+            $subcmdvar{WCT} ne $subcmdvar{CWD}) {
+          chdir $subcmdvar{CWD};
+          $out_t->insert ('end', 'cd ' . $subcmdvar{CWD} . "\n");
+          $out_t->yviewMoveto (1);
+        }
+
+        # Enable all action buttons again
+        $action_b{$_}->configure ('-state' => 'normal') for (keys %action_b);
+        $cmdrunning = 0;
+
+        # If the command is "checkout", change directory to working copy
+        if (lc ($selsubcmd) eq 'checkout') {
+          my $url = expand_url_keyword (URL => $subcmdvar{CHECKOUT}{URL});
+          my $dir = $subcmdvar{CHECKOUT}{PATH}
+                  ? $subcmdvar{CHECKOUT}{PATH}
+                  : basename $url;
+          $dir    = File::Spec->rel2abs ($dir);
+          &change_cwd ($dir);
+
+        # If the command is "switch", change URL
+        } elsif (lc ($selsubcmd) eq 'switch') {
+          $subcmdvar{URL_CWD} = &get_url_of_wc ($subcmdvar{CWD}, 1);
+          $subcmdvar{URL_WCT} = &get_url_of_wc ($subcmdvar{WCT}, 1);
+        }
+      }
+      1;
+    });
+
+  } else {
+    $mw->messageBox (
+      '-title'   => 'Error',
+      '-message' => 'Error running "' . $cmd . '"',
+      '-icon'    => 'error',
+    );
+  }
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+
+__END__
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/bin/fcm_gui_internal
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/bin/fcm_gui_internal	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/bin/fcm_gui_internal	(revision 5058)
@@ -0,0 +1,272 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# NAME
+#   fcm_gui_internal
+#
+# SYNOPSIS
+#   fcm_gui_internal POS FUNCTION
+#
+# DESCRIPTION
+#   The fcm_gui_internal command is part of a simple graphical user interface
+#   for some of the commands of the FCM system. The argument POS is a geometry
+#   string used by the &Main::get_input method to determine the location of the
+#   pop up window. The argument FUNCTION must be a keyword recognised by the
+#   &Fcm::Cm::cm_command function.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+# Standard pragmas
+use warnings;
+use strict;
+
+# Standard modules
+use Tk;
+use File::Basename;
+use File::Spec::Functions;
+
+# FCM component modules:
+use lib catfile (dirname (dirname ($0)), 'lib');
+use Fcm::Cm;
+use Fcm::Config;
+
+# ------------------------------------------------------------------------------
+
+# Get configuration settings
+my $config = Fcm::Config->new ();
+$config->get_config ();
+
+my $pos      = shift @ARGV;
+my $function = shift @ARGV;
+cm_command $function;
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $cfg = &main::cfg ();
+#
+# DESCRIPTION
+#   Return the $config variable.
+# ------------------------------------------------------------------------------
+
+sub cfg {
+  return $config;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $ans = &main::get_input (
+#     TITLE   => $title,
+#     MESSAGE => $mesg,
+#     TYPE    => $type,
+#     DEFAULT => $def,
+#   );
+#
+# DESCRIPTION
+#   Get an input string from the user and return it as $ans. If TYPE is 'YN', a
+#   'YesNo' type message box will be displayed to prompt the user to click
+#   either the 'yes' or 'no' button. If TYPE is 'YNA', then an 'all' button is
+#   provided as a third option. Otherwise, a dialog box with an entry box for
+#   the user to type in a string will be displayed. TITLE is the title of the
+#   dialog box, and MESSAGE is the main message of the dialog box. If DEFAULT is
+#   set, $ans is set to the default value when the dialog box is invoked.
+# ------------------------------------------------------------------------------
+
+sub get_input {
+  my %args  = @_;
+  my $title = exists $args{TITLE}   ? $args{TITLE}   : '';
+  my $mesg  = exists $args{MESSAGE} ? $args{MESSAGE} : '';
+  my $type  = exists $args{TYPE}    ? $args{TYPE}    : '';
+  my $def   = exists $args{DEFAULT} ? $args{DEFAULT} : '';
+  my $ans   = '';
+
+  # Create a main window
+  my $mw = MainWindow->new;
+  $mw->title ($title);
+
+  # Define the default which applies if the dialog box is just closed or
+  # the user selects 'cancel'
+  $ans = $def ? $def : '';
+
+  if ($type =~ /^yn/i) {
+    # Create a yes-no(-all) dialog box
+
+    # If TYPE is YNA then add a third button: 'all'
+    my $buttons;
+    if ($type =~ /a$/i) {
+      $buttons = 3;
+
+    } else {
+      $buttons = 2;
+    }
+
+    # Message of the dialog box
+    $mw->Label ('-text' => $mesg)->grid (
+      '-row'        => 0,
+      '-column'     => 0,
+      '-columnspan' => $buttons,
+      '-padx'       => 10,
+      '-pady'       => 10,
+    );
+
+    # The "yes" button
+    my $y_b = $mw->Button (
+      '-text'      => 'Yes',
+      '-underline' => 0,
+      '-command'   => sub {$ans = 'y'; $mw->destroy},
+    )->grid (
+      '-row'    => 1,
+      '-column' => 0,
+      '-padx'   => 5,
+      '-pady'   => 5,
+    );
+
+    # The "no" button
+    my $n_b = $mw->Button (
+      '-text'      => 'No',
+      '-underline' => 0,
+      '-command'   => sub {$ans = 'n'; $mw->destroy},
+    )->grid (
+      '-row'    => 1,
+      '-column' => 1,
+      '-padx'   => 5,
+      '-pady'   => 5,
+    );
+
+    # The "all" button
+    my $a_b;
+    if ($buttons == 3) {
+      $a_b = $mw->Button (
+        '-text'      => 'All',
+        '-underline' => 0,
+        '-command'   => sub {$ans = 'a'; $mw->destroy},
+      )->grid (
+        '-row'    => 1,
+        '-column' => 2,
+        '-padx'   => 5,
+        '-pady'   => 5,
+      );
+    }
+
+    # Keyboard binding
+    if ($buttons == 3) {
+      $mw->bind ('<Key>' => sub {
+	if ($Tk::event->K eq 'Y' or $Tk::event->K eq 'y') {
+	  $y_b->invoke;
+
+	} elsif ($Tk::event->K eq 'N' or $Tk::event->K eq 'n') {
+	  $n_b->invoke;
+
+	} elsif ($Tk::event->K eq 'A' or $Tk::event->K eq 'a') {
+	  $a_b->invoke;
+	}
+      });
+
+    } else {
+      $mw->bind ('<Key>' => sub {
+	if ($Tk::event->K eq 'Y' or $Tk::event->K eq 'y') {
+	  $y_b->invoke;
+
+	} elsif ($Tk::event->K eq 'N' or $Tk::event->K eq 'n') {
+	  $n_b->invoke;
+	}
+      });
+    }
+
+    # Handle the situation when the user attempts to quit the window
+    $mw->protocol ('WM_DELETE_WINDOW', sub {
+      $ans = $def if $def;
+      $mw->destroy;
+    });
+
+  } else {
+    # Create a dialog box to obtain an input string
+
+    # Message of the dialog box
+    $mw->Label ('-text' => $mesg)->grid (
+      '-row'    => 0,
+      '-column' => 0,
+      '-padx'   => 5,
+      '-pady'   => 5,
+    );
+
+    # Entry box for the user to type in the input string
+    my $entry   = $ans;
+    my $input_e = $mw->Entry (
+      '-textvariable'    => \$entry,
+      '-width'           => 40,
+    )->grid (
+      '-row'    => 0,
+      '-column' => 1,
+      '-sticky' => 'ew',
+      '-padx'   => 5,
+      '-pady'   => 5,
+    );
+
+    my $b_f = $mw->Frame->grid (
+      '-row'        => 1,
+      '-column'     => 0,
+      '-columnspan' => 2,
+      '-sticky'     => 'e',
+    );
+
+    # An OK button to accept the input string
+    my $ok_b = $b_f->Button (
+      '-text' => 'OK',
+      '-command' => sub {$ans = $entry; $mw->destroy},
+    )->grid (
+      '-row'        => 0,
+      '-column'     => 0,
+      '-padx'       => 5,
+      '-pady'       => 5,
+    );
+
+    # A Cancel button to reject the input string
+    my $cancel_b = $b_f->Button (
+      '-text' => 'Cancel',
+      '-command' => sub {$ans = undef; $mw->destroy},
+    )->grid (
+      '-row'        => 0,
+      '-column'     => 1,
+      '-padx'       => 5,
+      '-pady'       => 5,
+    );
+
+    # Keyboard binding
+    $mw->bind ('<Key>' => sub {
+      if ($Tk::event->K eq 'Return' or $Tk::event->K eq 'KP_Enter') {
+        $ok_b->invoke;
+
+      } elsif ($Tk::event->K eq 'Escape') {
+        $cancel_b->invoke;
+      }
+    });
+
+    # Allow the entry box to expand
+    $mw->gridColumnconfigure (1, '-weight' => 1);
+
+    # Set initial focus on the entry box
+    $input_e->focus;
+    $input_e->icursor ('end');
+  }
+
+  $mw->geometry ($pos);
+
+  # Switch on "always on top" property for $mw
+  $mw->property (
+    qw/set _NET_WM_STATE ATOM/,
+    32,
+    ['_NET_WM_STATE_STAYS_ON_TOP'],
+    ($mw->toplevel->wrapper)[0],
+  );
+
+  MainLoop;
+
+  return $ans;
+}
+
+# ------------------------------------------------------------------------------
+
+__END__
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/bin/fcm_internal
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/bin/fcm_internal	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/bin/fcm_internal	(revision 5058)
@@ -0,0 +1,581 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# NAME
+#   fcm_internal
+#
+# SYNOPSIS
+#   fcm_internal SUBCOMMAND ARGS
+#
+# DESCRIPTION
+#   The fcm_internal command is a frontend for some of the internal commands of
+#   the FCM build system. The subcommand can be "compile", "load" or "archive"
+#   for invoking the compiler, loader and library archiver respectively. If
+#   "compile" is specified, it can be suffixed with ":TYPE" to specify the
+#   nature of the source file. If TYPE is not specified, it is set to F by
+#   default (for Fortran source). For compile and load, the other arguments are
+#   the 1) name of the container package of the source file, 2) the path to the
+#   source file and 3) the target name after compiling or loading the source
+#   file. For compile, the 4th argument is a flag to indicate whether
+#   pre-processing is required for compiling the source file. For load, the 4th
+#   and the rest of the arguments is a list of object files that cannot be
+#   archived into the temporary load library and must be linked into the target
+#   through the linker command. (E.g. Fortran BLOCKDATA program units must be
+#   linked this way.) If archive is specified, the first argument should be the
+#   name of the library archive target and the rest should be the object files
+#   to be included in the archive. This command is invoked via the build system
+#   and should never be called directly by the user.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+# Standard pragmas:
+use warnings;
+use strict;
+
+# FCM component modules
+use Fcm::Timer;
+
+# Function declarations
+sub catfile;
+sub basename;
+sub dirname;
+
+# ------------------------------------------------------------------------------
+
+# Module level variables
+my %unusual_tool_name = ();
+
+# ------------------------------------------------------------------------------
+
+MAIN: {
+  # Name of program
+  my $this = basename $0;
+
+  # Arguments
+  my $subcommand = shift @ARGV;
+  my ($function, $type) = split /:/, $subcommand; 
+  my ($srcpackage, $src, $target, $requirepp, @objects, @blockdata);
+  
+  if ($subcommand eq 'archive') {
+    ($target, @objects) = @ARGV;
+
+  } elsif ($subcommand eq 'load') {
+    ($srcpackage, $src, $target, @blockdata) = @ARGV;
+
+  } else {
+    ($srcpackage, $src, $target, $requirepp) = @ARGV;
+  }
+
+  # Set up hash reference for all the required information
+  my %info = (
+    SRCPACKAGE => $srcpackage,
+    SRC        => $src,
+    TYPE       => $type,
+    TARGET     => $target,
+    REQUIREPP  => $requirepp,
+    OBJECTS    => \@objects,
+    BLOCKDATA  => \@blockdata,
+  );
+
+  # Get list of unusual tools
+  my $i = 0;
+  while (my $label = &get_env ('FCM_UNUSUAL_TOOL_LABEL' . $i)) {
+    my $value = &get_env ('FCM_UNUSUAL_TOOL_VALUE' . $i);
+    $unusual_tool_name{$label} = $value;
+    $i++;
+  }
+
+  # Invoke the action
+  my $rc = 0;
+  if ($function eq 'compile') {
+    $rc = &compile (\%info);
+
+  } elsif ($function eq 'load') {
+    $rc = &load (\%info);
+
+  } elsif ($function eq 'archive') {
+    $rc = &archive (\%info);
+
+  } else {
+    print STDERR $this, ': incorrect usage, abort';
+    $rc = 1;
+  }
+
+  # Throw error if action failed
+  if ($rc) {
+    print STDERR $this, ' ', $function, ' failed (', $rc, ')', "\n";
+    exit 1;
+
+  } else {
+    exit;
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = &compile (\%info);
+#
+# DESCRIPTION
+#   This method invokes the correct compiler with the correct options to
+#   compile the source file into the required target. The argument $info is a
+#   hash reference set up in MAIN. The following environment variables are
+#   used, where * is the source file type (F for Fortran, and C for C/C++):
+# 
+#   *C          - compiler command
+#   *C_OUTPUT   - *C option to specify the name of the output file
+#   *C_DEFINE   - *C option to declare a pre-processor def
+#   *C_INCLUDE  - *C option to declare an include directory
+#   *C_COMPILE  - *C option to ask the compiler to perform compile only
+#   *CFLAGS     - *C user options
+#   *PPKEYS     - list of pre-processor defs (may have sub-package suffix)
+#   FCM_VERBOSE - verbose level
+#   FCM_OBJDIR  - destination directory of object file
+#   FCM_TMPDIR  - temporary destination directory of object file
+# ------------------------------------------------------------------------------
+
+sub compile {
+  my $info = shift;
+
+  # Verbose mode
+  my $verbose = &get_env ('FCM_VERBOSE');
+  $verbose    = 1 unless defined ($verbose);
+
+  my @command = ();
+
+  # Only support C and Fortran source files at the moment
+  my $type = $info->{TYPE};
+  $type    = ($info->{SRC} =~ /\.c(\w+)?$/i) ? 'C' : 'F' if not $type;
+
+  # Compiler
+  push @command, &get_env ($type . 'C', 1);
+
+  # Compile output target (typical -o option)
+  push @command, &get_env ($type . 'C_OUTPUT', 1), $info->{TARGET};
+
+  # Pre-processor definition macros
+  if ($info->{REQUIREPP}) {
+    my @ppkeys = split /\s+/, &select_flags ($info, $type . 'PPKEYS');
+    my $defopt = &get_env ($type . 'C_DEFINE', 1);
+
+    push @command, (map {$defopt . $_} @ppkeys);
+  }
+
+  # Include search path
+  my $incopt  = &get_env ($type . 'C_INCLUDE', 1);
+  my @incpath = split /:/, &get_env ('FCM_INCPATH');
+  push @command, (map {$incopt . $_} @incpath);
+
+  # Other compiler flags
+  my $flags = &select_flags ($info, $type . 'FLAGS');
+  push @command, $flags if $flags;
+
+  my $compile_only = &get_env ($type . 'C_COMPILE');
+  if ($flags !~ /(?:^|\s)$compile_only\b/) {
+    push @command, &get_env ($type . 'C_COMPILE');
+  }
+
+  # Name of source file
+  push @command, $info->{SRC};
+
+  # Execute command
+  my $objdir = &get_env ('FCM_OBJDIR', 1);
+  my $tmpdir = &get_env ('FCM_TMPDIR', 1);
+  chdir $tmpdir;
+
+  my $command = join ' ', @command;
+  if ($verbose > 1) {
+    print 'cd ', $tmpdir, "\n";
+    print &timestamp_command ($command, 'Start');
+
+  } elsif ($verbose) {
+    print $command, "\n";
+  }
+
+  my $rc = system $command;
+
+  print &timestamp_command ($command, 'End  ') if $verbose > 1;
+
+  # Move temporary output to correct location on success
+  # Otherwise, remove temporary output
+  if ($rc) { # error
+    unlink $info->{TARGET};
+
+  } else {   # success
+    print 'mv ', $info->{TARGET}, ' ', $objdir, "\n" if $verbose > 1;
+    rename $info->{TARGET}, &catfile ($objdir, $info->{TARGET});
+  }
+
+  # Move any Fortran module definition files to the INC directory
+  my @modfiles = <*.mod *.MOD>;
+  for my $file (@modfiles) {
+    rename $file, &catfile ($incpath[0], $file);
+  }
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = &load (\%info);
+#
+# DESCRIPTION
+#   This method invokes the correct loader with the correct options to link
+#   the main program object into an executable. The argument $info is a hash
+#   reference set up in MAIN. The following environment variables are used:
+# 
+#   LD           - linker command
+#   LD_OUTPUT    - LD option to specify the name of the output file
+#   LD_LIBSEARCH - LD option to declare a directory in the library search path
+#   LD_LIBLINK   - LD option to declare an object library
+#   LDFLAGS      - LD user options
+#   FCM_VERBOSE  - verbose level
+#   FCM_LIBDIR   - destination directory of object libraries
+#   FCM_OBJDIR   - destination directory of object files
+#   FCM_BINDIR   - destination directory of executable file
+#   FCM_TMPDIR   - temporary destination directory of executable file
+# ------------------------------------------------------------------------------
+
+sub load {
+  my $info = shift;
+
+  my $rc = 0;
+
+  # Verbose mode
+  my $verbose = &get_env ('FCM_VERBOSE');
+  $verbose    = 1 unless defined ($verbose);
+
+  # Create temporary object library
+  (my $name   = $info->{TARGET}) =~ s/\.\S+$//;
+  my $libname = '__fcm__' . $name;
+  my $lib     = 'lib' . $libname . '.a';
+  my $libfile = catfile (&get_env ('FCM_LIBDIR', 1), $lib);
+  $rc = &archive ({TARGET => $lib});
+
+  unless ($rc) {
+    my @command = ();
+
+    # Linker
+    push @command, &select_flags ($info, 'LD');
+
+    # Linker output target (typical -o option)
+    push @command, &get_env ('LD_OUTPUT', 1), $info->{TARGET};
+
+    # Name of main object file
+    my $mainobj = (basename ($info->{SRC}) eq $info->{SRC})
+                  ? catfile (&get_env ('FCM_OBJDIR'), $info->{SRC})
+                  : $info->{SRC};
+    push @command, $mainobj;
+
+    # Link with Fortran BLOCKDATA objects if necessary
+    if (@{ $info->{BLOCKDATA} }) {
+      my @blockdata = @{ $info->{BLOCKDATA} };
+      my @objpath   = split /:/, &get_env ('FCM_OBJPATH');
+
+      # Search each BLOCKDATA object file from the object search path
+      for my $file (@blockdata) {
+        for my $dir (@objpath) {
+          my $full = catfile ($dir, $file);
+
+          if (-r $full) {
+            $file = $full;
+            last;
+          }
+        }
+
+        push @command, $file;
+      }
+    }
+
+    # Library search path
+    my $libopt  = &get_env ('LD_LIBSEARCH', 1);
+    my @libpath = split /:/, &get_env ('FCM_LIBPATH');
+    push @command, (map {$libopt . $_} @libpath);
+
+    # Link with temporary object library if it exists
+    push @command, &get_env ('LD_LIBLINK', 1) . $libname if -f $libfile;
+
+    # Other linker flags
+    my $flags = &select_flags ($info, 'LDFLAGS');
+    push @command, $flags;
+
+    # Execute command
+    my $tmpdir = &get_env ('FCM_TMPDIR', 1);
+    my $bindir = &get_env ('FCM_BINDIR', 1);
+    chdir $tmpdir;
+
+    my $command = join ' ', @command;
+    if ($verbose > 1) {
+      print 'cd ', $tmpdir, "\n";
+      print &timestamp_command ($command, 'Start');
+
+    } elsif ($verbose) {
+      print $command, "\n";
+    }
+
+    $rc = system $command;
+
+    print &timestamp_command ($command, 'End  ') if $verbose > 1;
+
+    # Move temporary output to correct location on success
+    # Otherwise, remove temporary output
+    if ($rc) { # error
+      unlink $info->{TARGET};
+
+    } else {   # success
+      print 'mv ', $info->{TARGET}, ' ', $bindir, "\n" if $verbose > 1;
+      rename $info->{TARGET}, &catfile ($bindir, $info->{TARGET});
+    }
+  }
+
+  # Remove the temporary object library
+  unlink $libfile if -f $libfile;
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = &archive (\%info);
+#
+# DESCRIPTION
+#   This method invokes the library archiver to create an object library. The
+#   argument $info is a hash reference set up in MAIN. The following
+#   environment variables are used:
+# 
+#   AR           - archiver command
+#   ARFLAGS      - AR options to update/create an object library
+#   FCM_VERBOSE  - verbose level
+#   FCM_LIBDIR   - destination directory of object libraries
+#   FCM_OBJPATH  - search path of object files
+#   FCM_OBJDIR   - destination directory of object files
+#   FCM_TMPDIR   - temporary destination directory of executable file
+# ------------------------------------------------------------------------------
+
+sub archive {
+  my $info = shift;
+
+  my $rc = 0;
+
+  # Verbose mode
+  my $verbose = &get_env ('FCM_VERBOSE');
+  $verbose    = 1 unless defined ($verbose);
+
+  # Set up the archive command
+  my $lib     = &basename ($info->{TARGET});
+  my $tmplib  = &catfile (&get_env ('FCM_TMPDIR', 1), $lib);
+  my @ar_cmd  = ();
+  push @ar_cmd, (&get_env ('AR', 1), &get_env ('ARFLAGS', 1));
+  push @ar_cmd, $tmplib;
+
+  # Get object directories and their files
+  my %objdir;
+  if (exists $info->{OBJECTS}) {
+    # List of objects set in the argument, sort into directory/file list
+    for my $name (@{ $info->{OBJECTS} }) {
+      my $dir = (&dirname ($name) eq '.')
+                ? &get_env ('FCM_OBJDIR', 1) : &dirname ($name);
+      $objdir{$dir}{&basename ($name)} = 1;
+    }
+
+  } else {
+    # Objects not listed in argument, search object path for all files
+    my @objpath  = split /:/, &get_env ('FCM_OBJPATH', 1);
+    my %objbase  = ();
+
+    # Get registered objects into a hash (keys = objects, values = 1)
+    my %objects = map {($_, 1)} split (/\s+/, &get_env ('OBJECTS'));
+
+    # Seach object path for all files
+    for my $dir (@objpath) {
+      next unless -d $dir;
+
+      chdir $dir;
+
+      # Use all files from each directory in the object search path
+      for ((glob ('*'))) {
+        next unless exists $objects{$_}; # consider registered objects only
+        $objdir{$dir}{$_} = 1 unless exists $objbase{$_};
+        $objbase{$_} = 1;
+      }
+    }
+  }
+
+  for my $dir (keys %objdir) {
+    next unless -d $dir;
+
+    # Go to each object directory and executes the library archive command 
+    chdir $dir;
+    my $command = join ' ', (@ar_cmd, sort keys %{ $objdir{$dir} });
+
+    if ($verbose > 1) {
+      print 'cd ', $dir, "\n";
+      print &timestamp_command ($command, 'Start');
+
+    } else {
+      print $command, "\n" if exists $info->{OBJECTS};
+    }
+
+    $rc = system $command;
+
+    print &timestamp_command ($command, 'End  ')
+      if $verbose > 1;
+    last if $rc;
+  }
+
+  # Move temporary output to correct location on success
+  # Otherwise, remove temporary output
+  if ($rc) { # error
+    unlink $tmplib;
+
+  } else {   # success
+    my $libdir = &get_env ('FCM_LIBDIR', 1);
+
+    print 'mv ', $tmplib, ' ', $libdir, "\n" if $verbose > 1;
+    rename $tmplib, &catfile ($libdir, $lib);
+  }
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flags = &select_flags (\%info, $set);
+#
+# DESCRIPTION
+#   This function selects the correct compiler/linker flags for the current
+#   sub-package from the environment variable prefix $set. The argument $info
+#   is a hash reference set up in MAIN.
+# ------------------------------------------------------------------------------
+
+sub select_flags {
+  my ($info, $set) = @_;
+
+  (my $srcroot = &basename ($info->{SRC})) =~ s/\.\S+$//;
+  my @names    = ($set);
+  push @names, split (/__/, $info->{SRCPACKAGE} . '__' . $srcroot);
+
+  my $string   = '';
+  while (@names) {
+    my $name = join '__', @names;
+    my $var  = &get_env ($name);
+
+    if (defined $var) {
+      $string = $var;
+      last;
+
+    } else {
+      pop @names;
+    }
+  }
+
+  return $string;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $variable = &get_env ($name);
+#   $variable = &get_env ($name, $compulsory);
+#
+# DESCRIPTION
+#   This internal method gets a variable from $ENV{$name}. If $compulsory is
+#   set to true, it throws an error if the variable is a not set or is an empty
+#   string. Otherwise, it returns C<undef> if the variable is not set.
+# ------------------------------------------------------------------------------
+
+sub get_env {
+  (my $name, my $compulsory) = @_;
+  my $string;
+
+  if ($name =~ /^\w+$/) {
+    # $name contains only word characters, variable is exported normally
+    die 'The environment variable "', $name, '" must be set, abort'
+      if $compulsory and not exists $ENV{$name};
+
+    $string = exists $ENV{$name} ? $ENV{$name} : undef;
+
+  } else {
+    # $name contains unusual characters
+    die 'The environment variable "', $name, '" must be set, abort'
+      if $compulsory and not exists $unusual_tool_name{$name};
+
+    $string = exists $unusual_tool_name{$name}
+              ? $unusual_tool_name{$name} : undef;
+  }
+
+  return $string;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $path = &catfile (@paths);
+#
+# DESCRIPTION
+#   This is a local implementation of what is in the File::Spec module.
+# ------------------------------------------------------------------------------
+
+sub catfile {
+  my @names = split (m!/!, join ('/', @_));
+  my $path  = shift @names;
+
+  for my $name (@names) {
+    $path .= '/' . $name if $name;
+  }
+
+  return $path;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $basename = &basename ($path);
+#
+# DESCRIPTION
+#   This is a local implementation of what is in the File::Basename module.
+# ------------------------------------------------------------------------------
+
+sub basename {
+  my $name = $_[0];
+
+  $name =~ s{/*$}{}; # remove trailing slashes
+
+  if ($name =~ m#.*/([^/]+)$#) {
+    return $1;
+
+  } else {
+    return $name;
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $dirname = &dirname ($path);
+#
+# DESCRIPTION
+#   This is a local implementation of what is in the File::Basename module.
+# ------------------------------------------------------------------------------
+
+sub dirname {
+  my $name = $_[0];
+
+  if ($name =~ m#^/+$#) {
+    return '/'; # dirname of root is root
+
+  } else {
+    $name =~ s{/*$}{}; # remove trailing slashes
+
+    if ($name =~ m#^(.*)/[^/]+$#) {
+      my $dir = $1;
+      $dir =~ s{/*$}{}; # remove trailing slashes
+      return $dir;
+
+    } else {
+      return '.';
+    }
+  }
+}
+
+# ------------------------------------------------------------------------------
+
+__END__
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/bin/fcm_setup_konqueror
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/bin/fcm_setup_konqueror	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/bin/fcm_setup_konqueror	(revision 5058)
@@ -0,0 +1,44 @@
+#!/usr/bin/ksh
+# ------------------------------------------------------------------------------
+# NAME
+#   fcm_setup_konqueror
+#
+# SYNOPSIS
+#   fcm_setup_konqueror
+#
+# DESCRIPTION
+#   Set up Konqueror to use "fcm gui".
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+# Check number of arguments
+script=$(basename $0)
+usage="$script: no argument required"
+if (( $# != 0 )); then
+  echo "$usage, abort..." >&2
+  exit 1
+fi
+
+filename=fcm_gui.desktop
+
+file=$(dirname $0)
+file=${file%/bin}/etc/$filename
+
+if [[ ! -f $file ]]; then
+  echo "$script: $file not found, abort..." >&2
+  exit 1
+fi
+
+dir=$HOME/.kde/share/applnk/.hidden
+mkdir -p $dir
+cd $dir
+rm -f $filename             # Always remove.
+ln -s $file .
+
+echo "$script: finished"
+
+#EOF
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/bin/fcm_update_version_dir.pl
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/bin/fcm_update_version_dir.pl	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/bin/fcm_update_version_dir.pl	(revision 5058)
@@ -0,0 +1,302 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# NAME
+#   fcm_update_version_dir.pl
+#
+# SYNOPSIS
+#   fcm_update_version_dir.pl [OPTIONS] [CFGFILE]
+#
+# DESCRIPTION
+#   See $usage for detail.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+# Standard pragmas
+use strict;
+use warnings;
+
+# Standard modules
+use Getopt::Long;
+use Cwd;
+use File::Basename;
+use File::Path;
+use File::Spec;
+
+# FCM component modules:
+use lib File::Spec->catfile (dirname (dirname ($0)), 'lib');
+use Fcm::Config;
+use Fcm::Util;
+
+# Usage
+# ------------------------------------------------------------------------------
+my $this  = basename $0;
+my $usage = <<EOF;
+NAME
+  $this
+
+SYNOPSIS
+  $this [OPTIONS] [CFGFILE]
+
+DESCRIPTION
+  Update the version directories for a list of relative paths in the source
+  repository URL.
+
+OPTIONS
+  -d [--dest] arg: Specify a destination for the extraction. If not specified,
+                   the command extracts to the current working directory.
+  -h [--help]    : Print help and exit.
+  -f [--full]    : Specify the full mode. If not specified, the command runs
+                   in incremental mode.
+  -u [--url]  arg: Specify the source repository URL. No default.
+
+ARGUMENTS
+  A configuration file may be given to this command, or it will attempt to
+  read from the standard input. Each line in the configuration must contain
+  a relative path that resides under the given source repository URL. (Empty
+  lines and lines beginning with a "#" are ignored.) Optionally, each
+  relative path may be followed by a list of space separated "conditions".
+  Each condition is a conditional operator (>, >=, <, <=, == or !=) followed
+  by a revision number or the keyword HEAD. The command uses the revision
+  log to determine the revisions at which the relative path has been updated
+  in the source repository URL. If these revisions also satisfy the
+  "conditions" set by the user, they will be considered in the extraction.
+  In full mode, everything is re-extracted. In incremental mode, the version
+  directories are only updated if they do not already exist.
+
+COPYRIGHT
+  This program is part of the FCM system.
+  (C) Crown copyright Met Office. All rights reserved.
+  For further details please refer to the file COPYRIGHT.txt
+  which you should have received as part of this distribution.
+EOF
+
+# Options
+# ------------------------------------------------------------------------------
+my ($dest, $full, $help, $url);
+GetOptions (
+  'dest|d=s' => \$dest,
+  'full|f'   => \$full,
+  'help'     => \$help,
+  'url|u=s'  => \$url,
+);
+
+if ($help) {
+  print $usage;
+  exit;
+}
+
+$dest = cwd () unless $dest;
+
+die 'An URL must be specified with the --url option, abort' unless $url;
+
+# Arguments
+# ------------------------------------------------------------------------------
+if (@ARGV) {
+  die 'Cannot read: ', $ARGV[0], ', abort' unless -f $ARGV[0] and -r $ARGV[0];
+}
+
+# Get configuration settings
+# ------------------------------------------------------------------------------
+my $config = Fcm::Config->new ();
+$config->get_config ();
+
+# Expand URL keyword
+$url = &expand_url_keyword (URL => $url);
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $cfg = &main::cfg ();
+#
+# DESCRIPTION
+#   Return the $config variable.
+# ------------------------------------------------------------------------------
+
+sub cfg {
+  return $config;
+}
+
+# ------------------------------------------------------------------------------
+
+MAIN: {
+  my $date = localtime;
+  print $this, ': started on ', $date, "\n";
+
+  my %dirs;
+
+  # Read input (file) for a list directories and update conditions
+  while (<>) {
+    chomp;
+
+    # Ignore empty and comment lines
+    next if /^\s*(?:#|$)/;
+
+    # Each line must contain a relative path, and optionally a list of
+    # space delimited conditions
+    my @words = split /\s+/;
+    my $dir   = shift @words;
+
+    # Check that the conditions are valid
+    my @conditions;
+    for my $word (@words) {
+      if ($word =~ /^([<>]=?|[!=]=)(.+)$/i) {
+        # Condition must be a conditional operator followed by a revision
+        my ($operator, $rev) = ($1, $2);
+        $rev = &expand_rev_keyword (REV => $rev, URL => $url, HEAD => 1);
+        push @conditions, $operator . $rev;
+
+      } else {
+        print STDERR 'Warning: ignore unknown syntax for update condition: ',
+                     $word, "\n";
+      }
+    }
+
+    # Add directory and its conditions to a hash
+    if ($dir =~ s#/\*$##) { # Directory finishes with wildcard
+
+      # Run "svn ls" in recursive mode
+      my $dirurl  = join ('/', ($url, $dir));
+      my @files   = &run_command ([qw/svn ls -R/, $dirurl], METHOD => 'qx');
+
+      # Find directories containing regular files
+      while (my $file = shift @files) {
+	# Skip directories
+	next if $file =~ m#/$#;
+
+	# Get "dirname" of regular file and add to hash
+	my $subdir = join ('/', ($dir, dirname ($file)));
+	$dirs{$subdir} = \@conditions;
+      }
+
+    } else {
+      $dirs{$dir} = \@conditions;
+    }
+
+  }
+
+  # Update each directory, if required
+  for my $dir (sort keys %dirs) {
+    # Use "svn log" to determine the revisions that need to be updated
+    my %allversions;
+    {
+      my $command = 'svn log -q ' . join ('/', ($url, $dir));
+      my @log     = &run_command (
+        [qw/svn log -q/, join ('/', ($url, $dir))], METHOD => 'qx',
+      );
+      @log        = grep /^r\d+/, @log;
+
+      # Assign a sequential "version" number to each sub-directory
+      my $version = scalar @log;
+      for (@log) {
+        m/^r(\d+)/;
+        $allversions{$1} = 'v' . $version--;
+      }
+    }
+    my %versions = %allversions;
+
+    # Extract only revisions matching the conditions
+    if (@{ $dirs{$dir} }) {
+      my @conditions = @{ $dirs{$dir} };
+
+      for my $condition (@conditions) {
+        for my $rev (keys %versions) {
+          delete $versions{$rev} unless eval ($rev . $condition);
+        }
+      }
+    }
+
+    # Destination directory
+    my $dirpath = File::Spec->catfile ($dest, $dir);
+
+    if (-d $dirpath) {
+      if ($full or not keys %versions) {
+        # Remove destination directory top, in full mode
+        # or if there are no matching revisions
+        &run_command ([qw/rm -rf/, $dirpath], PRINT => 1);
+
+      } else {
+        # Delete excluded revisions if they exist, in incremental mode
+        if (opendir DIR, $dirpath) {
+          while (my $rev = readdir 'DIR') {
+            next unless $rev =~ /^\d+$/;
+
+            if (not grep {$_ eq $rev} keys %versions) {
+              my @command = (qw/rm -rf/, File::Spec->catfile ($dirpath, $rev));
+              &run_command (\@command, PRINT => 1);
+
+              # Remove "version" symlink
+              my $verlink = File::Spec->catfile ($dirpath, $allversions{$rev});
+              unlink $verlink if -l $verlink;
+            }
+          }
+          closedir DIR;
+        }
+      }
+    }
+
+    # Create container directory of destination if it does not already exist
+    if (keys %versions and not -d $dirpath) {
+      print '-> mkdir -p ', $dirpath, "\n";
+      my $rc = mkpath $dirpath;
+      die 'mkdir -p ', $dirpath, ' failed' unless $rc;
+    }
+
+    # Update each version directory that needs updating
+    for my $rev (keys %versions) {
+      my $revpath = File::Spec->catfile ($dest, $dir, $rev);
+
+      # Create version directory if it does not exist
+      if (not -e $revpath) {
+        # Use "svn export" to create the version directory
+        my @command = (
+          qw/svn export -q -r/,
+          $rev,
+          join ('/', ($url, $dir)),
+          $revpath,
+        );
+
+        &run_command (\@command, PRINT => 1);
+
+        # Run GenScr_GenRule on any component directories
+        # This can be removed once old versions of the SCSUI are no longer used
+	if ($dir =~ m#(?:^|/)components/#) {
+          my @command = (
+            "GenScr_GenRule",
+            File::Spec->catfile ($revpath, join ('.', (basename ($dir), "rule")))
+          );
+
+          &run_command (\@command, PRINT => 1);
+	}
+      }
+
+      # Create "version" symlink if necessary
+      my $verlink = File::Spec->catfile ($dest, $dir, $versions{$rev});
+      symlink $rev, $verlink unless -l $verlink;
+    }
+
+    # Symbolic link to the "latest" version directory
+    my $headlink = File::Spec->catfile ($dest, $dir, 'latest');
+    my $headrev  = 0;
+    for my $rev (keys %versions) {
+      $headrev = $rev if $rev > $headrev;
+    }
+
+    if (-l $headlink) {
+      # Remove old symbolic link if there is no revision to update or if it
+      # does not point to the correct version directory
+      my $org = readlink $headlink;
+      unlink $headlink if (! $headrev or $org ne $headrev);
+    }
+
+    # (Re-)create the "latest" symbolic link, if necessary
+    symlink $headrev, $headlink if ($headrev and not -l $headlink);
+  }
+
+  $date = localtime;
+  print $this, ': finished normally on ', $date, "\n";
+}
+
+__END__
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/etc/fcm.cfg.eg
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/etc/fcm.cfg.eg	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/etc/fcm.cfg.eg	(revision 5058)
@@ -0,0 +1,266 @@
+# ------------------------------------------------------------------------------
+# FCM central configuration file
+# ------------------------------------------------------------------------------
+
+# ------------------------------------------------------------------------------
+# Standard repository locations
+# ------------------------------------------------------------------------------
+
+# AMV repository
+set::url::amv                svn://fcm7/AMV_svn/AMV
+
+set::trac::amv               http://fcm7/projects/AMV/browser/AMV
+
+# ANCIL repository
+set::url::ancil              svn://fcm8/ANCIL_svn/ANCIL
+
+set::trac::ancil             http://fcm8/projects/ANCIL/browser/ANCIL
+
+# ATSR repository
+set::url::atsr               svn://fcm7/ATSR_svn/ATSR
+
+set::trac::atsr              http://fcm7/projects/ATSR/browser/ATSR
+
+# CICE repository
+set::url::cice               svn://fcm3/CICE_svn/CICE
+
+set::trac::cice              http://fcm3/projects/CICE/browser/CICE
+
+# CVC repository
+set::url::cvc_admin          svn://fcm6/CVC_svn/Admin
+set::url::bullseye           svn://fcm6/CVC_svn/Bullseye
+set::url::cat                svn://fcm6/CVC_svn/CAT
+set::url::deicing            svn://fcm6/CVC_svn/Deicing
+set::url::ea                 svn://fcm6/CVC_svn/EA  
+set::url::ensemble           svn://fcm6/CVC_svn/Ensemble
+set::url::gales              svn://fcm6/CVC_svn/Gales
+set::url::ifv                svn://fcm6/CVC_svn/IFV
+set::url::mogreps            svn://fcm6/CVC_svn/MOGREPS
+set::url::openroad           svn://fcm6/CVC_svn/OpenRoad
+set::url::powertable         svn://fcm6/CVC_svn/PowerTable
+set::url::qnh                svn://fcm6/CVC_svn/QNH
+
+set::trac::cvc_admin         http://fcm6/projects/CVC/browser/Admin
+set::trac::bullseye          http://fcm6/projects/CVC/browser/Bullseye
+set::trac::cat               http://fcm6/projects/CVC/browser/CAT
+set::trac::deicing           http://fcm6/projects/CVC/browser/Deicing
+set::trac::ea                http://fcm6/projects/CVC/browser/EA
+set::trac::ensemble          http://fcm6/projects/CVC/browser/Ensemble
+set::trac::gales             http://fcm6/projects/CVC/browser/Gales
+set::trac::ifv               http://fcm6/projects/CVC/browser/IFV
+set::trac::mogreps           http://fcm6/projects/CVC/browser/MOGREPS
+set::trac::openroad          http://fcm6/projects/CVC/browser/OpenRoad
+set::trac::powertable        http://fcm6/projects/CVC/browser/PowerTable
+set::trac::qnh               http://fcm6/projects/CVC/browser/QNH
+
+# DA repository
+set::url::da                 svn://fcm5/DA_svn/DA
+
+set::trac::da                http://fcm5/projects/DA/browser/DA
+
+# ENS repository
+set::url::ens                svn://fcm9/ENS_svn/ENS
+
+set::trac::ens               http://fcm9/projects/ENS/browser/ENS
+
+# FCM repository
+set::url::fcm                svn://fcm1/FCM_svn/FCM
+set::url::fcm_admin          svn://fcm1/FCM_svn/Admin
+
+set::trac::fcm               http://fcm1/projects/FCM/browser/FCM
+set::trac::fcm_admin         http://fcm1/projects/FCM/browser/Admin
+
+# FLUME repository
+set::url::flume_metadata     svn://fcm2/FLUME_svn/metadata
+
+set::trac::flume_metadata    http://fcm2/projects/FLUME/browser/metadata
+
+# GEN repository
+set::url::gen                svn://fcm1/GEN_svn/GEN
+
+set::trac::gen               http://fcm1/projects/GEN/browser/GEN
+
+# LEM repository
+set::url::lem                svn://fcm2/LEM_svn/LEM
+
+set::trac::lem               http://fcm2/projects/LEM/browser/LEM
+
+# LINK repository
+set::url::link               svn://fcm1/LINK_svn/LINK
+
+set::trac::link              http://fcm1/projects/LINK/browser/LINK
+
+# NEMO repository
+set::url::nemosys            svn://fcm3/NEMO_svn/NEMOSYS
+
+set::trac::nemosys           http://fcm3/projects/NEMO/browser/NEMOSYS
+
+# NWPWEB repository
+set::url::www_nwp            svn://fcm1/NWPWEB_svn/www_nwp
+
+set::trac::www_nwp           http://fcm1/projects/NWPWEB_svn/browser/www_nwp
+
+# obsmon repository
+set::url::obsmon_dc          svn://fcm4/obsmon_svn/DC
+set::url::obsmon_rtm         svn://fcm4/obsmon_svn/RTM
+
+set::trac::obsmon_dc         http://fcm4/projects/obsmon/browser/DC
+set::trac::obsmon_rtm        http://fcm4/projects/obsmon/browser/RTM
+
+# OCN repository
+set::url::polcoms            svn://fcm3/OCN_svn/POLCOMS
+
+set::trac::polcoms           http://fcm3/projects/OCN/browser/POLCOMS
+
+# OPFC repository
+set::url::opfc               svn://fcm9/OPFC_svn/OPFC
+
+set::trac::opfc              http://fcm9/projects/OPFC/browser/OPFC
+
+# OPS repository
+set::url::ops                svn://fcm4/OPS_svn/OPS
+set::url::ops_admin          svn://fcm4/OPS_svn/Admin
+set::url::ops_data           svn://fcm4/OPS_svn/Data
+
+set::trac::ops               http://fcm4/projects/OPS/browser/OPS
+set::trac::ops_admin         http://fcm4/projects/OPS/browser/Admin
+set::trac::ops_data          http://fcm4/projects/OPS/browser/Data
+
+# OSTIA repository
+set::url::ostia              svn://fcm3/OSTIA_svn/OSTIA
+
+set::trac::ostia             http://fcm3/projects/OSTIA/browser/OSTIA
+
+# PF repository
+set::url::pf                 svn://fcm5/PF_svn/PF
+
+set::trac::pf                http://fcm5/projects/PF/browser/PF
+
+# PostProc repository
+set::url::pp                 svn://fcm9/PostProc_svn/PostProc
+set::url::ppancil            svn://fcm9/PostProc_svn/PostProcAncil
+
+set::trac::pp                http://fcm9/projects/PostProc/browser/PostProc
+set::trac::ppancil           http://fcm9/projects/PostProc/browser/PostProcAncil
+
+# PRISM repository
+set::url::oasis4             svn://fcm2/PRISM_svn/oasis4
+
+set::trac::oasis4            http://fcm2/projects/PRISM/browser/oasis4
+
+# radarnet repository
+set::url::radarnet4          svn://fcm9/radarnet_svn/radarnet4
+
+set::trac::radarnet4         http://fcm7/projects/radarnet/browser/radarnet4
+
+# ROPP repository
+set::url::ropp_doc           svn://fcm7/ROPP_svn/ropp_doc
+set::url::ropp_src           svn://fcm7/ROPP_svn/ropp_src
+set::url::ropp_test          svn://fcm7/ROPP_svn/ropp_test
+set::url::ropp_web           svn://fcm7/ROPP_svn/ropp_web
+
+set::trac::ropp_doc          http://fcm7/projects/ROPP/browser/ropp_doc
+set::trac::ropp_src          http://fcm7/projects/ROPP/browser/ropp_src
+set::trac::ropp_test         http://fcm7/projects/ROPP/browser/ropp_test
+set::trac::ropp_web          http://fcm7/projects/ROPP/browser/ropp_web
+
+# RTTOV repository
+set::url::rttov8             svn://fcm7/RTTOV_svn/RTTOV8
+set::url::rttov9             svn://fcm7/RTTOV_svn/RTTOV9
+
+set::trac::rttov8            http://fcm7/projects/RTTOV/browser/RTTOV8
+set::trac::rttov9            http://fcm7/projects/RTTOV/browser/RTTOV9
+
+# SAUtils repository
+set::url::autoscat_global      svn://fcm7/SAUtils_svn/AUTOSCAT_Global
+set::url::autoscat_nae         svn://fcm7/SAUtils_svn/AUTOSCAT_NAE
+set::url::gpsiwv_mon           svn://fcm7/SAUtils_svn/GPSIWV_Mon
+set::url::gpsro_mon            svn://fcm7/SAUtils_svn/GPSRO_Mon
+set::url::iasi_mon             svn://fcm7/SAUtils_svn/IASI_Mon
+set::url::metstrike            svn://fcm7/SAUtils_svn/METSTRIKE
+set::url::scatwind_mon         svn://fcm7/SAUtils_svn/Scatwind_Mon
+
+set::trac::autoscat_global     http://fcm7/projects/SAUtils/browser/AUTOSCAT_Global
+set::trac::autoscat_nae        http://fcm7/projects/SAUtils/browser/AUTOSCAT_NAE
+set::trac::gpsiwv_mon          http://fcm7/projects/SAUtils/browser/GPSIWV_Mon
+set::trac::gpsro_mon           http://fcm7/projects/SAUtils/browser/GPSRO_Mon
+set::trac::iasi_mon            http://fcm7/projects/SAUtils/browser/IASI_Mon
+set::trac::metstrike           http://fcm7/projects/SAUtils/browser/METSTRIKE
+set::trac::scatwind_mon        http://fcm7/projects/SAUtils/browser/Scatwind_Mon
+
+# SBV repository
+set::url::sbv                svn://fcm6/SBV_svn/SBV
+set::url::sbv_admin          svn://fcm6/SBV_svn/Admin
+
+set::trac::sbv               http://fcm6/projects/SBV/browser/SBV
+set::trac::sbv_admin         http://fcm6/projects/SBV/browser/Admin
+
+# SCS repository
+set::url::scs                svn://fcm1/SCS_svn/SCS
+set::url::tik                svn://fcm1/SCS_svn/TIK
+set::url::tt                 svn://fcm1/SCS_svn/TT
+
+set::trac::scs               http://fcm1/projects/SCS/browser/SCS
+set::trac::tik               http://fcm1/projects/SCS/browser/TIK
+set::trac::tt                http://fcm1/projects/SCS/browser/TT
+
+# SPS repository
+set::url::sps                svn://fcm7/SPS_svn/SPS
+set::url::sps_archive        svn://fcm7/SPS_svn/Archive
+
+set::trac::sps               http://fcm7/projects/SPS/browser/SPS
+set::trac::sps_archive       http://fcm7/projects/SPS/browser/Archive
+
+# SURF repository
+set::url::surf               svn://fcm8/SURF_svn/SURF
+
+set::trac::surf              http://fcm8/projects/SURF/browser/SURF
+
+# test repository
+set::url::test               svn://fcm1/test_svn/OPS
+
+set::trac::test              http://fcm1/projects/test/browser/OPS
+
+# tutorial repository
+set::url::tutorial           svn://fcm1/tutorial_svn/tutorial
+
+set::trac::tutorial          http://fcm1/projects/tutorial/browser/tutorial
+
+# TRUI repository
+set::url::trui               svn://fcm1/TRUI_svn/TRUI
+
+set::trac::trui              http://fcm1/projects/TRUI/browser/TRUI
+
+# UM repository
+set::url::um                 svn://fcm2/UM_svn/UM
+set::url::um_admin           svn://fcm2/UM_svn/Admin
+set::url::gcom               svn://fcm2/UM_svn/GCOM
+
+set::trac::um                http://fcm2/projects/UM/browser/UM
+set::trac::um_admin          http://fcm2/projects/UM/browser/Admin
+set::trac::gcom              http://fcm2/projects/UM/browser/GCOM
+
+# UM tutorial repository
+set::url::um_tutorial        svn://fcm2/UM_TUTORIAL_svn/UM
+
+set::trac::um_tutorial       http://fcm2/projects/UM_TUTORIAL/browser/UM
+
+# VAR repository
+set::url::var                svn://fcm5/VAR_svn/VAR
+set::url::var_admin          svn://fcm5/VAR_svn/Admin
+set::url::var_data           svn://fcm5/VAR_svn/Data
+
+set::trac::var               http://fcm5/projects/VAR/browser/VAR
+set::trac::var_admin         http://fcm5/projects/VAR/browser/Admin
+set::trac::var_data          http://fcm5/projects/VAR/browser/Data
+
+# VER repository
+set::url::ver                svn://fcm6/VER_svn/VER
+set::url::ver_admin          svn://fcm6/VER_svn/Admin
+set::url::ver_archive        svn://fcm6/VER_svn/Archive
+
+set::trac::ver               http://fcm6/projects/VER/browser/VER
+set::trac::ver_admin         http://fcm6/projects/VER/browser/Admin
+set::trac::ver_archive       http://fcm6/projects/VER/browser/Archive
+
+# EOF
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/etc/fcm_gui.desktop
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/etc/fcm_gui.desktop	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/etc/fcm_gui.desktop	(revision 5058)
@@ -0,0 +1,13 @@
+[Desktop Entry]
+Comment=
+Exec=fcm gui %f
+Hidden=false
+Icon=wizard
+MimeType=inode/directory
+Name=FCM GUI
+Path=
+Terminal=0
+TerminalOptions=
+Type=Application
+X-KDE-SubstituteUID=false
+X-KDE-Username=
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Ecmwf/Fortran90_stuff.pm
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Ecmwf/Fortran90_stuff.pm	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Ecmwf/Fortran90_stuff.pm	(revision 5058)
@@ -0,0 +1,2736 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# NAME
+#   Ecmwf::Fortran90_stuff
+#
+# DESCRIPTION
+#   This is a module for analysing Fortran 9X code. It is used by the FCM
+#   system to generate interface blocks for Fortran 9X free source files.
+#
+# ABOUT THIS MODULE:
+#   The original version of this module was developed by the European Centre
+#   for Medium-Range Weather Forecasts (ECMWF). This version has been modified
+#   by UK Met Office to become part of the FCM system.
+# ------------------------------------------------------------------------------
+
+package Ecmwf::Fortran90_stuff;
+
+# Standard pragmas
+use strict;
+use warnings;
+
+# Standard modules
+require Exporter;
+
+our @ISA      = qw(Exporter);
+our @EXPORT   = qw(study setup_parse pre_tidy remove_macro expcont 
+                   process_include_files tidy tidy_decl getvars 
+		   find_unused_vars remove_unused_vars doctor_viol
+		   fix_doctor_viol various cont_lines f90_indent
+		   writefile readfile create_interface_block
+		   add_interface_blocks change_var_names insert_hook
+		   remake_arg_decl remove_some_comments parse_prog_unit
+		   get_calls_inc);
+
+# ------------------------------------------------------------------------------
+
+# Module variables
+
+my $fname = '';
+
+# ------------------------------------------------------------------------------
+# SYNPOSIS
+#   $file = &Ecmwf::Fortran90_stuff::fname ();
+#   &Ecmwf::Fortran90_stuff::fname ($file);
+#
+# DESCRIPTION
+#   This function returns the value in the module variable $fname, which is the
+#   name of the input Fortran source file from FCM. If an argument exists, the
+#   value of $fname is set to the value of the argument.
+#
+# ------------------------------------------------------------------------------
+
+sub fname {
+  $fname = $_[0] if @_;
+  return $fname;
+}
+
+#==========================================================================
+sub study{
+# Study statements and put attributes into array $statements
+# Attributes assigned:
+# $href->{content}       - What statement it is
+# $href->{decl}       - true if declaration,
+#                       5 means statement function
+#                       4 means USE statement,
+#                       2 means type decleration
+#                       3 means FORMAT statement
+#                       1 means the rest
+# $href->{in_contain} - true while in internal procedure(s)
+# $href->{exec}       - true if executable statement
+#                     - 2 means first executable statement in program unit
+#                     - 3 means last executable statement in program unit
+#                     - 23 means first and last executable statement in program unit
+# $href->{prog_unit}  - program unit number (numbered from 0)
+# $href->{number}     - statement number (numbered from 0)
+ 
+# Further attributes will be assigned later (action attributes)
+
+  my($statements,$prog_info) = @_;
+
+  our ($name,$nest_par);
+  my ($unit_name,@args,$prog_unit,$href,@punit,$current_punit);
+  my ($content,$decl,$exec);
+  my($type_def)=0;
+  my($unit_count)=-1;
+  @punit=();
+  $current_punit='';
+  my $number=-1;
+  my $in_contain=0;
+  my $in_interface=0;
+  my $contain_host='';
+  my $current_unit_name='';
+  our($study_called);
+#  if(! $study_called) {
+#    $$prog_info{has_interface_block}=0;
+#  }
+# Initial "parsing" loop
+
+  foreach $href (@$statements) {
+    $href->{in_contain}=$in_contain;
+    $href->{contain_host}=$contain_host if($in_contain);
+    $number++;
+    $_=$href->{statement};
+    $content='unknown';
+    my $content2='';
+    $decl=0;
+    $exec=0;
+
+    if($type_def) {
+ #     $href->{content}='typedef';
+    }
+    
+# Comment
+CRACK:    {
+      if(/^\s*(?:!|$)/) {
+	$content='comment';
+	last CRACK;
+      }
+
+      $_ = uc unless /^#/;
+      s/^\s*//;
+      s/\!.*\n/\n/g; # Remove trailing comments in all lines
+#      print "AA $_";
+ 
+# Program name statement 
+      if($content eq 'unknown' and ! $in_interface) {
+	$prog_unit=&parse_prog_unit(\$unit_name,\@args);
+	if($prog_unit) {
+          $current_unit_name=$unit_name;
+	  $content=uc($prog_unit);
+	  push(@punit,$prog_unit);
+	  $current_punit=$prog_unit;
+	  $unit_count++;
+	  if(! $study_called) {
+	    $$prog_info{'unit_name'}[$unit_count]=uc($unit_name);
+	    $$prog_info{'unit_name'}[$unit_count]=uc($unit_name);
+#	  $$prog_info{'tokens'}[$unit_count]=[];
+	    if($prog_unit eq 'module') {
+	      $$prog_info{'is_module'}=1;
+	      $$prog_info{'module_name'}=$unit_name;
+	    }
+	  }
+	  last CRACK;
+	}
+      }
+      if($content eq 'unknown') {
+	$decl=0;
+	$exec=1;
+# Executable constructs
+	&study_exec(\$content,$prog_info,\$study_called);
+	if($content eq 'IF') {
+	  s/^IF\s*$nest_par\s*//;
+	  &study_exec(\$content2,$prog_info,\$study_called);
+	}
+      }
+      
+
+      if($content eq 'unknown') {
+# Specification statemnts
+	$exec=0;
+	$decl=1;
+	if(/^USE\b/) {
+	  $content='USE';
+	  $decl=4;
+	}
+	elsif(/^INTEGER\b/) {
+	  $content='INTEGER';
+	  $decl=2;
+	}
+	elsif(/^REAL\b/) {
+	  $content='REAL';
+	  $decl=2;
+	}
+	elsif(/^LOGICAL\b/) {
+	  $content='LOGICAL';
+	  $decl=2;
+	}
+	elsif(/^CHARACTER\b/) {
+	  $content='CHARACTER';
+	  $decl=2;
+	}
+	elsif(/^DOUBLE\s*PRECISION\b/) {
+	  $content='DOUBLE PRECISION';
+	  $decl=2;
+	}
+	elsif(/^COMPLEX\b/) {
+	  $content='COMPLEX';
+	  $decl=2;
+	}
+	elsif(/^TYPE *\(/) {
+	  $content='type_decl';
+	  $decl=2;
+	}
+	elsif(/^ALLOCATABLE\b/) {
+	  $content='ALLOCATABLE';
+	}
+	elsif(/^COMMON\b/) {
+	  $content='COMMON';
+	}
+	elsif(/^DATA\b/) {
+	  $content='DATA';
+	}
+	elsif(/^DIMENSION\b/) {
+	  $content='DIMENSION';
+	}
+	elsif(/^EQUIVALENCE\b/) {
+	  $content='EQUIVALENCE';
+	}
+	elsif(/^EXTERNAL\b/) {
+	  $content='EXTERNAL';
+	}
+	elsif(/^\d+\s+FORMAT\b/) {
+	  $content='FORMAT';
+	  $decl=3;
+	}
+	elsif(/^IMPLICIT\b\s+NONE\b/) {
+	  $content='IMPLICIT NONE';
+	}
+	elsif(/^IMPLICIT\b/) {
+	  $content='IMPLICIT';
+	}
+	elsif(/^INTENT\b/) {
+	  $content='INTENT';
+	}
+	elsif(/^INTRINSIC\b/) {
+	  $content='INTRINSIC';
+	}
+	elsif(/^NAMELIST\b/) {
+	  $content='NAMELIST';
+	}
+	elsif(/^OPTIONAL\b/) {
+	  $content='OPTIONAL';
+	}
+	elsif(/^PARAMETER\b/) {
+	  $content='PARAMETER';
+          $decl = 2;
+	}
+	elsif(/^POINTER\b/) {
+	  $content='POINTER';
+	}
+	elsif(/^PUBLIC\b/) {
+	  $content='PUBLIC';
+	}
+	elsif(/^PRIVATE\b/) {
+	  $content='PRIVATE';
+	}
+	elsif(/^SAVE\b/) {
+	  $content='SAVE';
+	}
+	elsif(/^TARGET\b/) {
+	  $content='TARGET';
+	}
+	elsif(/^SEQUENCE\b/) {
+	  $content='SEQUENCE';
+	}
+	elsif(/^INTERFACE\b/) {
+	  $content='INTERFACE';
+	  if(! $study_called) {
+	    $$prog_info{has_interface_block}=1;
+	    $in_interface=1;
+	  }
+	}
+	elsif(/^END ?INTERFACE\b/) {
+	  $content='END INTERFACE';
+	    $in_interface=0;
+	}
+	elsif(/^TYPE *[^\( ]/i) {
+	  $content='type_def';
+	  $type_def=1;
+	}
+	elsif(/^END\s*TYPE\b/){
+	  $content='type_def';
+	  $type_def=0;
+	}
+        elsif( $in_interface ) {
+          if(/^MODULE PROCEDURE\b/) {
+            $content='MODULE PROCEDURE';
+          }
+        }
+      }
+# Other constructs
+      if($content eq 'unknown') {
+	$decl=0;
+	$exec=0;
+	
+	if(/^CONTAINS\b/) {
+	  $content='CONTAINS';
+	  $in_contain=1;
+          $contain_host=uc($current_unit_name);
+	  if(! $study_called) {
+	    $$prog_info{has_contain}=1;
+	    $$prog_info{containing}=1;
+	  }
+	}
+	elsif(/^(?:INCLUDE|#include)\b/) {
+	  $content='include';
+	  if(! $study_called) {
+	    $$prog_info{has_include}=1;
+	  }
+	}
+	elsif(/^\#/) {
+	  $content='cpp';
+	}
+	elsif(/^\@/) {
+	  $content='compiler_directive';
+	}
+	
+	else{
+	  if(/^END\b/ and ! $in_interface) {
+	    $prog_unit=pop(@punit);
+	    $content='END '.uc($prog_unit);
+	    if($in_contain) {
+	      unless(@punit) {
+		$unit_count=0;
+		$href->{in_contain}=0;
+		$in_contain=0;
+	      }
+	    }
+	  }
+	} 
+      }
+    }
+    
+    if($in_interface and $content ne 'INTERFACE') {
+      $content='in_interface';
+      $exec=0;
+      $decl=1;
+    }
+
+#    print "BB $unit_count $content $_";
+    if($content  eq 'unknown') {
+      print STDERR $fname, ': failed to crack statement starting at line ',
+                   $href->{first_line}, ', - syntax error?', "\n";
+      print STDERR ' ', $_, "\n";
+#      print STDERR "study_called $study_called in_interface $in_interface \n";
+#      print STDERR Dumper($statements);
+      #die "Failed in study";
+    }
+#    unless($content eq 'comment') {
+#      my @tmpvar=/\b$name\b/g;
+#      my $i=0;
+#      foreach my $tmp (@tmpvar){ 
+#	$href->{'tokens'}[$i]=$tmp;
+#	$i++;
+#	if(! $study_called and $unit_count > -1) {
+#	  $$prog_info{'token_hash'}[$unit_count]{$tmp}++;
+#	}
+#      }
+#    }
+               
+    $href->{content}=$content;
+    $href->{content2}=$content2 if($content2);
+    $href->{decl}=$decl;
+    $href->{exec}=$exec;
+#    $href->{type_decl}=$type_decl;
+    $href->{prog_unit}=$unit_count;
+    $href->{number}=$number;
+    unless($content eq 'comment') {
+      $href->{multi_line} = 1 if(tr/\n// > 1);
+    }
+  }
+
+
+# Find first executable statement in each program unit
+# Also repair statement functions wrongly assigned as executable
+  my $prev_unit_count=-2;
+  my $stat_func_suspicion=0;
+  my @lastexec=();
+
+  foreach $href (@$statements) {
+    $exec=$href->{exec};
+    $unit_count=$href->{prog_unit};
+    if($exec) {
+      if($unit_count > $prev_unit_count) {
+	$content=$href->{content};
+	if($content eq 'array_assign') {
+	  $stat_func_suspicion=1;
+	  $_=$href->{statement};
+	  if(/^\s*$name\s*\(\s*:/){
+	    $stat_func_suspicion=0;
+#	    print " A $_";
+	   } 
+	  elsif(/^\s*$name\s*\(\s*$name\s*:/){
+	    $stat_func_suspicion=0;
+#	    print " B $_";
+	  } 
+	  elsif(/^\s*$name\s*\(\s*\d+/){
+	    $stat_func_suspicion=0;
+#	    print " C $_";
+	  }
+	  else {
+	    $href->{exec}=0;
+	    $href->{decl}=5;
+	    $href->{content}='statmf';
+#	    print " D $_";
+	    next;
+	  }
+	}
+	$href->{exec}=2;
+	$prev_unit_count=$unit_count;
+	$content=$href->{content};
+      }
+      $lastexec[$unit_count]=$href->{number}  unless ($unit_count < 0);  
+# No prog_unit assigned, include file?
+    }
+  }
+
+# Assign last executable statement
+  if(@lastexec) {
+    foreach my $last (@lastexec) {
+      if(defined ($last)) {
+	if($$statements[$last]->{exec} == 1) {
+	  $$statements[$last]->{exec}=3;
+	}
+	else{
+	  $$statements[$last]->{exec}=23;
+	}      
+      }
+    }
+  }
+# Consistency checks
+  my $fail=0;
+  my $prev_exec=0;
+  $prev_unit_count=-1;
+  foreach $href (@$statements) {
+    $content=$href->{content};
+    next if($content eq 'comment');
+    $unit_count=$href->{prog_unit};
+    $exec=$href->{exec};
+    $decl=$href->{decl};
+    if($unit_count == $prev_unit_count) {
+      if($decl and $prev_exec) {
+	unless ($content eq 'FORMAT' | $content eq 'DATA' ) {
+          die $fname, ': declaration after executable statement', "\n",
+              $href->{first_line}, ' ', $href->{statement}, "\n";
+	}
+      }
+    }
+    $prev_unit_count=$unit_count;
+    $prev_exec=$exec;
+  }
+
+  $study_called=1;
+}
+
+#==========================================================================
+sub study_exec{
+  my($content,$prog_info,$study_called) = @_;
+  our ($name,$nest_par);
+  if(/^(\w+\s*:\s*)*IF\s*$nest_par\s*THEN/) {
+    $$content='IF_construct';
+  }
+  elsif(/^ELSE\s*IF\s*\(/) {
+    $$content='ELSEIF';
+  }
+  elsif(/^ELSE\b\s*($name)*/) {
+    $$content='ELSE';
+  }
+  elsif(/^END\s*IF\b\s*($name)*/) {
+    $$content='ENDIF';
+  }
+  elsif(/^(?:\d+\s+)?($name\s*:\s*)*DO(\s+WHILE)?\b/) {
+    $$content='DO';
+  }
+  elsif(/^(?:\d+\s+)?END\s*DO\b/) {
+    $$content='ENDDO';
+  }
+  elsif(/^(?:\d+\s+)?ALLOCATE\b/) {
+    $$content='ALLOCATE';
+  }
+  elsif(/^ASSIGN\b/) {
+    $$content='ASIGN';
+  }
+  elsif(/^(?:\d+\s+)?BACKSPACE\b/) {
+    $$content='BACKSPACE';
+  }
+  elsif(/^(?:\d+\s+)?CALL\b/) {
+    $$content='CALL';
+    if(!$$study_called) {
+      $$prog_info{no_calls}++;
+    }
+  }
+  elsif(/^(?:\d+\s+)?CLOSE\b/) {
+    $$content='CLOSE';
+  }
+  elsif(/^(?:\d+\s+)?CONTINUE\b/) {
+    $$content='CONTINUE';
+  }
+  elsif(/^(?:\d+\s+)?CYCLE\b/) {
+    $$content='CYCLE';
+  }
+  elsif(/^(?:\d+\s+)?DEALLOCATE\b/) {
+    $$content='DEALLOCATE';
+  }
+  elsif(/^ENDFILE\b/) {
+    $$content='ENDFILE';
+  }
+  elsif(/^(?:\d+\s+)?EXIT\b/) {
+    $$content='EXIT';
+  }
+  elsif(/^(?:\d+\s+)?GO\s*TO\b/) {
+    $$content='GOTO';
+  }
+  elsif(/^(?:\d+\s+)?IF\s*\(/) {
+    $$content='IF';
+  }
+  elsif(/^(?:\d+\s+)?INQUIRE\b/) {
+    $$content='INQUIRE';
+  }
+  elsif(/^(?:\d+\s+)?NULLIFY\b/) {
+    $$content='NULLIFY';
+  }
+  elsif(/^(?:\d+\s+)?OPEN\b/) {
+    $$content='OPEN';
+  }
+  elsif(/^(?:\d+\s+)?PAUSE\b/) {
+    $$content='PAUSE';
+  }
+  elsif(/^(?:\d+\s+)?PRINT\b/) {
+    $$content='PRINT';
+  }
+  elsif(/^(?:\d+\s+)?(?:READ|BUFFER\s*IN)\b/) {
+    $$content='READ';
+  }
+  elsif(/^(?:\d+\s+)?RETURN\b/) {
+    $$content='RETURN';
+  }
+  elsif(/^(?:\d+\s+)?REWIND\b/) {
+    $$content='REWIND';
+  }
+  elsif(/^(?:\d+\s+)?STOP\b/) {
+    $$content='STOP';
+  }
+  elsif(/^(?:\d+\s+)?(?:WRITE|BUFFER\s*OUT)\s*\(/) {
+    $$content='WRITE';
+  }
+  elsif(/^(?:\d+\s+)?($name\s*:\s*)*SELECT\s*CASE\b/) {
+    $$content='SELECT CASE';
+  }
+  elsif(/^(?:\d+\s+)?CASE\b/) {
+    $$content='CASE';
+  }
+  elsif(/^(?:\d+\s+)?END\s*SELECT\b/) {
+    $$content='END SELECT';
+  }
+  elsif(/^(?:\d+\s+)?WHERE\s*$nest_par\s*$name.*=/) {
+    $$content='WHERE';
+  }
+  elsif(/^(?:\d+\s+)?WHERE\s*\(/) {
+    $$content='WHERE_construct';
+  }
+  elsif(/^ELSE\s*WHERE\b/) {
+    $$content='ELSEWHERE';
+  }
+  elsif(/^END\s*WHERE\b/) {
+    $$content='ENDWHERE';
+  }
+  elsif(/^(?:\d+\s+)?FORALL\s*\(/) {
+    $$content='FORALL';
+  }
+  elsif(/^END\s*FORALL\b/) {
+    $$content='ENDFORALL';
+  }
+  elsif(/^(?:\d+\s+)?$name(?:\s*%\s*$name)*\s*=/o) {
+    $$content='scal_assign';
+  }
+  elsif(/^(?:\d+\s+)?$name(?:\s*$nest_par)*(?:\s*%\s*$name(?:\s*$nest_par)?)*\s*=/o) {
+    $$content='array_assign';
+  }
+}
+#===================================================================================
+sub pre_tidy {
+
+# Initial tidying to make the rest work
+
+  my($lines)=@_;
+  foreach (@$lines) {
+
+# Substitute tab with four blanks
+    s/\t/    /g;
+    s/^ *INTEGER /INTEGER_M /i;
+    s/^ *REAL /REAL_B /i;
+  }
+}
+#==========================================================================
+sub remove_macro {
+
+# Remove INTEGER_M, _ONE_ etc. macros and replace by expanded statement
+
+  my($lines)=@_;
+
+  my($im)=1; # Until I start checking include files
+  my($ia)=0;
+  my($ib)=0;
+  my($rb)=1; # Until I start checking include files
+  my($is)=0;
+  my($rh)=0;
+  my($rm)=0;
+  my(@pars,$string);
+  for (@$lines) {
+    next if(/^ *$/ | /^ *!/);
+# The following two substitutions should be restored at end of processing
+    s/(\'[^!]*)!+(.*\')/$1\£$2/;   # Protect against mischief 
+    s/(["][^!]*)!+(.*["])/$1\£$2/;      # Protect against mischief
+    $im=$im+/JPIM\b/i unless($im);
+    $rb=$rb+/JPRB\b/i unless($rb);
+    $rm=$rm+/JPRM\b/i unless($rm);
+    $im=$im+s/\bINTEGER_M\b/INTEGER(KIND=JPIM)/o;
+    $ia=$ia+s/\bINTEGER_A\b/INTEGER(KIND=JPIA)/o;
+    $ib=$ib+s/\bINTEGER_B\b/INTEGER(KIND=JPIB)/o;
+    $is=$is+s/\bINTEGER_S\b/INTEGER(KIND=JPIS)/o;
+    $rb=$rb+s/\bREAL_B\b/REAL(KIND=JPRB)/o;
+    $rh=$rh+s/\bREAL_H\b/REAL(KIND=JPRH)/o;
+    $rm=$rm+s/\bREAL_M\b/REAL(KIND=JPRM)/o;
+    $rb=$rb+s/\b_ZERO_\b/0.0_JPRB/og;
+    $rb=$rb+s/\b_ONE_\b/1.0_JPRB/og;
+    $rb=$rb+s/\b_TWO_\b/2.0_JPRB/og;
+    $rb=$rb+s/\b_HALF_\b/0.5_JPRB/og;
+  }
+  @pars=();
+  push(@pars,"JPIM") if $im;
+  push(@pars,"JPRB") if $rb;
+  push(@pars,"JPRM") if $rm;
+  push(@pars,"JPIA") if $ia;
+  push(@pars,"JPIB") if $ib;
+  push(@pars,"JPIS") if $is;
+  ($string=join('     ,',@pars))=~s/ *$//;
+  for (@$lines) {
+    next unless (/^\#/);
+    if(@pars) {
+      s/^#include +"tsmbkind.h"/USE PARKIND1  ,ONLY : $string/ ;
+    }
+    else {
+      s/^#include +"tsmbkind.h"//;
+    }
+#    if($rh) {
+      s/^#include +"hugekind.h"/USE PARKIND2  ,ONLY : JPRH/ ;
+#    }
+#    else {
+#      s/^#include +"hugekind.h"// ;
+#    }
+  }
+}
+
+#==========================================================================
+sub readfile  {
+# Read file 
+  my($fname)=@_;
+  my(@lines);
+  if(!open(INFIL,$fname)) {
+    print STDERR "Can't open $fname for reading\n";
+    die("Can't open $fname for reading\n");
+  }
+  @lines=<INFIL>;
+  close INFIL;
+  (@lines);
+}
+
+#==========================================================================
+sub writefile  {
+# Write file
+  my($fname,$lines)=@_;
+  if(!open(OUTFIL,">".$fname)) {
+    print STDERR "Can't open $fname for writing\n";
+    exit;
+  }
+  print OUTFIL @$lines;
+  close OUTFIL;
+}
+
+#==========================================================================
+sub expcont {
+#
+# Expand continuation lines into statements for free-format Fortran while
+# maintaining line-breaking and all comments
+# Put statements onto array of references to anonymous hashes as key 'statement'
+# Also put into the hash the linenumber of first line of statement as key 'first_line'
+  my ($lines, $statements) = @_;
+  my ($statm, $first_line);
+
+  my $prev        = 0;
+  my $line_number = 0;
+
+  for (@$lines) {
+    $line_number++;
+
+    s/^([^'"]*)(?:\s*!.*)$/$1/; # Remove trailing comments
+
+    s/^(\s*)&(.*)$/$1$2/s;
+
+    if (!/^\s*!.*$/ && /^.+?&(?:\s*!.*)*\s*$/) {
+      s/(.+?)&(.+)/$1\n/s;
+
+      $statm     .= $_;
+      $first_line = $line_number unless $prev;
+      $prev       = 1;
+      next;
+
+    } elsif ($prev && /^\s*(?:!|$)/) { # ignore blank/comment lines
+      next;
+
+    } else {
+      s/!.*?$//;
+
+      $statm     .= $_;
+      push @$statements, {
+        'statement'  => $statm,
+        'first_line' => $prev ? $first_line : $line_number,
+      };
+
+      $statm = "";
+      $prev  = 0;
+    }
+  }
+}
+#==========================================================================
+
+sub cont_lines {
+#
+# Put back continuation character in correct place and execute delayed actions
+#
+  my($statements,$lines,$line_hash) = @_;
+  my(@temp,$i,$iup,$href);
+
+
+# Put back continuation characters and split statements into lines as they were
+  @$lines=();
+  @$line_hash=();
+  foreach $href (@$statements) {
+    $_=$href->{statement};
+    if (/\n.*\n/){                      # This is a multi-line statement
+      @temp=split /\n/;                 # Split statement into lines (removes EOL)
+      $iup=scalar(@temp);               # Number of lines in statement
+      for ($i=0;$i < $iup;$i++) {       # Loop through lines
+	$_=$temp[$i];
+	if($i == 0 ){                   # First line
+	  if(/^([^!]+)(!.*)$/) {        # Line has trailing comment
+	    s/^([^!]+)(!.*)$/$1&$2\n/;  # Put back & at end of line before comment
+	  }
+	  else {                        # No trailing comment
+	    s/^([^!]+)$/$1&\n/;         # Put back & and EOL at end of line
+	  }	    
+	}
+	elsif ($i == ($iup-1)) {        # Last line
+	  s/^( *)(.*)$/$1& $2 \n/;      # Put back & at beginning of line
+	}
+	else {                          # Other lines
+	  if (/^ *!/) {                 # Line is comment line
+	    $_=$_."\n";                 # Restore EOL for comments
+	  }
+	  else {
+	    if(/^( *)([^!]*)(!.*)$/) {  # Line has trailing comment
+	      s/^( *)([^!]*)(!.*)*$/$1& $2&$3\n/;  # & at beginning and end of line
+	    }
+	    else {                      # No trailing comment
+	      s/^( *)([^!]*)$/$1& $2&\n/; # & at beggining and end of line
+	    }	 
+	  }  
+	}
+	if($i == 0        && exists $href->{pre_insert}) {
+	  my @templines=split('\n',$href->{pre_insert});
+	  foreach my $tline (@templines) {
+	    my $rec={};
+	    $rec->{'content'}='unknown';
+	    $rec->{'line'}=$tline."\n";
+	    push(@$lines,$rec->{'line'});
+	    push(@$line_hash,$rec);
+	  }
+	}
+	unless(exists $href->{remove}) {
+	  my $rec={};
+	  $rec->{'line'}=$_;
+	  if($i == 0) {
+	    $rec->{'content'}=$href->{content};
+	  }
+	  else {
+	    $rec->{'content'}='cont_line';
+	  }
+	  push(@$lines,$rec->{'line'});
+	  push(@$line_hash,$rec);
+	}
+	if($i == ($iup-1) && exists $href->{post_insert}) {
+	  my @templines=split('\n',$href->{post_insert});
+	  foreach my $tline (@templines) {
+	    my $rec={};
+	    $rec->{'content'}='unknown';
+	    $rec->{'line'}=$tline."\n";
+	    push(@$lines,$rec->{'line'});
+	    push(@$line_hash,$rec);
+	  }
+	}
+      }
+    }
+    else {  # Not multiline statement
+      if(exists $href->{pre_insert}) {
+	my @templines=split('\n',$href->{pre_insert});
+	foreach my $tline (@templines) {
+	  my $rec={};
+	  $rec->{'content'}='unknown';
+	  $rec->{'line'}=$tline."\n";
+	  push(@$lines,$rec->{'line'});
+	  push(@$line_hash,$rec);
+	}
+      }
+      unless(exists $href->{remove}) {
+	my $rec={};
+	$rec->{'line'}=$_;
+	$rec->{'content'}=$href->{content};
+	push(@$lines,$rec->{'line'});
+	push(@$line_hash,$rec);
+#	print $rec;
+      }
+      if(exists $href->{post_insert}) {
+	my @templines=split('\n',$href->{post_insert});
+	foreach my $tline (@templines) {
+	  my $rec={};
+	  $rec->{'content'}='unknown';
+	  $rec->{'line'}=$tline."\n";
+	  push(@$lines,$rec->{'line'});
+	  push(@$line_hash,$rec);
+	}
+      }
+    }
+  }
+}
+#==========================================================================
+sub getvars {
+# Return list of locally declared variables with type and scope information
+# 
+  my($statements,$prog_info,$vars,$use_vars) = @_;
+  my ($test,$type,@vars1,$func,$prog_unit,$dum,$tmp_name,@pu_args);
+  my ($preserve,$rank,$href);
+  our($nest_par,$name);
+
+  %$vars=();
+  $func="";
+  $prog_unit=0;
+  %$use_vars=();
+  foreach $href (@$statements) {
+    next if($href->{content} eq 'comment');           # Skip comments
+    next if($href->{exec});                        # Don't look in executable statements
+    next if($$prog_info{is_module} and ! $href->{in_contain}); # Unless inside CONTAIN skip module 
+    $prog_unit=$href->{prog_unit};
+    if($href->{content} eq 'FUNCTION') {
+      $_=$href->{statement};
+      my $dum=&parse_prog_unit(\$func,\@pu_args);          # Get name of FUNCTION
+#      print "GETVARS FUNCTION $func \n";
+      $func=uc($func);
+    }
+    if($href->{decl} == 2 or $href->{content} eq 'EXTERNAL'){  # Real parse starts
+      $_=$href->{statement};
+      $_=uc($_);                                   # Upcase to avoid /.../i
+      s/^ *//;                                     # remove leading blanks
+      if($href->{decl} == 2) {
+	$type=lc(substr($href->{content},0,1));
+      }
+      else {
+	$type='e';
+      }
+      s/\!.*\n/\n/g;                               # Remove trailing comments in all lines
+      $preserve=$_;
+      s/(.+)::(.+)/$2/s;                           #REAL(KIND=JPRB) :: zsig(:) -> zsig(:),
+      s/^EXTERNAL (.+)$/$1/;
+      s/\s+//g;                                    # Remove all white-space
+      if($href->{content} eq 'CHARACTER') {
+	s/($name)\*\d+/$1/g;
+	s/($name)\*$nest_par/$1/g;
+	s/($name)$nest_par\*\w+/$1/g;
+      }
+      s#=\(/.+/\)##;      # ZVAL(1:2)=(/1.0,2.0/) -> ZVAL(1:2)
+#?      s/=[^,\n]+//g;
+      s/$nest_par//g;     # ISEC3(SIZE(NSEC3)),ISEC4(SIZE(NSEC4)) -> ISEC3,ISEC4
+      s/=\w+//g;          # ZVAL=1.0 -> ZVAL
+      s@/.*/@@;           # What?
+      @vars1=split(',',$_);
+      for(@vars1) {
+	next unless /^$name$/;          # A bit of security
+	if($preserve =~ /\b$_\b *\(/ | $preserve =~ /DIMENSION/) {
+	  $rank=1;        # Variable is array
+	}
+	else {
+	  $rank=0;        # Variable is scalar
+	}
+	if($_ eq $func) {
+	  $$vars{$_}{type_spec}="f";
+	} 
+	else {
+	  if($href->{content} eq 'FUNCTION') {
+	    $$vars{$_}{type_spec}='f';
+	  }
+	  else {
+	    $$vars{$_}{type_spec}=$type;
+	  }
+	}
+	$$vars{$_}{scope}=$prog_unit;
+	$$vars{$_}{rank}=$rank;
+	$$vars{$_}{usage}='local';
+      }
+    }
+# Perhaps the variable is really a statement function?
+    if($href->{decl} == 5) {
+      $_=$href->{statement};
+      s/\s+//g;                                    # Remove all white-space
+      /^($name)\((.+)\)=/i;
+      my $tvar=uc($1);
+      my @stmf_args=split(',',$2);
+      if (exists($$vars{$tvar})) {
+	$$vars{$tvar}{type_spec}='s';
+#	print "STATMF OK $tvar \n ";
+      }
+      for (@stmf_args) {
+	if (exists($$vars{$_})) {
+	  $$vars{$_}{type_spec}='s';
+#	  print "STATMF ARG OK $_ \n ";
+	}
+      }
+    }
+  }
+# Perhaps instead the variable is a declaration of an external function?
+  my @extract=();                  # Extract part of statements for efficiency
+  foreach $href (@$statements) {
+    if($href->{exec}) {                 # Function call must be in executable stat.
+      next if($href->{content} eq 'CALL'); # A call can't contain an undeclared function
+      push(@extract,$href->{statement});
+    }
+  }
+  
+  foreach my $var (keys (%$vars)) {
+    next if($$vars{$var}{rank} > 0);   # Can't be a function if rank > 0
+    next if($$vars{$var}{type_spec} eq 's' | $$vars{$var}{type_spec} eq 'f');
+    my $dec_unit=$$vars{$var}{scope};
+    my $regex1=qr/\b$var\b\s*\(/i;      # As var's rank=0 this could be function call
+    for(@extract) {
+      if(/${regex1}/) {
+	s/\!.*\n/\n/g;                       # Remove trailing comments in all lines
+	s/\s+//g;                            # Remove all white-space
+	if(/${regex1}/) {
+	  if($$vars{$var}{type_spec} eq 'c') {   # Avoid CLVAR(1:3) etc.
+	    next if(/${regex1}\s*(\d+|$name)*\s*:\s*(\d+|$name)*\s*\)/);
+	  }
+#	  print "TYPE changed to function $var $_ \n";
+	  $$vars{$var}{type_spec}='f';
+	  last;
+	}
+      }
+    }
+  }
+# ---------------------------------------------------------------------
+# Assign  "usage" in Doctor sense to variable (default usage is 'local')
+# 
+  foreach $href (@$statements) {
+# Is the varaible a dummy argument
+    if($href->{content} eq 'FUNCTION' or $href->{content} eq 'SUBROUTINE') {
+      $_=$href->{statement};
+      @pu_args=();
+      my $dum=&parse_prog_unit(\$func,\@pu_args);   # Get arguments
+      for(@pu_args) {
+	if( exists $$vars{$_} ) {
+	  if($$vars{$_}{scope} == $href->{prog_unit}) {
+	    $$vars{$_}{usage}='arg';
+	  }
+
+	} else {
+          print STDERR "Argument $_ has not got a corresponding declaration " .
+                       "statement\n";
+          print STDERR "Bailing out at this point\n";
+          die "Bailing out";
+	}
+      }
+    }
+# Does the variable appear in a NAMELIST
+# We want to distinguish this for more lenient Doctor check
+    if($href->{content} eq 'NAMELIST') {
+      $_=$href->{statement};
+      s/\!.*\n/\n/g;     # Remove trailing comments in all lines
+      s/\s+//g;          # Remove all white-space
+      m:NAMELIST/\w+/(.+):;
+      my @namvars=split(',',uc($1));
+      for (@namvars) {
+	if( exists $$vars{$_} ) {
+	  if($$vars{$_}{scope} == $href->{prog_unit}) {
+	    $$vars{$_}{usage}='namvar';
+	  }
+	}
+      }
+    }
+    if(exists $href->{inc_statm}) { # We also have to look in include files
+      my $incs=$href->{inc_statm};
+      foreach my $hrefi (@$incs) {
+	if($hrefi->{content} eq 'NAMELIST') {
+	  $_=$hrefi->{statement};
+	  s/\!.*\n/\n/g;     # Remove trailing comments in all lines
+	  s/\s+//g;          # Remove all white-space
+	m:NAMELIST/\w+/(.+):;
+	  my @namvars=split(',',uc($1));
+	  for (@namvars) {
+	    if( exists $$vars{$_} ) {
+	      if($$vars{$_}{scope} == $href->{prog_unit}) {
+		$$vars{$_}{usage}='namvar';
+	      }
+	    }
+	  }
+	}
+      }
+    }
+  }
+# -----------------------------------------------------------------------------
+# Find use variables
+  my %use_count=();
+  foreach $href (@$statements) {
+    if($href->{content} eq 'USE') {
+      $prog_unit=$href->{prog_unit};
+      $_=$href->{statement};
+      s/\!.*\n/\n/g;                               # Remove trailing comments in all lines
+      s/\s+//g;                                    # Remove all white-space
+      $_=uc($_);                                   # Upcase to avoid /.../i
+      if(/^USE($name),ONLY:(.+)$/){
+	my $modname=$1;
+	if( exists $use_count{$modname}) {
+	  if($prog_unit == $use_count{$modname}) {
+	    print STDERR "-> $href->{statement}";
+            print STDERR "USE $modname appears more than once in program unit $prog_unit \n\n";
+
+	  }
+	}
+	$use_count{$modname} = $prog_unit;
+	my @usevars = split /,/ ,$2;
+	my %usevars=();
+	foreach my $usevar (@usevars) {
+	  $usevars{$usevar}++;
+	  $$use_vars{$usevar}{module}=$modname;
+	  $$use_vars{$usevar}{scope}=$prog_unit;
+	  $$use_vars{$usevar}{count}++;
+	}
+	foreach my $usevar (keys (%usevars)) {
+	  if($usevars{$usevar} >1) {
+	    print STDERR "DUPLICATE USE ONLY VARIABLE ",
+	    "$modname $usevar $prog_unit \n";
+	    $_=$href->{statement};
+	    s/\b$usevar\b//i;
+	    s/,\s*,/,/;
+	    s/,\s*\n$/\n/;
+	    s/\n *\n/\n/;
+	    s/^(.+:\s*),/$1/;
+	    $href->{statement}=$_;
+	  }
+	}
+      }
+      else {
+#	print "WARNING:USE without ONLY \n";
+      }
+    }
+  }
+}
+#==========================================================================
+sub find_unused_vars {
+# Find declared variables not used
+  my($statements,$vars,$unused_vars,$use_vars,$unused_use_vars) = @_;
+  my ($var,@tokens,$href);
+  @tokens=();
+# Find all tokens in file
+  foreach $href (@$statements) {
+    next if($href->{content} eq 'comment');
+    if(exists $href->{inc_statm}) {  # Look also in include files
+      my $incs=$href->{inc_statm};
+      foreach my $hrefi (@$incs) {
+	die "FUV $href->{content} $href->{statement}" unless exists $hrefi->{statement};
+	$_=$hrefi->{statement};
+	if(/\b[a-zA-Z]\w*\b/) {
+	  push(@tokens,/\b[a-zA-Z]\w*\b/g);
+	}
+      }
+    }
+    else {
+      $_=$href->{statement};
+      push(@tokens,/\b[a-zA-Z]\w*\b/g);
+    }
+  }
+  @tokens= map {uc} @tokens; # Upcase array of tokens, the variables are upper-case
+
+# Find out how many times the variable appears in array tokens
+  foreach $var (keys (%$vars)) {
+    $$vars{$var}{uses}=0;
+  }
+  foreach $var (keys (%$use_vars)) {
+    $$use_vars{$var}{uses}=0;
+  }
+  for (@tokens) {
+    if(exists($$vars{$_})){
+      $$vars{$_}{uses}++; 
+    }
+    if(exists($$use_vars{$_})){
+      $$use_vars{$_}{uses}++; 
+    }
+  }
+# If it appears only one time (which must be in a declaration) it is unused
+  @$unused_vars=();
+  foreach $var (keys (%$vars)) {
+    push(@$unused_vars,$var) if($$vars{$var}{uses} < 2);
+  }
+  @$unused_use_vars=();
+  foreach $var (keys (%$use_vars)) {
+    push(@$unused_use_vars,$var) if($$use_vars{$var}{uses} < 2);
+  }
+}
+#==========================================================================
+sub remove_unused_vars {
+# Does what it says on the tin
+  my($statements,$unused_vars,$unused_use_vars) = @_;
+  my ($var,$href);
+  our $nest_par;
+  for (@$unused_vars) {
+    $var=$_;
+    foreach $href (@$statements) {
+      $_=$href->{statement};
+      next unless(($href->{decl}) | ($href->{content} eq 'comment'));
+      if($href->{content} eq 'comment') {
+	next unless(/^ *!\$OMP/);
+      }
+      if(/\b$var\b/i) {
+#	print $_;
+	
+	if(/\b$var\b *\(/i) {
+#	  print "ZYZ $var $_";
+	  s/\b$var\b *$nest_par *(=\s*\(\/.*\/\))*//si;
+#	  print "ZZZ $var $_";
+	}
+	s/\b$var\b\s*=\s*\d+(\.\d*)*//i;
+	s/\b$var\b *(\* *\d+)*//i if($href->{content} eq 'CHARACTER') ;
+	s/\b$var\b//i; 
+#	print $_;
+        s/^.+:: *\n$//;
+        s/^.+:: *\!.*\n$//;
+#	print $_;
+        s/,\s*,/,/;
+#	print $_;
+	s/, *\n$/\n/;
+#	print $_;
+        s/(::\s*),(.+)$/$1$2/s;
+        s/\n *\n/\n/;
+        s/\n *!.*\n/\n/;
+	s/, *\n$/\n/;
+# Remove "empty" lines
+        s/^.+::\s*$//;
+        s/^.+::\s*=.*$//;
+        s/^.+::\s*!.*$//;
+#	print $_;
+        s/^CHARACTER *\*\d+ *\n$//i if($href->{content} eq 'CHARACTER') ;
+	$href->{statement}=$_;
+      }
+    }
+  }
+  for (@$unused_use_vars) {
+    $var=$_;
+    foreach $href (@$statements) {
+      next unless($href->{decl} == 4);
+      $_=$href->{statement};
+      next if(/PARKIND/); #I am sure this could be done betterh
+
+      if(/\b$var\b/i) {
+	s/\b$var\b//i;
+        s/,\s*,/,/;
+        s/,\s*\n$/\n/;
+        s/\n *\n/\n/;
+	s/^(.+:\s*),/$1/;
+	s/^.+:\s*$//;
+	$href->{statement}=$_;
+      }
+    }
+  }
+}
+#==========================================================================
+sub tidy_decl {
+# Tidy up declarions
+  my($statements) = @_;
+  my($href,$content);
+
+  foreach $href (@$statements) {
+    next unless($href->{decl} == 2);
+    $_=$href->{statement};
+    $content=$href->{content};
+    
+    if($content eq 'CHARACTER') {
+      s/CHARACTER *\* *(\w+)/CHARACTER \(LEN = $1\)/i; 
+      s/CHARACTER *\* *\(\*\)/CHARACTER \(LEN = \*\)/i;
+      s/CHARACTER *\* *\( *(\w+) *\)/CHARACTER \(LEN = $1)/i;
+    }
+    if($content eq 'INTEGER') {
+      if(/^ *INTEGER[^\(]/i) {
+	s/INTEGER\b/INTEGER(KIND=JPIM)/;
+      }
+    }
+    unless (/::/) {
+      s/^( *LOGICAL )/$1:: /i;
+      s/^( *INTEGER\(KIND=JPI\w\) )/$1:: /;
+      s/^( *REAL\(KIND=JPR\w\) )/$1:: /;
+      if(/^ *CHARACTER/i) {
+	if( s/^( *CHARACTER *\( *LEN *= *\w+ *\))/$1 :: /i) {
+	  $href->{statement}=$_;
+	  next;
+	}
+	if(s/^( *CHARACTER *\( *LEN *= *\* *\))/$1 :: /i) {
+	  $href->{statement}=$_;
+	  next;
+	}
+	s/^( *CHARACTER )/$1:: /i;
+      }
+    }
+    $href->{statement}=$_;
+  }
+}
+#==========================================================================
+
+sub doctor_viol {
+# Find Doctor violations
+
+  my($vars,$fix_doc) = @_;
+  my ($var,$type,$zz,$prog_unit,$usage);
+  %$fix_doc=();
+
+  foreach $var (keys (%$vars)) {
+    $type=$$vars{$var}{type_spec};
+    $prog_unit=$$vars{$var}{scope};
+    $usage=$$vars{$var}{usage};
+#    print "DOC $var $type $prog_unit $usage \n";
+    if($zz=&doc_char($type,$usage,$var)) {
+#      print "DOCTOR VIOL - ",$var," $type $zz $prog_unit\n";
+      $$fix_doc{$var}=$zz.'_'.$var.','.$prog_unit
+    }
+  }  
+}
+#==========================================================================
+
+sub fix_doctor_viol {
+# Fix Doctor violations
+  my($statements,$fix_doc) = @_;
+  my($doc_viol,$repl,$prog_unit,$cur_prog_unit,@allowed,$href,$content);
+  my($tmp_name,@pu_args);
+
+  @allowed=('NRGRI'); # Hack
+
+  VIOL:foreach $doc_viol (keys (%$fix_doc)) {
+    # Let's allow some violations
+    for (@allowed){ 
+      next VIOL if($doc_viol eq $_);
+    }
+
+    ($repl,$prog_unit)=split(',',$$fix_doc{$doc_viol});
+
+    print "FIX $repl $prog_unit \n";
+    foreach $href (@$statements) {
+      $content=$href->{content};
+      $_=$href->{statement};
+      if($href->{content} eq 'comment') {
+	next unless(/^ *!\$OMP/);
+      }
+      $cur_prog_unit=$href->{prog_unit};
+      if($prog_unit == $cur_prog_unit) {  # Could be fine in other program units
+	if(/\b$doc_viol\b/i) {
+	  s/%$doc_viol\b/_X_$doc_viol/ig; # Protect type-components
+	  s/\b$doc_viol\b/$repl/ig;
+	  s/_X_$doc_viol\b/%$doc_viol/ig; # Restore type-components
+	}
+      }
+      $href->{statement}=$_;
+    }
+  }
+  
+}
+#==========================================================================
+sub various{
+# 
+  my($statements,$prog_info,$vars) = @_;
+  my($punit,@args,$tmp_name,$cont,$statm);
+  my($href,$exec);
+  our $nest_par;
+#------------------------------------------------------------------
+# Remove unneccesary RETURN statement
+  foreach $href (@$statements) {
+    $cont=$href->{content};
+    if($cont eq 'RETURN') {
+      if($href->{exec} == 3) {   # $href->{exec} == 3 means last executable statement
+	$href->{remove} = 1;     # Post remove line for later
+      }
+    }
+  }
+
+
+# Make sure all CALL MPL_... has a CDSTRING argument
+  foreach $href (@$statements) {
+    $cont=$href->{content};
+    if($href->{content} eq 'CALL' ) {
+      $_=$href->{statement};
+      if(/^\s*CALL\s+MPL_/i) {
+	next if(/^\s*CALL\s+MPL_ABORT/i);
+	next if(/^\s*CALL\s+MPL_WRITE/i);
+	next if(/^\s*CALL\s+MPL_READ/i);
+	next if(/^\s*CALL\s+MPL_OPEN/i);
+	next if(/^\s*CALL\s+MPL_CLOSE/i);
+	next if(/^\s*CALL\s+MPL_INIT/i);
+	next if(/^\s*CALL\s+MPL_GROUPS_CREATE/i);
+	next if(/^\s*CALL\s+MPL_BUFFER_METHOD/i);
+	next if(/^\s*CALL\s+MPL_IOINIT/i);
+	next if(/^\s*CALL\s+MPL_CART_COORD/i);
+#	print "CDSTRING=$$prog_info{'unit_name'}[$href->{prog_unit}]: \n";
+	unless(/CDSTRING\s*=/i) {
+	  s/\)(\s)$/,CDSTRING=\'$$prog_info{'unit_name'}[$href->{prog_unit}]:\'\)$1/;
+	  $href->{statement}=$_;
+	}
+      }
+    }
+  }
+	
+
+
+#------------------------------------------------------------------
+# Add Standard Modification Line
+
+  my $start=0;
+  foreach $href (@$statements) {
+    $cont=$href->{content};
+    if($cont eq 'comment') {
+      $_=$href->{statement};
+      if($start) {                        # Found header - look for end of mod lines
+	if(/^ *$/ || /^! *------------------------/) {
+	  $href->{pre_insert} = "!        M.Hamrud      01-Oct-2003 CY28 Cleaning\n";
+	  last;
+	}
+	next;
+      }
+      $start=1 if(/^! +Modifications/i) ;  # This how the header should look
+      next;
+    }
+    last if($href->{exec});                # We have failed - bail out
+  }
+
+# Change subroutine and call multi-line statements so that the comma
+# beetwen variables comes at the end of the line
+  my @lines=();
+  foreach $href (@$statements) {
+    if(exists $href->{multi_line}) {
+      $cont=$href->{content};
+      if($cont eq 'SUBROUTINE' | $cont eq 'CALL' ) {
+	$statm=$href->{statement};
+	@lines=split "\n", $statm;
+	@lines = reverse @lines;
+	my $append_comma=0;
+	for (@lines) {
+#	  print "A $append_comma $_ \n";
+	  next if(/^ *!/);
+	  if($append_comma) {
+	    if(/\S *!.*$/) {
+	      s/(\S)( *!.*)$/$1,$2/;
+	    }
+	    else {
+	      s/(\S) *$/$1,/;
+	    }
+	  }
+	  $append_comma=s/^ *,//;
+#	  print "B $append_comma $_ \n";
+	}
+	@lines = reverse @lines;
+	$statm=join  "\n",@lines;
+	$statm=$statm."\n";
+	$href->{statement}=$statm;
+      }
+    }
+  }
+  our $name;
+  foreach $href (@$statements) {
+    if($href->{content} eq 'USE') {
+      $_=$href->{statement};
+      unless(/^\s*USE\s+$name\s*,\s*ONLY\s*:/i){
+	print $_;
+	print "WARNING:USE without ONLY \n";
+      }
+    }
+  }    
+}
+#==========================================================================
+sub insert_hook{
+# 
+  my($statements,$prog_info,$vars) = @_;
+  my($punit,@args,$tmp_name,$cont,$statm);
+  my($href,$exec);
+  our $nest_par;
+#------------------------------------------------------------------
+# Add HOOK function
+  my $unit_name='';
+  my $last_use=0;
+  my $hook_status=0;
+  my $in_contain=0;
+  my $prev_prog=0;
+  my ($decl,$remember);
+  foreach $href (@$statements) {
+    $cont=$href->{content};
+    next if($cont eq 'comment');
+
+    $decl=$href->{decl};
+    $exec=$href->{exec};
+    $in_contain=$href->{in_contain};
+    if(! $in_contain and $href->{prog_unit} > $prev_prog) {
+      $hook_status=0;
+      $prev_prog=$href->{prog_unit};
+      print "resetting hook status \n";
+    }
+
+    if($cont eq 'FUNCTION' or $cont eq 'SUBROUTINE' or 
+       $cont eq 'PROGRAM'){ # Need name of routine
+      $_=$href->{statement};
+      &parse_prog_unit(\$unit_name,\@args);
+      $unit_name=uc($unit_name);
+# If in module pre-pend module name
+      $unit_name=$$prog_info{module_name}.':'.$unit_name if($$prog_info{is_module}); 
+      $remember=0;
+    }
+
+    if($hook_status == 0) {   # $hook_status == 0 means we have not done anything yet
+      if($cont eq 'USE') {    # Add USE YOMHOOK as second use statement
+	$href->{post_insert}="USE YOMHOOK   ,ONLY : LHOOK,   DR_HOOK\n";
+	$hook_status=1;
+      }
+      elsif($cont eq 'IMPLICIT NONE') { # No previous USE, add USE YOMHOOK before IMPLICIT NONE
+	$href->{pre_insert} ="USE PARKIND1  ,ONLY : JPRB\n".
+	  "USE YOMHOOK   ,ONLY : LHOOK,   DR_HOOK\n";
+	$hook_status=1;
+      } 
+    }
+    $remember=$href->{number} if($decl == 2); 
+
+#   Use statement added ($hook_status == 1), now insert HOOK switch on statement
+#   before first executable statement in program unit ($exec == 2)
+    if($hook_status == 1 && $exec == 2) {
+      if($remember) {
+	$$statements[$remember]->{post_insert}="REAL(KIND=JPRB) :: ZHOOK_HANDLE\n";
+	$href->{pre_insert}="IF (LHOOK) CALL DR_HOOK(\'${unit_name}\',0,ZHOOK_HANDLE)\n";
+      }
+      else {
+	$href->{pre_insert}="REAL(KIND=JPRB) :: ZHOOK_HANDLE\n".
+	    "IF (LHOOK) CALL DR_HOOK(\'${unit_name}\',0,ZHOOK_HANDLE)\n";
+      }   
+      if($cont eq 'IF') {
+	if($href->{content2} eq 'RETURN') {
+	  $_=$href->{statement};
+	  s/(\s*IF\s*$nest_par).*\n/$1/i;
+	  s/\)$/ .AND. LHOOK\)/;
+	  $href->{pre_insert}=$href->{pre_insert}."$_ CALL DR_HOOK(\'${unit_name}\',1,ZHOOK_HANDLE)\n";
+	}
+      }
+      $hook_status=2;
+    }
+#   Hook switched on($hook_status == 2), switch off after last executable statement
+#   ($exec == 3)
+    elsif($hook_status == 2) {
+      if($exec == 3 or $exec == 23) {
+	$href->{post_insert}="IF (LHOOK) CALL DR_HOOK(\'${unit_name}\',1,ZHOOK_HANDLE)\n";
+	$hook_status=3;
+      }
+      elsif($cont eq 'RETURN') {
+	$href->{pre_insert}="IF (LHOOK) CALL DR_HOOK(\'${unit_name}\',1,ZHOOK_HANDLE)\n";
+      }
+      elsif($cont eq 'IF') {
+	if($href->{content2} eq 'RETURN') {
+	  $_=$href->{statement};
+	  s/(\s*IF\s*$nest_par).*\n/$1/i;
+	  s/\)$/ .AND. LHOOK\)/;
+	  $href->{pre_insert}="$_ CALL DR_HOOK(\'${unit_name}\',1,ZHOOK_HANDLE)\n";
+	}
+      }	
+    }
+    $hook_status=1 if($in_contain && $hook_status==3); # Reset hook status in CONTAIN region
+  }
+  die "Adding HOOK function failed " if($hook_status == 2);
+}
+#==========================================================================
+
+sub doc_char{
+# Returns suggested prefix in case of DOCTOR violation (otherwise null string)
+  my($type,$usage,$var) = @_;
+  my $prefix="";
+# INTEGER variables
+  if( $type eq "i") {
+    if($usage eq "arg") {
+      $prefix="K" unless($var=~/^K/i);
+    }
+    elsif($usage eq "local") {
+      $prefix="I" unless($var=~/^[IJ]/i);
+    }
+    elsif($usage eq "module") {
+      $prefix="N" unless($var=~/^[MN]/i);
+    }
+    elsif($usage eq "namvar") {
+      $prefix="I" unless($var=~/^[MNIJ]/i);
+    }
+    else { 
+      die "Unknown usage";
+    }
+  }
+# REAL variables
+  elsif( $type eq "r") {
+    if($usage eq "arg") {
+      $prefix="P" unless($var=~/^P/i);
+    }
+    elsif($usage eq "local") {
+      $prefix="Z" unless($var=~/^Z|^PP/i);
+    }
+    elsif($usage eq "module") {
+      $prefix="R" if ($var=~/^[ZPIJKLMNCY]/i);
+    }
+    elsif($usage eq "namvar") {
+      $prefix="Z" if ($var=~/^[PIJKLMNCY]/i);
+    }
+    else { 
+      die "Unknown usage";
+    }
+  }
+#LOGICAL variables
+  elsif( $type eq "l") {
+    if($usage eq "arg") {
+      $prefix="LD" unless($var=~/^LD/i);
+    }
+    elsif($usage eq "local") {
+      $prefix="LL" unless($var=~/^LL/i);
+    }
+    elsif($usage eq "module") {
+      $prefix="L" unless($var=~/^L[^LD]/i);
+    }
+    elsif($usage eq "namvar") {
+      $prefix="LL" unless($var=~/^L/i);
+    }
+    else { 
+      die "Unknown usage";
+    }
+  }
+#CHARACTER variables
+  elsif( $type eq "c") {
+    if($usage eq "arg") {
+      $prefix="CD" unless($var=~/^CD/i);
+    }
+    elsif($usage eq "local") {
+      $prefix="CL" unless($var=~/^CL/i);
+    }
+    elsif($usage eq "module") {
+      $prefix="C" unless($var=~/^C[^LD]/i);
+    }
+    elsif($usage eq "namvar") {
+      $prefix="CL" unless($var=~/^C/i);
+    }
+    else { 
+      die "Unknown usage";
+    }
+  }
+# USER DEFINED TYPES
+  elsif( $type eq 't') {
+    if($usage eq "arg") {
+      $prefix="YD" unless($var=~/^YD/i);
+    }
+    elsif($usage eq "local") {
+      $prefix="YL" unless($var=~/^YL/i);
+    }
+    elsif($usage eq "module") {
+      $prefix="Y" unless($var=~/^Y[^LD]/i);
+    }
+    elsif($usage eq "namvar") {
+      $prefix="YL" unless($var=~/^Y/i);
+    }
+    else { 
+      die "Unknown usage";
+    }
+  }
+# FUNCTION/EXTERNAL declarations
+  elsif( $type eq 'f' || $type eq 'e' || $type eq 's') {
+# Everything is OK
+  }
+  else {
+    die "Unknown type $type"
+  }
+  ($prefix);
+}
+#==========================================================================
+     
+sub parse_prog_unit {
+  # Find out type (return), name ($$unit_name) and arguments (@$args)
+  # from a program unit statement ($_)
+  my ($unit_name, $args) = @_;
+  $$unit_name = '';
+  @$args      = ();
+
+  my $type = '';
+
+  our ($name, $type_spec, $attribute);
+
+  if (/^\s*(MODULE|PROGRAM|BLOCK\s*DATA)\s+($name)\s*$/io) {
+    $type       = lc ($1);
+    $$unit_name = $2;
+
+    # Remove space from "block data"
+    $type       =~ s/\s*//;
+
+  } elsif (/^\s*(?:$attribute)?\s*(SUBROUTINE)\s+($name)\b/io or
+           /^\s*(?:$attribute)?\s*(?:$type_spec)?\s*(FUNCTION)\s+($name)\b/io) {
+    $type       = lc ($1);
+    $$unit_name = $2;
+
+    # Get arguments/keywords from SUBROUTINE/FUNCTION
+    if(/^[^\(]+\([^\)]+\)/) {
+      my $tstatm = $_;
+
+      # Remove trailing comment
+      $tstatm =~ s/\!.*\n/\n/g;
+
+      # Remove space characters
+      $tstatm =~ s/\s//g;
+
+      # Add the RESULT clause to the argument/keyword list
+      $tstatm =~ s/\)result\((\w+\))$/,$1/i if $type eq 'function';
+
+      # Remove the parenthesis around the argument list
+      $tstatm =~ s/.+\((.+)\)/$1/;
+
+      @$args = split (',', uc ($tstatm));
+
+      # For FUNCTION, add its name to the list if necessary
+      push @$args, uc ($$unit_name)
+        if $type eq 'function' and not grep {$_ eq uc ($$unit_name)} @$args;
+    }
+  }
+
+  return $type;
+}
+
+#==========================================================================
+
+sub setup_parse {
+  # Set up some "global" variables that helps with parsing statements
+
+  # Pattern for nested parenthesis
+  our $nest_par;
+  $nest_par = qr/\((?:(?>[^()]+)|(??{$nest_par}))*\)/; #Camel p214
+
+  # Patterns for variable name and natural digit
+  our $name='[a-zA-Z]\w*';
+  our $digit_string='\d+';
+  our $type_name=$name;
+
+  # Patterns for specification
+  our $specification_expr='(?:'.$name.'|'.$digit_string.')'; # Simplification
+  our $type_param_value='(?:\*|'.$specification_expr.')';
+
+  # Patterns for length/kind attributes
+  our $char_selector='LEN *= *'.$type_param_value; # Simplification
+  our $kind_selector='\( *KIND *= *'.$name.' *\)';    # Simplification
+
+  # Pattern for type specification
+  our $type_spec='INTEGER *(?:'.$kind_selector.')?|REAL *(?:'.$kind_selector.
+    ')?|DOUBLE PRECISION|COMPLEX *(?:'.$kind_selector.')?|CHARACTER *'.
+    $char_selector.'|LOGICAL *(?:'.$kind_selector.')?|TYPE\s*\(\s*'.$type_name.
+    '\s*\)';
+
+  # Pattern for function/subroutine attribute
+  our $attribute = 'ELEMENTAL|(?:RECURSIVE(?:\s+PURE)?|PURE(?:\s+RECURSIVE)?)';
+
+  return;
+}
+
+#==========================================================================
+
+sub f90_indent {
+# Indent free-format F90 program to our standards
+  my($line_hash,$lines)=@_;
+  my($delta)='  '; 
+  my($cur_indent)='';
+  @$lines=();
+  foreach my $href (@$line_hash) {
+    $_=$href->{line};
+    if($href->{content} eq 'comment') {
+      push(@$lines,$_);
+      next;
+    }
+    s/^ *//; # Remove current indentation
+    my($post_chg)=0;
+    my($pre_chg)=0;
+    my($cont_line)='';
+    exit if (! exists $href->{content});
+    if($href->{content} eq 'DO') {
+      $post_chg=1 unless /^DO\s+\d/;
+    }
+    elsif($href->{content} eq 'ENDDO') {
+      $pre_chg=1;
+    }
+    elsif($href->{content} eq 'IF_construct') {
+      $post_chg=1;
+    }
+    elsif($href->{content} eq 'ELSEIF') {
+      $post_chg=1;
+      $pre_chg=1;
+    }
+    elsif($href->{content} eq 'ELSE') {
+      $post_chg=1;
+      $pre_chg=1;
+    }
+    elsif($href->{content} eq 'ENDIF') {
+      $pre_chg=1;
+    }
+    elsif($href->{content} eq 'ENDIF') {
+      $pre_chg=1;
+    }
+    elsif($href->{content} eq 'WHERE_construct') {
+      $post_chg=1;
+    }
+    elsif($href->{content} eq 'ELSEWHERE') {
+      $post_chg=1;
+      $pre_chg=1;
+    }
+    elsif($href->{content} eq 'ENDWHERE') {
+      $pre_chg=1;
+    }
+    elsif($href->{content} eq 'ENDIF') {
+      $pre_chg=1;
+    }
+    elsif($href->{content} eq 'SELECT CASE') {
+      $post_chg=1;
+    }
+    elsif($href->{content} eq 'CASE') {
+      $post_chg=1;
+      $pre_chg=1;
+    }
+    elsif($href->{content} eq 'END SELECT') {
+      $pre_chg=1;
+    }
+    $cont_line=' ' if($href->{content} eq 'cont_line');
+    if( $pre_chg ) {
+      unless($cur_indent=~s/^$delta//o) {
+	print STDERR $_;
+	die  "f90_indent: something wrong, indent negative\n";;
+      }
+    }
+#    print "$cur_indent$cont_line$_";
+    
+    $_=$cur_indent.$cont_line.$_;
+    push(@$lines,$_);
+    $cur_indent.=$delta if( $post_chg );
+  }
+
+  if(! ($cur_indent eq '')) {
+    die "f90_indent: something wrong, indent=XX${cur_indent}XX\n";
+  }
+}
+
+#==========================================================================
+
+sub tidy {
+# Straigthforward tidiyng of statements
+  my($statements) = @_;
+  my($href,$content);
+  foreach $href (@$statements) {
+    $_=$href->{statement};
+    $content=$href->{content};
+# Substitute tab with four blanks
+    s/\t/    /g;
+    if($content eq 'comment') {
+# Substitute empty comment line with empty line
+      s/^[!] *\n$/\n/;
+      $href->{statement}=$_;
+      next;
+    }
+    if($href->{exec}) {
+      if($content eq 'ENDDO') {
+	s/\bEND DO\b/ENDDO/i;
+	$href->{statement}=$_;
+	next;
+      }
+      if($content eq 'ENDIF') {
+	s/\bEND IF\b/ENDIF/i;
+	$href->{statement}=$_;
+	next;
+      }
+      if($content eq 'ENDWHERE') {
+	s/\bEND WHERE\b/ENDWHERE/i;
+	$href->{statement}=$_;
+	next;
+      }
+
+      s/\bELSE IF\b/ELSEIF/i  if($content eq 'ELSEIF');
+
+      if(/\./) {
+	s/ *\.EQ\. */ == /gi;
+	s/ *\.NE\. */ \/= /gi;
+	s/ *\.LT\. */ < /gi;
+	s/ *\.LE\. */ <= /gi;
+	s/ *\.GT\. */ > /gi;
+	s/ *\.GE\. */ >= /gi;
+      }
+
+#
+      s/\bA?MAX[01]\b/MAX/gi;
+      s/\bA?MIN[01]\b/MIN/gi;
+      s/\bAMOD\b/MOD/gi;
+      s/\bALOG\b/LOG/gi;
+      s/\bALOG10\b/LOG10/gi;
+#      s/\bI(SIGN *\()/$1/gi; # Goes wrong in larcinad etc.
+      s/\bFLOAT\b/REAL/g;
+      s/\bfloat\b/real/g;
+    }
+    
+    $href->{statement}=$_;
+  }
+}
+
+#==========================================================================
+
+sub process_include_files {
+# Read include files and put reference to the anonomys array
+# holding the array of "statement" hashes in $href->{inc_statm}
+  my($statements,$prog_info,$inc_statements) = @_;
+  my ($content,$fname,$href);
+  return unless ($$prog_info{has_include});
+  my @lines=();
+  foreach $href (@$statements) {
+    $content=$href->{content};
+    if($content eq 'include'){
+      $_=$href->{statement};
+      /["](\S+)["]/;
+      $fname=$1;
+      &get_inc_lines($fname,\@lines);
+# Macro-removal
+      &remove_macro(\@lines);
+# Expand lines into statements and put refernce to this
+# array of hashes into $href->{inc_statm}
+      my @inc_statms=();
+      my $dum={};
+      &expcont(\@lines,\@inc_statms);
+      $href->{inc_statm}=[@inc_statms];
+      my $incs=$href->{inc_statm};
+# Study the read in file and add more attributes
+      &study($incs);
+#      print Dumper($incs,$dum);
+      
+    }
+  }
+}
+#==========================================================================
+sub get_inc_lines{
+# Recurcivly get lines from include files, flatten into array of lines
+  my ($fname,$lines) = @_;
+  my ($VPATH,@vpath,@tmp_lines);
+
+  $VPATH=$ENV{VPATH} or die "VPATH not defined ";
+# IFS VPATH /tmp/27/ifs/function:/tmp/27/ifs/common:/tmp/27/ifs/interface:/tmp/27/ifs/namelist:/tmp/27/ifsaux/include:/tmp/27/trans/interface:/tmp/27/obsort/interface:/tmp/27/ifs/ald_inc/function:/tmp/27/ifs/ald_inc/interface:/tmp/27/ifs/ald_inc/namelist
+  @vpath=split(":",$VPATH);
+# Look for include file in VPATH
+  foreach my $path (@vpath) {
+    my $ffname=$path.'/'.$fname;
+    if( -f $ffname) {
+# Read lines from include file
+      @tmp_lines = &readfile($ffname);
+#      print "$ffname \n";
+      for (@tmp_lines) {
+	if(/^\#include\b/){
+	  /["](\S+)["]/;
+	  my $fname2=$1;
+	  &get_inc_lines($fname2,$lines);
+	}
+	else {
+	  push(@$lines,$_);
+	}
+      }
+      last;
+    }
+  }
+  die "Include file $fname not found in VPATH=$VPATH " unless(@$lines);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &create_interface_block (\@statements, \@interface_block);
+#
+# DESCRIPTION
+#   This function analyses the Fortran statements in \@statements and returns
+#   an interface block in \@interface_block.
+# ------------------------------------------------------------------------------
+
+sub create_interface_block {
+  # Create a "minimal" interface block for subroutines
+  my  ($statements, $interface_block) = @_;
+  my  (%pu_args, %tokens);
+  our ($name, $nest_par);
+
+  @$interface_block = ();
+
+  my @tokens_in_lines = (); # List of tokens in each line
+
+  # Gather information needed to create interface block for routine
+  for my $href (@$statements) {
+    last if $href->{exec}; # exit loop at beginning of executable statements
+
+    # Get arguments of subroutine or function
+    if ($href->{content} eq 'SUBROUTINE' or $href->{content} eq 'FUNCTION') {
+      my $func;
+      my @pu_args;
+      $_ = $href->{statement};
+      &parse_prog_unit (\$func, \@pu_args);
+      $pu_args{uc ($_)} = 1 for @pu_args;
+      next;
+    }
+
+    # Get tokens from lines where arguments are present
+    # Inspect only type declaration statements
+    next unless $href->{decl} == 2;
+
+    my $statement = uc $href->{statement};
+    $statement =~ s/!.*$//; # Remove trailing comment
+
+    my @line_tokens = ();
+    if ($statement =~ s/^(.*?):://) {
+      # New style declaration statement contains "::"
+
+      # Tokens in specification part
+      my $spec = $1;
+      my @tokens = ($spec =~ /\b$name\b/g);
+      shift @tokens; # Remove leading token
+
+      for (@tokens) {
+        push @line_tokens, $_
+          unless /^(?:KIND|LEN|ALLOCATABLE|POINTER|TARGET|DIMENSION|OPTIONAL|
+                  SAVE|INTENT|IN|OUT|INOUT|PARAMETER)$/x;
+      }
+
+      # Tokens in declaration part
+      push @line_tokens, ($statement =~ /\b$name\b/g);
+
+    } else {
+      # Old style declaration statement does not contain "::"
+      @line_tokens = ($statement =~ /\b$name\b/g);
+      shift @line_tokens; # Remove leading token
+    }
+
+    push @tokens_in_lines, \@line_tokens;
+
+    # Check whether each token matches an argument
+    for my $token (@line_tokens) {
+      if (exists $pu_args{$token}) {
+        $tokens{$_} = 1 for @line_tokens;
+        last;
+      }
+    }
+  }
+
+  # Parse statements one more time to ensure all required tokens are included
+  for (@tokens_in_lines) {
+    my @line_tokens = @{ $_ };
+
+    # Check whether line contains an essential token
+    for my $token (@line_tokens) {
+      if (exists $tokens{$token}) {
+        $tokens{$_} = 1 for @line_tokens;
+        last;
+      }
+    }
+  }
+
+  # Create the interface block
+  for my $href (@$statements) {
+    my %myhref  = %$href;
+    my $content = $myhref{content};
+
+    # Ignore comment, executable statements and items in CONTAINS block
+    next if $content eq 'comment';
+    next if $myhref{exec};
+    next if $myhref{in_contain};
+
+    # Delete existing pre- and post -inserts
+    delete $myhref{pre_insert}  if exists $myhref{pre_insert};
+    delete $myhref{post_insert} if exists $myhref{post_insert};
+
+    # Put SUBROUTINE/FUNCTION statement into interface block
+    if ($content =~ /^(?:SUBROUTINE|FUNCTION)$/) {
+      $myhref{pre_insert} = 'INTERFACE' . "\n"; # Insert INTERFACE statement
+      push @$interface_block, \%myhref;
+    }
+
+    # Add USE statement in interface block, if necessary
+    if($myhref{decl} == 4) {
+      $_ = uc $myhref{statement};
+      tr/ \n//d;
+
+      if(/^USE$name,ONLY:(.+)$/) {
+        # USE statement with ONLY, check token to see if it is necessary
+	my @line_tokens = /\b$name\b/g;
+
+	for (@line_tokens) {
+	  if (exists $tokens{$_}) {
+	    push @$interface_block, \%myhref;
+	    last;
+	  }
+	}
+
+      } else {
+        # Always add USE statement without ONLY
+	push @$interface_block, \%myhref;
+      }  
+    }
+
+    if ($myhref{decl} == 1 or $myhref{decl} == 2) {
+      $_ = uc ($myhref{statement});
+      s/\s*!.*$//;
+
+      if ($content eq 'INTEGER' or $content eq 'PARAMETER') {
+        # INTEGER and PARAMETER may be used for dimensioning
+	my @line_tokens = /\b$name\b/g;
+
+	for (@line_tokens) {
+	  if (exists $tokens{$_}) {
+	    push @$interface_block, \%myhref;
+	    last;
+	  }
+	}
+      } else {
+        # Add line only if an argument is present
+	s/$nest_par//g;
+	my @line_tokens = /\b$name\b/g;
+
+	for (@line_tokens) {
+	  if (exists $pu_args{$_}) {
+	    push @$interface_block, \%myhref;
+	    last;
+	  }
+	}
+      }
+    }
+
+    # Add END statement to interface block
+    if ($content =~ /^END\s+(?:SUBROUTINE|FUNCTION)/) {
+      $myhref{post_insert} = 'END INTERFACE' . "\n";
+      push @$interface_block, \%myhref;
+    }
+  }
+
+  # Beautify the interface block
+  for my $href (@$interface_block) {
+    $_ = $href->{statement};
+
+    s/\!.*\n/\n/g; # Remove trailing comments
+    s/ +/ /g;      # Only one space
+    s/\n *\n/\n/g; # Remove empty lines
+    s/\n *\n/\n/g; # Remove empty lines again
+    s/ +\n/\n/g;   # No trailing spaces
+
+    $href->{statement} = $_;
+  }
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+
+sub change_var_names{
+  my($statements) = @_;
+  foreach my $href (@$statements) {
+    $_=$href->{statement};
+    s/\bVAZX\b/YVAZX/ig;
+    s/\bPVAZX\b/YDVAZX/ig;
+    s/\bVAZG\b/YVAZG/ig;
+    s/\bPVAZG\b/YDVAZG/ig;
+    s/\bSCALP_DV\b/YSCALP/ig;
+    s/\bRSCALP_DV\b/YRSCALP/ig;
+    s/\bSCALPSQRT_DV\b/YSCALPSQRT/ig;
+    s/\bRSCALPSQRT_DV\b/YRSCALPSQRT/ig;
+    s/\bPYBAR\b/YDYBAR/ig;
+    s/\bPSBAR\b/YDSBAR/ig;
+    s/\bVCGLPC\b/YVCGLPC/ig;
+    s/\bVCGLEV\b/YVCGLEV/ig;
+    s/\bSKFROT\b/YSKFROT/ig;
+    s/\bSKFMAT\b/YSKFMAT/ig;
+    s/\bSTATE_VECTOR_4D\b/YSTATE_VECTOR_4D/ig;
+    s/\bVAZX0\b/YVAZX0/ig;
+    s/\bVAZG0\b/YVAZG0/ig;
+    s/\bRSPFORCE\b/YSPFORCE/ig;
+    $href->{statement}=$_;
+  }
+}
+# =========================================================================
+sub remake_arg_decl{
+  my($statements,$prog_info) = @_;
+  my($href,$content,@pu_args,$func,%tokens);
+  my($left,$right,%arghash,$dim);
+  our($nest_par,$name);
+
+  my $dims='';
+# Crack existing dummy declarations, build hash arghash
+  foreach $href (@$statements) {
+    last if($href->{prog_unit} >0);
+    if($href->{content} eq 'SUBROUTINE') {   # Get arguments of subroutine
+      $_=$href->{statement};
+      my $dum=&parse_prog_unit(\$func,\@pu_args);
+#      print Dumper(\@pu_args);
+      for(@pu_args) {
+	$_=uc($_);
+	$arghash{$_}{other}='';
+	$arghash{$_}{dimuse}=0;
+	$arghash{$_}{intent}='';
+	$arghash{$_}{used}=0;
+	$arghash{$_}{set}=0;
+	$arghash{$_}{reallyset}=0;
+	$arghash{$_}{type}='';
+	$arghash{$_}{comment}='';
+	$arghash{$_}{inif}=0;
+      }
+      next;
+    }
+    if($href->{decl} == 2) {
+      $_=$href->{statement};
+      my $comment='';
+      $comment=$1 if(/.*(\!.*)$/);
+      s/\!.*\n/\n/g;                        # Remove trailing comments in all lines
+      $_=uc($_);
+      s/\s//g;
+      if(/^(.+)::(.+)$/){
+	$left=$1;
+	$right=$2;
+	$_=$right;
+	s/$nest_par//g;
+	s/($name)\*\w+/$1/g;
+#	print "XX  $_ \n";
+	foreach my $arg (@pu_args) {
+	  if(/\b$arg\b/) {
+#	    print "ARG $arg $left $_ \n";
+	    $arghash{$arg}{linedec}=$href->{number};
+	    $arghash{$arg}{comment}=$comment;
+	    my @locdec =split ',',$left;
+	    my $i=0;
+	    foreach my $locdec (@locdec) {
+	      if($i == 0) {
+		$arghash{$arg}{type}=$locdec;
+	      }
+	      elsif($locdec=~/\bINTENT/) {
+		$arghash{$arg}{intent}=','.$locdec;
+	      }
+	      else {
+		$arghash{$arg}{other}=$arghash{$arg}{other}.','.$locdec;
+	      }
+	      $i++;
+	    }
+	    if($right=~/\b$arg\b(\*\w+)/) {
+	      $dim=$1;
+	    }
+	    elsif($right=~/\b$arg\b($nest_par\*$nest_par)/) {
+	      $dim=$1;
+	    }
+	    elsif($right=~/\b$arg\b($nest_par\*\w+)/) {
+	      $dim=$1;
+	    }
+	    elsif($right=~/\b$arg\b(\*$nest_par)/) {
+	      $dim=$1;
+	    }
+	    elsif($right=~/\b$arg\b($nest_par)/) {
+	      $dim=$1;
+	    }
+	    else {
+	      $dim='';
+	    }
+	    $arghash{$arg}{dim}=$dim;
+	    $dims=$dims.$dim
+	      }
+	}
+	foreach my $arg (@pu_args) {  # Is arg. used for dimensioning other args?
+	  if($dims=~/\b$arg\b/i) {
+	    $arghash{$arg}{dimuse}=1;
+	  }
+	}  
+      }
+    }
+  }
+  my $insert_line=0;
+  foreach $href (@$statements) {
+    last if($href->{prog_unit} >0);
+    if($href->{decl} == 2 or $href->{content} eq 'PARAMETER') {                 
+      $_=uc($href->{statement});
+      next unless /\bPARAMETER\b/;
+      my @tmpvar=/\b$name\b/g;
+      foreach my $token (@tmpvar) {
+	if($dims=~/\b$token\b/) {
+	  $insert_line=$href->{number};
+	}
+      }
+    }
+  }
+      
+# Gather info to decide INTENT status
+  my $inif=0;
+  my @inif_stack=();
+  my $cur_inif=0;
+  foreach $href (@$statements) {
+    last if($href->{prog_unit} >0);
+    if($href->{exec}) {
+      if($href->{content} eq 'ENDIF') {
+	$inif--;
+	$cur_inif=pop @inif_stack;
+	next;
+      }
+      elsif($href->{content} eq 'ELSEIF' or $href->{content} eq 'ELSE') {
+	$cur_inif=pop @inif_stack;
+	$cur_inif=$href->{number};
+	push @inif_stack,$cur_inif;
+      }
+      my ($left,$right);
+      $_=$href->{statement};
+      s/\!.*\n/\n/g;                        # Remove trailing comments in all lines
+      my %setnow=();
+      foreach my $arg (@pu_args) {
+	$setnow{$arg}=0;
+	$setnow{$arg}=1 if($arghash{$arg}{reallyset});
+	unless ($setnow{$arg}) {
+	  foreach my $xx (@inif_stack) {
+	    $setnow{$arg}=1 if($xx == $arghash{$arg}{inif});
+	  }
+	}
+      }
+      
+      if($href->{content} eq 'scal_assign' or $href->{content} eq 'array_assign') {
+	s/\s//g;
+	($left,$right)=/^(.+)=(.+)$/;
+	$_=$right;
+	foreach my $arg (@pu_args) {
+	  if(/\b$arg\b/i) {
+	    $arghash{$arg}{used}=1 unless $setnow{$arg};
+	  }
+	}
+	$_=$left;
+	if(/($nest_par)/) {
+	  $_=$1;
+	  foreach my $arg (@pu_args) {
+	    if(/\b$arg\b/i) {
+	      $arghash{$arg}{used}=1 unless $setnow{$arg};
+	    }
+	  }
+	}
+	$_=$left;
+	foreach my $arg (@pu_args) {
+	  if(/^$arg\b/i) {
+	    $arghash{$arg}{set}=1;
+	    $arghash{$arg}{inif}=$cur_inif;
+	    $arghash{$arg}{reallyset}=1 unless($inif);
+	  }
+	}
+      }
+      elsif($href->{content} eq 'IF' ) {
+	if($href->{content2} eq 'scal_assign' or $href->{content2} eq 'array_assign' or 
+	   $href->{content2} eq 'CALL') {
+	  s/\n//g;
+	  ($left,$right)=/^\s*(IF\b\s*$nest_par)(.+)/i;
+	  $_=$left;
+	  foreach my $arg (@pu_args) {
+	    if(/\b$arg\b/i) {
+	      $arghash{$arg}{used}=1 unless $setnow{$arg};
+	    }
+	  }
+	  $_=$right;
+	  if($href->{content2} eq 'CALL') {
+	    my $statement=$right;
+	    my $inifx=1;
+	    &propag_arg(\$statement,\%arghash,\$inifx,\%setnow);
+	  }
+	  else {
+	    s/\s//g;
+	    ($left,$right)=/^(.+)=(.+)$/;
+	    $_=$right;
+	    foreach my $arg (@pu_args) {
+	      if(/\b$arg\b/i) {
+		$arghash{$arg}{used}=1 unless $setnow{$arg};
+	      }
+	    }
+	    $_=$left;
+	    if(/($nest_par)/) {
+	      $_=$1;
+	      foreach my $arg (@pu_args) {
+		if(/\b$arg\b/i) {
+		  $arghash{$arg}{used}=1 unless $setnow{$arg};
+		}
+	      }
+	    }
+	    $_=$left;
+	    foreach my $arg (@pu_args) {
+	      if(/^$arg\b/i) {
+		$arghash{$arg}{inif}=$cur_inif;
+		$arghash{$arg}{set}=1;
+	      }
+	    }
+	  }
+	}
+	else {
+	  foreach my $arg (@pu_args) {
+	    if(/\b$arg\b/i) {
+	      $arghash{$arg}{used}=1 unless $setnow{$arg};
+	    }
+	  }
+	}
+      }
+      elsif($href->{content} eq 'WHERE' ) {
+	s/\s//g;
+	($left,$right)=/^(WHERE$nest_par)(.+)/i;
+	$_=$left;
+	foreach my $arg (@pu_args) {
+	  if(/\b$arg\b/i) {
+	    $arghash{$arg}{used}=1 unless $setnow{$arg};
+	  }
+	}
+	$_=$right;
+	($left,$right)=/^(.+)=(.+)$/;
+	$_=$right;
+	foreach my $arg (@pu_args) {
+	  if(/\b$arg\b/i) {
+	    $arghash{$arg}{used}=1 unless $setnow{$arg};
+	  }
+	}
+	$_=$left;
+	foreach my $arg (@pu_args) {
+	  if(/^$arg\b/i) {
+	    $arghash{$arg}{inif}=$cur_inif;
+	    $arghash{$arg}{set}=1;
+	  }
+	}
+      }
+      elsif($href->{content} eq 'CALL') {
+	my $statement=$_;
+	&propag_arg(\$statement,\%arghash,\$inif);
+      }
+      else{
+	foreach my $arg (@pu_args) {
+	  if(/\b$arg\b/i) {
+	    $arghash{$arg}{used}=1 unless $setnow{$arg};
+	  }
+	}
+      }
+      if($href->{content} eq 'IF_construct') {
+	$inif++;
+	$cur_inif=$href->{number};
+	push @inif_stack,$cur_inif;
+      }
+    }	  
+  }
+
+# Create INTENT statemant based on gathered info
+  foreach my $arg (@pu_args) {
+    if($arghash{$arg}{linedec}) {
+      if($arghash{$arg}{nointent}) {
+	unless($arghash{$arg}{intent}) {
+	  $arghash{$arg}{intent}=' ';
+	  $arghash{$arg}{comment}='! UNDETERMINED INTENT';
+	}
+      }
+      else{
+	my $intent='';
+	$intent='IN' if($arghash{$arg}{used} or $arghash{$arg}{dimuse});
+	$intent=$intent.'OUT' if($arghash{$arg}{set});
+	if($intent) {
+	  if($arghash{$arg}{intent} and $intent eq 'OUT') {
+	    $intent='INOUT' if $arghash{$arg}{intent}=~/INOUT/i;
+	  }
+	  $arghash{$arg}{intent}=',INTENT('.$intent.')';
+	}
+	else {
+	  $arghash{$arg}{intent}=' ';
+	  $arghash{$arg}{comment}='! Argument NOT used';
+	}
+      }
+    }
+  }
+
+# Remove existing argument declarations
+  foreach my $arg (@pu_args) {
+    if($arghash{$arg}{linedec}) {
+      $_=$$statements[$arghash{$arg}{linedec}]->{statement};
+      #   print "BEFORE $arg $_";
+      if(/.*::\s*\b$arg\b\s*(\!.*\n)*$/i) {
+	$_='';
+      }
+      elsif(/.*::\s*\b$arg\b\s*$nest_par\s*(\!.*\n)*$/i) {
+	$_='';
+      }
+      elsif(/.*::\s*\b$arg\b\s*\*\s*\w+\s*(\!.*\n)*$/i) {
+	$_='';
+      }
+      elsif(/.*::\s*\b$arg\b\s*\*\s*$nest_par\s*(\!.*\n)*$/i) {
+	$_='';
+      }
+      elsif(/.*::\s*\b$arg\b\s*$nest_par\s*\*\s*\w+\s*(\!.*\n)*$/i) {
+	$_='';
+      }
+      elsif(/.*::\s*\b$arg\b\s*$nest_par\s*\*\s*$nest_par\s*(\!.*\n)*$/i) {
+	$_='';
+      }
+      else{
+	/^(.*::)(.*)$/s;
+	my $left=$1;
+	$_=$2;
+	s/\b$arg\b\s*$nest_par//i;
+	s/\b$arg\b\s*\*\s*\w+//i;
+	s/\b$arg\b\s*\*\s*$nest_par//i;
+	s/\b$arg\b//i;
+	s/,\s*,/,/;
+	s/,(\s*)$/$1/;
+	s/\n\s*\n/\n/g;
+	$_=$left.$_;
+	s/::\s*,/::/;
+      }
+ #   print "AFTER $arg $_\n";
+      $$statements[$arghash{$arg}{linedec}]->{statement}=$_; 
+    }
+  }
+
+ # Write out
+
+  my $newdecl='';
+  my $linedec;
+  foreach my $arg (@pu_args) {
+    if($arghash{$arg}{linedec}) {
+      if($arghash{$arg}{other} and ! $arghash{$arg}{dim}) {
+	$arghash{$arg}{other}=~s/\s//g;
+	if($arghash{$arg}{other}=~/^,DIMENSION($nest_par)$/i) {
+	  $arghash{$arg}{other}='';
+	  $arghash{$arg}{dim}=$1;
+	}
+      }
+      if($arghash{$arg}{dimuse}) { # Put declerations of args first
+	$linedec=sprintf "%-18s%s%-14s%s%s%s%s %s",
+	$arghash{$arg}{type},$arghash{$arg}{other},$arghash{$arg}{intent},
+	    ' :: ',$arg,$arghash{$arg}{dim},$arghash{$arg}{comment},"\n";
+	$newdecl=$newdecl.$linedec;
+      }
+    }
+  }
+  foreach my $arg (@pu_args) {
+    if($arghash{$arg}{linedec}) {
+      unless($arghash{$arg}{dimuse}) {
+	$linedec=sprintf "%-18s%s%-14s%s%s%s %s%s",
+	$arghash{$arg}{type},$arghash{$arg}{other},$arghash{$arg}{intent},
+	    ' :: ',$arg,$arghash{$arg}{dim},$arghash{$arg}{comment},"\n";
+	$newdecl=$newdecl.$linedec;
+      }
+    }
+  }
+#  print "INSERT_LINE $insert_line \n";
+  if($insert_line) {
+    $$statements[$insert_line]->{post_insert}=$newdecl;
+  }
+  else{
+    foreach $href (@$statements) {
+      if($href->{decl} == 2) {                 
+	$href->{pre_insert}=$newdecl;
+	last;
+      }
+    }
+  }
+
+#  print $newdecl;
+#  print Dumper(\%arghash);
+}
+
+sub propag_arg{
+  my ($statement,$arghash,$inif,$setnow) = @_;
+  our ($name,$nest_par);
+  my (%argpos);
+  $_=$$statement;
+  s/^\s*CALL\s+($name)//i;
+  my $called=lc($1);
+  s/\s//g;
+  s/^\((.*)\)$/$1/s;
+  my @inpars=/$nest_par/g;
+  s/$nest_par//g;
+  s/($name)%$name/$1/g;
+  $_=uc($_);
+#  print "PROPAG $called $_ ££ @inpars \n";
+  my @call_args=split ',' , $_;
+  my $i=0;
+  my $interesting=0;
+  %argpos=();
+  foreach my $call (@call_args) {
+    
+#    print "CALL $called $call \n" ;
+    if($call=~/(.+)=(.+)/) {
+      $call=$2; #This just by-passes the problem
+    }
+    if(exists $$arghash{$call}) {
+      if(exists $argpos{$call}) {
+	push @{$argpos{$call}},$i;
+      }
+      else {
+	my @i=($i);
+	$argpos{$call}=[@i];
+      }
+      $interesting=1;
+    }
+    $i++;
+  }
+  if($interesting) {
+    my $fname='/tmp/intblocks/'.$called.'.intfb.h';
+    if( -f $fname ) {
+      my @dumargs=();
+      my $unit_name;
+      print "FILE $fname FOUND \n";
+      my @lines = &readfile($fname);
+      my @loc_statements=(); 
+      &expcont(\@lines,\@loc_statements);
+      foreach my $href (@loc_statements) {
+	$_=$href->{statement};
+	if(/^\s*SUBROUTINE/i) {
+	  my $dum=&parse_prog_unit(\$unit_name,\@dumargs);
+	  next;
+	}
+	if(/::/) {
+	  s/\s//g;
+	  foreach my $arg (keys (%argpos)) {
+	    my $set_before=$$setnow{$arg};
+	    foreach my $i (@{$argpos{$arg}}){
+	      if(/::$dumargs[$i]/) {
+		if(/INTENT\(IN\)/i) {
+		  $$arghash{$arg}{used}=1 unless $set_before;
+		}
+		elsif(/INTENT\(OUT\)/i) {
+		  $$arghash{$arg}{set}=1;
+		  $$setnow{$arg}=1 unless($$inif);
+		}
+		elsif(/INTENT\(INOUT\)/i) {
+		  $$arghash{$arg}{set}=1;
+		  $$arghash{$arg}{used}=1 unless $set_before;;
+		  $$arghash{$arg}{reallyset}=1 unless($$inif);
+		}
+		elsif(/\! UNDETERMINED INTENT/) {
+		  $$arghash{$arg}{nointent}=1;
+		}
+	      }
+	    }
+	  }
+	}
+      }
+    }
+    else {
+      foreach my $arg (keys (%argpos)) {
+	$$arghash{$arg}{nointent}=1;
+      }
+    }
+  }
+  for (@inpars) {
+    foreach my $arg (keys (%$arghash)) {
+      if(exists $$arghash{$arg}) {
+	if(/\b$arg\b/i) {
+	  $$arghash{$arg}{used}=1 unless $$setnow{$arg};
+	}
+      }
+    }
+  }
+}
+  
+sub add_interface_blocks {
+# Add interface block for called routines
+  use File::Find;
+  my($statements,$prog_info) = @_;
+  my($href,$call);
+  our($name,$nest_par);
+  our(@call_names,@call_names_found,%call_names);
+
+  return unless ($$prog_info{no_calls}); # Skip if there are no calls
+  @call_names=();
+  %call_names=();
+
+  my $last_decl=0;
+  my $in_intfblk=0;
+  my %already_in=();
+  ST:foreach $href (@$statements) {
+    last if($href->{prog_unit} > 0);  # Only consider first program unit (no contains)
+    if($href->{content} eq 'INTERFACE') {
+      $in_intfblk=1;
+      next;
+    }
+    if($href->{content} eq 'END INTERFACE') {
+      $in_intfblk=0;
+      next;
+    }
+    if($in_intfblk) {
+      $_=$href->{statement};
+      s/\#include\s*\"(\w+)\.h\"\s*$/$1/;
+      $_=lc($_);
+      $already_in{$_}++;
+      next;
+    }
+    
+# Find last declaration
+    if($href->{decl}) {
+      next if($href->{content} eq 'FORMAT');
+      next if($href->{content} eq 'DATA');
+      $last_decl = $href->{number} ;
+    }
+# Find calls
+    next unless($href->{exec});
+    if($href->{content} eq 'CALL' or 
+       (exists  $href->{content2} and$ href->{content2} eq 'CALL') ) {
+      $_=$href->{statement};
+      /\s*\bCALL\b\s*($name)/i;
+      my $call=lc($1);
+      next if($already_in{$call}); # Exclude already existing interface block
+      next if($call eq 'packmsg'); # A couple of special exceptions
+      next if($call eq 'unpkmsg');
+      $call_names{$call}++;
+    }
+  }
+  
+
+# Check that routine exists in IFS
+  @call_names_found=();
+  find(\&calls_wanted,'/tmp/27/ifs/');
+#  find(\&calls_wanted,'/home/mats/work/cy28/ifs/');
+#  find(\&calls_wanted,'/tmp/27/trans/');
+  @call_names_found=sort(@call_names_found);
+#  print "P2 @call_names_found \n";
+  @call_names=@call_names_found;
+
+# Contruct include block
+  my $block='';
+  for (@call_names) {
+    $block=$block.'#include "'.$_.'.intfb.h"'."\n";
+  }
+#  print $block;
+
+  my $clean=0;
+  if(@call_names) {
+    if($$prog_info{has_interface_block}) {
+      foreach $href (@$statements) {
+# Add interface block to routine that already has INTERFACE statement
+	if($href->{content} eq 'END INTERFACE'){
+	  if($href->{post_insert}) {
+	    $href->{post_insert}=$href->{post_insert}."\n".$block;
+	  }
+	  else {
+	    $href->{post_insert}="\n".$block;
+	  }	    
+	  last;
+	}
+      }
+    }
+# Add interface block to routine that does not have previous INTERFACE statement
+    else {
+      $href=@$statements[$last_decl];
+      if($href->{post_insert}) {
+	$href->{post_insert}=$href->{post_insert}."\n".$block;
+      }
+      else {
+	$href->{post_insert}="\n".$block;
+      }	    
+    }
+# Remove from EXTERNAL statement where interface block has been added
+    foreach $href (@$statements) {
+      if($href->{content} eq 'EXTERNAL') {
+	$_=$href->{statement};
+	foreach my $ext (@call_names) {
+	  s/\b$ext\b//i;
+	}
+	s/,\s*,/,/g;
+	s/^(\s*EXTERNAL\s*),/$1/i;
+	s/^(\s*EXTERNAL.*),\s*$/$1/i;
+	s/^\s*EXTERNAL\s*,*\s*$//i;
+	$href->{statement}=$_;	
+      }
+    }
+  }
+}
+#======================================================================================
+sub calls_wanted {
+  # Used by Find as called from add_interface_blocks
+  our(%call_names,@call_names_found);
+  return unless (/^(\w+)\.F90$/);
+  my $call=$1;
+  if($call_names{$call}) {
+    push(@call_names_found,$call);
+  }    
+}
+sub remove_some_comments{
+  my($statements) = @_;
+  my $prev_empty=0;
+  foreach my $href (@$statements) {
+    if($href->{content} eq 'comment'){
+      $_=$href->{statement};
+      if(/^\s*$/) {
+	if($prev_empty) {
+	  s/\s*//;
+	  $href->{statement}=$_;
+	}
+	else {
+	  $prev_empty=1;
+	} 
+	next;
+      }
+      $prev_empty=0;
+      s/^\s*![\s\*]*\bLOCAL\s+(INTEGER|REAL|LOGICAL|CHARACTER)\s+(SCALARS|ARRAYS).*\n$//i;
+      s/^\s*![\s\*]*\bDUMMY\s+(INTEGER|REAL|LOGICAL|CHARACTER)\s+(SCALARS|ARRAYS).*\n$//i;
+      s/^\s*![\s\*]*\bLOCAL\s+(INTEGER|REAL|LOGICAL|CHARACTER).*\n$//i;
+      s/^\s*![\s\*]*\bDUMMY\b\s*$//i;
+      s/^\s*![\s\*]*\bLOCAL\b\s*$//i;
+      s/^\s*![\s\*]*\bLOCAL\b:\s*$//i;
+      s/^\s*![\s\*]*\bLOCAL ARRAYS\b[\s\*]*$//i;
+      s/^\s*![\s\*]*\bLOCAL SCALARS\b\s*$//i;
+      s/^\s*![\s\*]*\s*\d\.\d+\s*\bLOCAL ARRAYS\b\s*$//i;
+      s/^\s*![\s\*]*\s*=== LOCAL ARRAYS ===\s*$//i;
+      $href->{statement}=$_;
+    }
+    else {
+      $prev_empty=0;
+    }
+  }
+}      
+sub get_calls_inc {
+  my($statements,$calls,$intfb) = @_;
+  foreach my $href (@$statements) {
+    if($href->{content} eq 'CALL') {
+      $_=$href->{statement};
+      /^\s*CALL\s+([A-Z]\w*)/i;
+      $$calls{lc($1)}++;
+    }
+    elsif($href->{content} eq 'IF') {
+      if($href->{content2} eq 'CALL') {
+	$_=$href->{statement};
+	/\bCALL\s+([A-Z]\w*)/i;
+	$$calls{lc($1)}++;
+      }
+    }
+    elsif($href->{content} eq 'include') {
+      $_=$href->{statement};
+      $$intfb{$1}=1 if(/["](\S+)\.intfb\.h["]/);
+      $$intfb{$1}=2 if(/["](\S+)\.h["]/); # For old-style interface blocks
+    }
+  }
+}
+
+1;
+      
+__END__
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/Build.pm
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/Build.pm	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/Build.pm	(revision 5058)
@@ -0,0 +1,2428 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::Build
+#
+# DESCRIPTION
+#   The main purpose of this class is to process the build configuration,
+#   generate the required files for the build and invoke make to create the
+#   build.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+package Fcm::Build;
+
+# Standard pragma
+use strict;
+use warnings;
+
+# Standard modules
+use Carp;
+use Cwd;
+use File::Basename;
+use File::Path;
+use File::Spec::Functions;
+
+# FCM component modules
+use Fcm::CfgFile;
+use Fcm::SrcPackage;
+use Fcm::BuildTask;
+use Fcm::Util;
+use Fcm::Timer;
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $bld = Fcm::Build->new (
+#     CONFIG  => $config,
+#     CFG_SRC => $cfg_src,
+#   );
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::Build class.
+#
+# ARGUMENTS
+#   CONFIG     - reference to a Fcm::Config instance
+#   CFG_SRC    - source path to the build configuration file
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $cfg    = exists $args{CFG_SRC} ? $args{CFG_SRC} : undef;
+  my $config = exists $args{CONFIG}  ? $args{CONFIG}  : &main::cfg;
+
+  my $self  = {
+    CONFIG   => $config,            # configuration settings
+    CFG      => Fcm::CfgFile->new ( # bld cfg
+      TYPE   => 'bld',              # config file type
+      SRC    => $cfg,               # source path of the bld cfg
+      CONFIG => $config,            # configuration settings
+    ),
+    NAME     => '',                 # name of this build
+    DIR      => {                   # directory tree of this build
+      ROOT    => '',                # root directory of this build
+    },
+    PATH     => {},                 # search paths of this build
+    SEARCH   => 1,                  # search for source directories in src/?
+    SRCDIR   => {},                 # source directories of this build
+    PP       => {},                 # pre-process flags
+    PACKAGE  => {},                 # source directory packages of this build
+    TARGET   => [],                 # targets of this build
+    USE      => [],                 # list of inherited builds
+    INHERIT  => {                   # inheritance flags
+      SRCDIR => 1,                  # inherit source directories?
+      PP     => 1,                  # inherit pre-process flags?
+      TARGET => 0,                  # inherit targets?
+    },
+    LIB      => {'' => ''},         # name of libraries
+    LOCK     => undef,              # lock file
+  };
+  bless $self, $class;
+  return $self;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->DESTROY;
+#
+# DESCRIPTION
+#   This method is called automatically when a Fcm::Build object is
+#   destroyed.
+# ------------------------------------------------------------------------------
+
+sub DESTROY {
+  my $self = shift;
+
+  # Remove the lock if it is set
+  unlink $self->{LOCK} if $self->{LOCK} and -e $self->{LOCK};
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $config = $bld->config;
+#
+# DESCRIPTION
+#   This method returns a reference to the Fcm::Config instance.
+# ------------------------------------------------------------------------------
+
+sub config {
+  my $self = shift;
+
+  return $self->{CONFIG};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $cfgfile = $bld->cfg;
+#
+# DESCRIPTION
+#   This method returns a reference to a Fcm::CfgFile instance for the build
+#   configuration file.
+# ------------------------------------------------------------------------------
+
+sub cfg {
+  my $self = shift;
+
+  return $self->{CFG};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   %allpcks = $bld->allpcks ();
+#
+# DESCRIPTION
+#   This method returns a hash table with keys representing all the packages
+#   declared in the current build. The value of each element in the hash is a
+#   reference to a list of children of the current package.
+# ------------------------------------------------------------------------------
+
+sub allpcks {
+  my $self        = shift;
+  my %allpcks = ();
+
+  for my $pckname (keys %{ $self->{PACKAGE} }) {
+    $allpcks{$pckname} = [];
+  }
+
+  for my $pckname (keys %{ $self->{PACKAGE} }) {
+    my @names = split /__/, $pckname;
+
+    my $cur = $pckname;
+    while ($cur) {
+      pop @names;
+      my $depend = @names ? join '__', @names : '';
+      $allpcks{$depend} = [] unless exists $allpcks{$depend};
+
+      push @{ $allpcks{$depend} }, $cur
+        unless grep {$_ eq $cur} @{ $allpcks{$depend} };
+
+      $cur = $depend;
+    }
+  }
+
+  return %allpcks;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $bld->build (
+#     [ARCHIVE     => $archive,]
+#     [FULL        => $full,]
+#     [IGNORE_LOCK => $ignore_lock,]
+#     [JOBS        => $jobs,]
+#     [STAGE       => $stage,]
+#     [TARGETS     => \@targets,]
+#   );
+#
+# DESCRIPTION
+#   This method performs a build based on the current configuration. The
+#   method returns 1 on success.
+#
+# ARGUMENTS
+#   ARCHIVE      - If set to "true", invoke the "archive" mode. Most build files
+#                  and directories created by this build will be archived using
+#                  the "tar" command. If not set, the default is not to invoke
+#                  the "archive" mode.
+#   FULL         - If set to "true", invoke the build in "full" mode. Build files
+#                  and directories created by previous builds in the same
+#                  location will be removed before the current build is
+#                  performed. If not set, the default is to perform the build
+#                  in "incremental" mode.
+#   IGNORE_LOCK  - If set to "true", it ignores any lock files that may exist in
+#                  the build root directory. 
+#   JOBS         - Specify number of jobs that can be handled by "make". If set,
+#                  the value must be a natural integer. If not set, the default
+#                  value is 1 (i.e. run "make" in serial mode).
+#   STAGE        - If set, it should be an integer number or a recognised
+#                  keyword or abbreviation. If set, the build is performed up
+#                  to the named stage. If not set, the default is to perform
+#                  all stages of the build. Allowed values are:
+#                  1, setup or s
+#                  2, pre_process or pp
+#                  3, generate_dependency or gd
+#                  4, generate_interface or gi
+#                  5, all, a, make or m
+#   TARGETS      - Specify targets to be built. If set, these targets will be
+#                  built instead of the ones specified in the build
+#                  configuration file.
+# ------------------------------------------------------------------------------
+
+sub build {
+  my $self = shift;
+  my %args = @_;
+
+  # Process arguments
+  my $archive     = exists $args{ARCHIVE}     ? $args{ARCHIVE}     : 0;
+  my $full        = exists $args{FULL}        ? $args{FULL}        : 0;
+  my $ignore_lock = exists $args{IGNORE_LOCK} ? $args{IGNORE_LOCK} : 0;
+  my $jobs        = exists $args{JOBS}        ? $args{JOBS}        : 1;
+  my $stage       = exists $args{STAGE}       ? $args{STAGE}       : 5;
+  my $targets     = exists $args{TARGETS}     ? $args{TARGETS}     : [qw/all/];
+
+  # Resolve named stages
+  $stage = 5 unless $stage;
+  if ($stage !~ /^\d$/) {
+    my %stagenames = (
+      'S(?:ETUP)?'                      => 1,
+      'P(?:RE)?_?P(?:ROCESS)?'          => 2,
+      'G(?:ENERATE)?_?D(?:ENPENDENCY)?' => 3,
+      'G(?:ENERATE)?_?I(?:NTERFACE)?'   => 4,
+      '(?:A(?:LL)|M(?:AKE)?)'           => 5,
+    );
+
+    for my $name (keys %stagenames) {
+      if ($stage =~ /$name/i) {
+        $stage = $stagenames{$name};
+        last;
+      }
+    }
+
+    if ($stage !~ /^\d$/) {
+      w_report 'Warning: invalid build stage: ', $stage, ', default to "5"';
+      $stage = 5;
+    }
+  }
+
+  # Get verbose mode
+  my $verbose = $self->config->verbose;
+
+  # Stage 1: setup
+  my $date = localtime;
+  print 'Build command started on ', $date, '.', "\n" if $verbose;
+  my $otime = time;
+
+  print '->Setup              : start', "\n" if $verbose;
+  my $stime = time;
+
+  # Read configurations
+  my $rc = $self->decipher_cfg;
+
+  # Check directories are set
+  $rc = $self->check_dir if $rc;
+
+  # Check for lock files
+  $rc = $self->check_lock if $rc and not $ignore_lock;
+
+  # Set a lock file
+  $rc = $self->_set_lock if $rc;
+
+  # Create build root directory if necessary
+  $rc = $self->_create_build_dir if $rc;
+
+  # Set up directory inheritance
+  $rc = $self->_setup_dir (FULL => $full) if $rc;
+
+  # Set up source directories, targets, etc
+  $rc = $self->_update_bld_info if $rc;
+
+  # Set up PP flag cache
+  $rc = $self->_update_pp_info if $rc;
+
+  # Set up build tools cache
+  $rc = $self->_update_tool_info if $rc;
+
+  # Set up regenerate make rule cache
+  $rc = $self->_update_regen_rule_info if $rc;
+
+  my $ftime = time;
+  my $s_str = $ftime - $stime > 1 ? 'seconds' : 'second';
+  print '->Setup              : ', $ftime - $stime, ' ', $s_str, "\n";
+
+  # Stage 2: Pre-process
+  if ($rc and $stage >= 2) {
+    print '->Pre-process        : start', "\n" if $verbose;
+    my $stime = time;
+
+    $rc = $self->_pre_process;
+
+    $ftime = time;
+    $s_str = $ftime - $stime > 1 ? 'seconds' : 'second';
+    print '->Pre-process        : ', $ftime - $stime, ' ', $s_str, "\n";
+  }
+
+  # Stage 3: Scan dependency and write make rules
+  if ($rc and $stage >= 3) {
+    print '->Scan dependency    : start', "\n" if $verbose;
+    my $stime = time;
+
+    $rc = $self->_scan_dependency;
+    $rc = $self->_write_make_rules if $rc;
+    $rc = $self->_write_makefile if $rc;
+
+    $ftime = time;
+    $s_str = $ftime - $stime > 1 ? 'seconds' : 'second';
+    print '->Scan dependency    : ', $ftime - $stime, ' ', $s_str, "\n";
+  }
+
+  # Stage 4: Generate Fortran 9x interface block
+  if ($rc and $stage >= 4) {
+    print '->Generate interface : start', "\n" if $verbose;
+    my $stime = time;
+
+    $rc = $self->_generate_f9x_interface;
+
+    $ftime = time;
+    $s_str = $ftime - $stime > 1 ? 'seconds' : 'second';
+    print '->Generate interface : ', $ftime - $stime, ' ', $s_str, "\n";
+  }
+
+  # Stage 5: Make the build
+  if ($rc and $stage >= 5) {
+    print '->Make               : start', "\n" if $verbose;
+    my $stime = time;
+
+    $rc = $self->_invoke_make (
+      TARGETS => $targets,
+      JOBS    => $jobs,
+      ARCHIVE => $archive,
+    );
+
+    # Remove empty build directories
+    $rc = $self->_remove_empty_dirs () if $rc;
+
+    # Create TAR archives if necessary
+    $rc = $self->_tar_build_dirs () if $rc and $archive;
+
+    # Create run time environment script if necessary
+    $rc = $self->_create_runenv_script () if $rc;
+
+    # Create exclude dependency configurations for libraries
+    $rc = $self->_create_lib_excl_dep () if $rc;
+
+    $ftime = time;
+    $s_str = $ftime - $stime > 1 ? 'seconds' : 'second';
+    print '->Make               : ', $ftime - $stime, ' ', $s_str, "\n";
+  }
+
+  if ($verbose) {
+    $s_str = $ftime - $otime > 1 ? 'seconds' : 'second';
+    print '->TOTAL              : ', $ftime - $otime, ' ', $s_str, "\n";
+  }
+
+  $date = localtime;
+  if ($rc) {
+    print 'Build command finished on ', $date, '.', "\n" if $verbose;
+
+  } else {
+    e_report 'Build command failed on ', $date, '.';
+  }
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $bld->decipher_cfg ();
+#
+# DESCRIPTION
+#   This method deciphers the build configuration file and assigns the
+#   configurations to the variables of the current build.
+# ------------------------------------------------------------------------------
+
+sub decipher_cfg {
+  my $self = shift;
+
+  my $read = $self->cfg->read_cfg;
+
+  # Check config file type
+  if ($read) {
+    if ($self->cfg->type ne 'bld') {
+      w_report 'Error: ', $self->cfg->src, ': not a build config file.';
+      return;
+    }
+
+  } else {
+    return;
+  }
+
+  my %cfg_label = %{ $self->config->setting ('CFG_LABEL') };
+  my %tools     = %{ $self->config->setting ('TOOL') };
+
+  # Get lines from cfg file
+  my @cfg_lines  = $self->cfg->lines;
+
+  LINE: for my $line (@cfg_lines) {
+    # Label and value of each line
+    my $label = $line->{LABEL};
+    my $value = $line->{VALUE};
+
+    next LINE unless $label; # ignore blank or comment line
+
+    # Strip out BLD prefix from all labels
+    my $prefix = $cfg_label{BDECLARE} . '::';
+    $label = substr ($label, length ($prefix))
+      if index (uc ($label), $prefix) == 0;
+
+    next LINE unless $label; # ignore blank or comment line
+
+    # Configuration file type/version, ignore
+    next LINE if uc $label eq $cfg_label{CFGFILE}{TYPE};
+    next LINE if uc $label eq $cfg_label{CFGFILE}{VERSION};
+
+    # User variable, ignore
+    next LINE if index (uc ($label), '%') == 0;
+
+    # Build name
+    if (uc $label eq $cfg_label{NAME}) {
+      $self->{NAME} = $value;
+      next LINE;
+    }
+
+    # Build directory tree
+    $prefix = $cfg_label{DIR} . '::';
+    if (index (uc ($label), $prefix) == 0) {
+      my $name = substr uc ($label), length ($prefix);
+      $self->{DIR}{$name} = expand_tilde $value;
+      next LINE;
+    }
+
+    # Source directory
+    $prefix = $cfg_label{SRCDIR} . '::';
+    if (index (uc ($label), $prefix) == 0) {
+      my $name = substr $label, length ($prefix);
+      $name    =~ s/::/__/g;
+      $self->{SRCDIR}{$name} = expand_tilde $value;
+      next LINE;
+    }
+
+    # Automatic source directory search?
+    if (uc $label eq $cfg_label{SEARCH_SRC}) {
+      $self->{SEARCH} = $value;
+      next LINE;
+    }
+
+    # Pre-process flag, directory/file requires pre-processing before all tasks
+    $prefix = $cfg_label{PP};
+    if (index (uc ($label), $prefix) == 0) {
+      my @flds = split /::/, $label;
+      my $name = uc shift @flds;
+      $name    = join '__', ($name, @flds) if @flds;
+      $self->{PP}{$name} = $value;
+      next LINE;
+    }
+
+    # Specify name of top level or package library
+    $prefix = $cfg_label{LIB};
+    if (index (uc ($label), $prefix) == 0) {
+      my @flds = split /::/, $label;
+      shift @flds;
+      my $name = @flds ? join ('__', @flds) : '';
+      $self->{LIB}{$name} = $value;
+
+      next LINE;
+    }
+
+    # Specify extra executable dependencies and BLOCKDATA dependency
+    for my $name (qw/EXE_DEP BLOCKDATA/) {
+      $prefix = $cfg_label{$name};
+
+      if (index (uc ($label), $prefix) == 0) {
+        my @flds = split /::/, $label;
+        shift @flds;
+        my $target = @flds ? $flds[0] : '';
+        my @deps   = split /\s+/, $value;
+
+        # If $value is a null string, set executable to depend on all objects
+        if (not @deps) {
+          if ($name eq 'BLOCKDATA') {
+
+            # Label not recognised
+            w_report 'Warning: ', $line->{SRC}, ': LINE ', $line->{NUMBER},
+                     ': "', $label, '" declaration must have a value';
+            next LINE;
+
+          } else {
+            push @deps, '';
+          }
+        }
+
+        for my $dep (@deps) {
+          $dep =~ s/::/__/g;
+
+          $self->config->assign_setting (
+            LABELS => [$name, $target, $dep],
+            VALUE  => 1,
+          );
+        }
+
+        next LINE;
+      }
+    }
+
+    # Build target
+    if (uc $label eq $cfg_label{TARGET}) {
+      push @{ $self->{TARGET} }, split (/\s+/, $value);
+      next LINE;
+    }
+
+    # Rename a main program target
+    $prefix = $cfg_label{EXE_NAME};
+    if (index (uc ($label), $prefix) == 0) {
+      my @flds = split /::/, $label;
+      shift @flds;
+      my $name = shift @flds;
+
+      if ($name and $value) {
+        $self->config->assign_setting (
+          LABELS => ['EXE_NAME', $name],
+          VALUE  => $value,
+        );
+
+        next LINE;
+      }
+    }
+
+    # Build tool
+    $prefix = $cfg_label{TOOL} . '::';
+    if (index (uc ($label), $prefix) == 0) {
+      my $name = substr $label, length ($prefix);
+      my @flds = split /::/, $name;
+
+      $name = uc (shift @flds);
+
+      if (exists $tools{$name}) {
+        $name = join '__', ($name, @flds) if @flds;
+
+        $self->config->assign_setting (
+          LABELS => ['TOOL', $name],
+          VALUE  => $value,
+        );
+        next LINE;
+      }
+    }
+
+    # File name extension and type
+    for my $name (qw/INFILE_EXT OUTFILE_EXT/) {
+      $prefix = $cfg_label{$name};
+      if (index (uc ($label), $prefix) == 0) {
+        my $key = (split /::/, $label)[1];
+        $key    = uc $key if $name eq 'OUTFILE_EXT';
+
+        my $val = ($name eq 'INFILE_EXT') ? uc $value : $value;
+
+        $self->config->assign_setting (
+          LABELS => [$name, $key],
+          VALUE  => $val,
+        );
+        next LINE;
+      }
+    }
+
+    # Dependency scan exclusion
+    $prefix = $cfg_label{EXCL_DEP};
+    if (index (uc ($label), $prefix) == 0) {
+      my @flds = split /::/, $label;
+      shift @flds;
+
+      my $pk = @flds ? join ('__', @flds) : '';
+      $self->config->assign_setting (
+        LABELS => ['EXCL_DEP', uc ($value), $pk],
+        VALUE  => 1,
+      );
+      next LINE;
+    }
+
+    # Use (inherit from) another build
+    if (uc $label eq $cfg_label{USE}) {
+      my $use = Fcm::Build->new (
+        CONFIG  => $self->config,
+        CFG_SRC => expand_tilde ($value),
+      );
+      $use->decipher_cfg;
+      $use->check_dir;
+      push @{ $self->{USE} }, $use;
+      next LINE;
+    }
+
+    # Inheritance flag
+    $prefix = $cfg_label{INHERIT} . '::';
+    if (index (uc ($label), $prefix) == 0) {
+      my $name = substr $label, length ($prefix);
+      my @flds = split /::/, $name;
+
+      $name = uc (shift @flds);
+
+      for my $flag (qw/SRCDIR PP LIB TARGET/) {
+        if ($name eq $cfg_label{$flag}) {
+          $name = @flds ? join ('__', ($flag, @flds)) : $flag;
+          $self->{INHERIT}{$name} = $value;
+          next LINE;
+        }
+      }
+    }
+
+    # Label not recognised
+    w_report 'ERROR: ', $line->{SRC}, ': LINE ', $line->{NUMBER}, ': label "',
+             $label, '" not recognised';
+    return;
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $bld->check_dir ();
+#
+# DESCRIPTION
+#   This method checks whether the build directories are set correctly.
+# ------------------------------------------------------------------------------
+
+sub check_dir {
+  my $self = shift;
+
+  # Make sure build root directory is set
+  if (not $self->{DIR}{ROOT}) {
+    w_report 'Error: build root directory not set.';
+    return;
+  }
+
+  # Set value of build sub-directories if necessary
+  for my $name (keys %{ $self->config->setting ('DIR') }) {
+    next if $self->{DIR}{$name};
+
+    $self->{DIR}{$name} = catfile (
+      $self->{DIR}{ROOT},
+      $self->config->setting ('DIR', $name),
+    );
+  }
+
+  # Search src/ sub-directory if necessary
+  if ($self->{SEARCH} and -d $self->{DIR}{SRC}) {
+    my %dir = find_srcdir ($self->{DIR}{SRC});
+    for my $name (keys %dir) {
+      $self->{SRCDIR}{$name} = $dir{$name} unless $self->{SRCDIR}{$name};
+    }
+  }
+
+  # Expand source directory paths if necessary
+  for my $name (keys %{ $self->{SRCDIR} }) {
+    if ($self->{SRCDIR}{$name} =~ /^\w/) {
+      my $src_search  = catfile $self->{DIR}{SRC} , $self->{SRCDIR}{$name};
+      my $root_search = catfile $self->{DIR}{ROOT}, $self->{SRCDIR}{$name};
+
+      if ($self->{DIR}{SRC} and -d $src_search) {
+        $self->{SRCDIR}{$name} = $src_search;
+
+      } elsif (-d $root_search) {
+        $self->{SRCDIR}{$name} = $root_search;
+
+      } else {
+        w_report 'Warning: cannot locate declared source directory: ',
+                 $self->{SRCDIR}{$name};
+        next;
+      }
+    }
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $bld->check_lock ();
+#
+# DESCRIPTION
+#   This method checks whether a lock is set in the current build.
+# ------------------------------------------------------------------------------
+
+sub check_lock {
+  my $self = shift;
+
+  my $rootdir  = $self->{DIR}{ROOT};
+  my $lock_ext = catfile ($rootdir, $self->config->setting (qw/MISC LOCK_EXT/));
+  my $lock_bld = catfile ($rootdir, $self->config->setting (qw/MISC LOCK_BLD/));
+
+  # Always throw error if extract lock exists
+  if (-e $lock_ext) {
+    w_report 'ERROR: extract lock file exists: ', $lock_ext, ',';
+    w_report '       an extract may be running at ', $rootdir, ', abort.';
+    return;
+  }
+
+  # Always throw error if build lock exists
+  if (-e $lock_bld) {
+    w_report 'ERROR: build lock file exists: ', $lock_bld, ',';
+    w_report '       a build may be running at ', $rootdir, ', abort.';
+    return;
+  }
+
+  # Check locks in inherited build
+  for my $use (@{ $self->{USE} }) {
+    return unless $use->check_lock;
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_set_lock ();
+#
+# DESCRIPTION
+#   This method sets a lock is set in the current build.
+# ------------------------------------------------------------------------------
+
+sub _set_lock {
+  my $self = shift;
+
+  $self->{LOCK} = catfile (
+    $self->{DIR}{ROOT}, $self->config->setting (qw/MISC LOCK_BLD/),
+  );
+
+  &touch_file ($self->{LOCK});
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_setup_dir (FULL => $full);
+#
+# DESCRIPTION
+#   This internal method sets up the build directories.
+# ------------------------------------------------------------------------------
+
+sub _setup_dir {
+  my $self = shift;
+  my %args = @_;
+
+  my $full = exists $args{FULL} ? $args{FULL} : 0;
+
+  my $tar      = $self->config->setting (qw/OUTFILE_EXT TAR/);
+  my @tar_dirs = split /,/, $self->config->setting (qw/TAR_DIRS/);
+  my $verbose  = $self->config->verbose;
+
+  if ($full) {
+    # Remove sub-directories/archives created from previous builds
+    for my $name (qw/BIN BLD DONE ETC FLAGS INC LIB PPSRC OBJ TMP/) {
+      &run_command ([qw/rm -rf/, $self->{DIR}{$name}], PRINT => $verbose)
+        if -d $self->{DIR}{$name};
+
+      &run_command ([qw/rm -f/, $self->{DIR}{$name} . $tar], PRINT => $verbose)
+        if -f $self->{DIR}{$name} . $tar;
+    }
+
+    # Remove cache
+    my @files;
+    if (-d $self->{DIR}{CACHE} and opendir DIR, $self->{DIR}{CACHE}) {
+      @files = grep {$_ ne '.' and $_ ne '..'} readdir 'DIR';
+      closedir DIR;
+    }
+
+    my $extension = '\\' . $self->config->setting (qw/CACHE PCKFILE/) . '|' .
+                    '\\' . $self->config->setting (qw/CACHE PCKPPDEPEND/) . '|' .
+                    '\\' . $self->config->setting (qw/CACHE PCKDEPEND/);
+
+
+    for my $file (@files) {
+      next unless $file eq $self->config->setting (qw/CACHE BLDTOOL/) or
+                  $file eq $self->config->setting (qw/CACHE PPOPTION/) or
+                  $file eq $self->config->setting (qw/CACHE EXE_DEP/) or
+                  $file =~ /$extension$/;
+
+      my $path = File::Spec->catfile ($self->{DIR}{CACHE}, $file);
+      &run_command ([qw/rm -f/, $path], PRINT => $verbose);
+    }
+
+  } else {
+    # Extract archives if necessary
+    for my $name (@tar_dirs) {
+      my $tar_file = $self->{DIR}{$name} . $tar;
+
+      if (-f $tar_file) {
+        &run_command ([qw/tar -x -f/, $tar_file], PRINT => $verbose > 1);
+        &run_command ([qw/rm -f/, $tar_file], PRINT => $verbose > 1);
+      }
+    }
+  }
+
+  # Set up search paths
+  for my $name (keys %{ $self->{DIR} }) {
+    $self->{PATH}{$name} = [$self->_get_inherited_paths ($name)];
+
+    $self->config->assign_setting (
+      LABELS => ['PATH', $name],
+      VALUE  => $self->{PATH}{$name},
+    )
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_update_bld_info ();
+#
+# DESCRIPTION
+#   This internal method sets up the inheritance relationship for source
+#   directories and targets, and other configurations required by the build.
+# ------------------------------------------------------------------------------
+
+sub _update_bld_info {
+  my $self = shift;
+
+  # Set up build targets
+  $self->{TARGET} = [$self->_get_inherited_items ('TARGET')];
+
+  # Set up PP switches
+  $self->{PP} = {$self->_get_inherited_items ('PP')};
+
+  # Set up source directory packages for this build
+  my %srcdir = $self->_get_inherited_items ('SRCDIR');
+  for my $name (keys %srcdir) {
+    my $package = Fcm::SrcPackage->new (
+      CONFIG     => $self->config,
+      NAME       => $name,
+      CURRENT    => exists $self->{SRCDIR}{$name},
+      REQUIREPP  => $self->_require_pp ($name),
+      SEARCHPATH => [$self->_get_inherited_srcdirs ($name)],
+    );
+
+    $package->update_file_info ();
+
+    $self->{PACKAGE}{$name} = $package;
+  }
+
+  # Set up runtime dependency scan patterns
+  my %dep_pattern = %{ $self->config->setting ('DEP_PATTERN') };
+  for my $key (keys %dep_pattern) {
+    my $pattern = $dep_pattern{$key};
+
+    while ($pattern =~ /##([\w:]+)##/g) {
+      my $match = $1;
+      my $val   = $self->config->setting (split (/::/, $match));
+
+      last unless defined $val;
+      $val =~ s/\./\\./;
+
+      $pattern =~ s/##$match##/$val/;
+    }
+
+    $self->config->assign_setting (
+      LABELS => ['DEP_PATTERN', $key],
+      VALUE  => $pattern,
+    ) unless $pattern eq $dep_pattern{$key};
+  }
+
+  # Set up top level library name
+  {
+    $self->{LIB} = {$self->_get_inherited_items ('LIB')};
+
+    my $lib = $self->{LIB}{''};
+    $lib    = ($self->{NAME} ? $self->{NAME} : 'fcm_default') unless $lib;
+    $self->{LIB}{''} = $lib;
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_update_regen_rule_info ();
+#
+# DESCRIPTION
+#   This internal method compares the current EXE_DEP, BLOCKDATA and EXE_NAME
+#   declarations and the previous. If changed, the REGEN_MAKERULE flag will be
+#   set to true and the cache will be updated.
+# ------------------------------------------------------------------------------
+
+sub _update_regen_rule_info {
+  my $self = shift;
+
+  # Look for an extra executable dependency cache file
+  my $cachebase = $self->config->setting (qw/CACHE EXE_DEP/);
+  my $incache   = find_file_in_path $cachebase, $self->{PATH}{CACHE};
+
+  my $uptodate = 0;
+  my @inlines  = ();
+
+  # Read cache if it exists
+  if ($incache and -r $incache) {
+    my $incfg = Fcm::CfgFile->new (CONFIG => $self->config, SRC => $incache);
+    $incfg->read_cfg;
+
+    @inlines  = $incfg->lines;
+    $uptodate = 1;
+  }
+
+  # Prepare output lines
+  my $outcfg = Fcm::CfgFile->new (CONFIG => $self->config);
+  $outcfg->add_line (COMMENT => 'EXE_DEP cache');
+
+  # List of extra executable dependencies
+  my %exe_dep   = %{ $self->config->setting ('EXE_DEP') };
+  for my $target (sort keys %exe_dep) {
+    $outcfg->add_line (
+      LABEL => ($target ? 'OBJECTS__' . $target : 'OBJECTS'),
+      VALUE => join (' ', sort keys %{ $exe_dep{$target} }),
+    );
+  }
+
+  # List of BLOCKDATA dependencies
+  my %blockdata = %{ $self->config->setting ('BLOCKDATA') };
+  for my $target (sort keys %blockdata) {
+    $outcfg->add_line (
+      LABEL => ($target ? 'BLOCKDATA__' . $target : 'BLOCKDATA'),
+      VALUE => join (' ', sort keys %{ $blockdata{$target} }),
+    );
+  }
+
+  # List of EXE_NAME
+  my %exe_name = %{ $self->config->setting ('EXE_NAME') };
+  for my $target (sort keys %exe_name) {
+    $outcfg->add_line (
+      LABEL => 'EXE_NAME__' . $target,
+      VALUE => $exe_name{$target},
+    );
+  }
+
+  # Compare cache with current output
+  my @outlines = $outcfg->lines ();
+
+  $uptodate = 0 if @inlines != @outlines;
+
+  if ($uptodate) {
+    for my $i (0 .. $#outlines) {
+      next unless $inlines[$i]->{LABEL} and $outlines[$i]->{LABEL};
+
+      if ($inlines[$i]->{LABEL} ne $outlines[$i]->{LABEL} or
+          $inlines[$i]->{VALUE} ne $outlines[$i]->{VALUE}) {
+        $uptodate = 0;
+        last;
+      }
+    }
+  }
+
+  # Update cache if it is out of date
+  $outcfg->print_cfg (catfile ($self->{DIR}{CACHE}, $cachebase))
+    unless $uptodate;
+
+  # If out to date, set regenerate make rule flag to true
+  $self->config->assign_setting (
+    LABELS => [qw/REGEN_MAKERULE/],
+    VALUE  => ! $uptodate,
+  );
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_update_pp_info ();
+#
+# DESCRIPTION
+#   This internal method compares the current set of pre-processor options
+#   with that of the previous build using a "cache" file. If some
+#   pre-processor options have changed, the method updates the cache file and
+#   remove the "make" rules for the appropriate source packages.
+# ------------------------------------------------------------------------------
+
+sub _update_pp_info {
+  my $self = shift;
+
+  # Look for a PP option cache file
+  my $cachebase = $self->config->setting (qw/CACHE PPOPTION/);
+  my $incache   = find_file_in_path $cachebase, $self->{PATH}{CACHE};
+
+  my @chgpp = ();
+  my %newpp = %{ $self->{PP} };
+
+  # Read config if exists, otherwise marked all current PP flags as "changed"
+  if ($incache and -r $incache) {
+    my $cfg = Fcm::CfgFile->new (CONFIG => $self->config, SRC => $incache);
+    $cfg->read_cfg;
+
+    my @lines   = $cfg->lines;
+    my %oldpp = ();
+
+    for my $line (@lines) {
+      next unless $line->{LABEL};
+
+      $oldpp{$line->{LABEL}} = $line->{VALUE};
+    }
+
+    # Compare new and old, mark as "changed" if changed or does not exist in old
+    @chgpp = (grep {
+      exists $oldpp{$_} ? $oldpp{$_} ne $newpp{$_} : 1;
+    } keys %newpp);
+
+    # Compare old and new, mark as "changed" if not exist in new
+    push @chgpp, (grep {not exists $newpp{$_}} keys %oldpp);
+
+  } else {
+    @chgpp = keys %newpp;
+  }
+
+  if (@chgpp) {
+    for my $name (@chgpp) {
+      for my $package (values %{ $self->{PACKAGE} }) {
+        next if $package->newpp;
+
+        if (('PP__' . $package->name) =~ /^$name(?:__|$)/) {
+          $package->current (1);
+          $package->newpp   (1);
+        }
+      }
+    }
+
+    # Update the PP cache file if necessary
+    my $cfg = Fcm::CfgFile->new (CONFIG => $self->config);
+
+    for my $name (keys %newpp) {
+      $cfg->add_line (LABEL => $name, VALUE => $newpp{$name});
+    }
+    $cfg->add_line unless $cfg->lines;
+
+    $cfg->print_cfg (catfile ($self->{DIR}{CACHE}, $cachebase));
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_update_tool_info ();
+#
+# DESCRIPTION
+#   This internal method compares the current set of build tools with that of
+#   the previous build using a "cache" file. If some build tools have changed,
+#   the method updates the cache file and (the time stamps of) dummy "flags"
+#   files to denote changes in build tools from the previous build.
+# ------------------------------------------------------------------------------
+
+sub _update_tool_info {
+  my $self = shift;
+
+  # Look for a build tool cache file
+  my $cachebase = $self->config->setting (qw/CACHE BLDTOOL/);
+  my $incache   = find_file_in_path $cachebase, $self->{PATH}{CACHE};
+
+  my @chgtool = ();
+  my %newtool = %{ $self->config->setting ('TOOL') };
+
+  # Read config if exists, otherwise marked all current tools as "changed"
+  if ($incache and -r $incache) {
+    my $cfg = Fcm::CfgFile->new (CONFIG => $self->config, SRC => $incache);
+    $cfg->read_cfg;
+
+    my @lines   = $cfg->lines;
+    my %oldtool = ();
+
+    for my $line (@lines) {
+      next unless $line->{LABEL};
+
+      $oldtool{$line->{LABEL}} = $line->{VALUE};
+    }
+
+    # Compare new and old, mark as "changed" if changed or does not exist in old
+    @chgtool = (grep {
+      exists $oldtool{$_} ? $oldtool{$_} ne $newtool{$_} : 1;
+    } keys %newtool);
+
+    # Compare old and new, mark as "changed" if not exist in new
+    push @chgtool, (grep {not exists $newtool{$_}} keys %oldtool);
+
+  } else {
+    @chgtool = keys %newtool;
+  }
+
+  if (@chgtool) {
+    # Update the time stamps of dummy files for changed tools
+    $self->_create_build_dir ('FLAGS');
+
+    my $ext = $self->config->setting (qw/OUTFILE_EXT FLAGS/);
+    for my $name (@chgtool) {
+      my $file = catfile $self->{DIR}{FLAGS}, $name . $ext;
+
+      # Create/touch the file
+      touch_file $file or croak 'Unable to update: ', $file, ', abort';
+
+      print 'Updated: ', $file, "\n" if $self->config->verbose > 2;
+    }
+
+    # Update the build tool cache file if necessary
+    my $cfg = Fcm::CfgFile->new (CONFIG => $self->config);
+
+    for my $name (keys %newtool) {
+      $cfg->add_line (LABEL => $name, VALUE => $newtool{$name});
+    }
+
+    $cfg->print_cfg (catfile ($self->{PATH}{CACHE}->[0], $cachebase));
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_pre_process ();
+#
+# DESCRIPTION
+#   This internal method obtains a list of source files that require
+#   pre-processing in the source packages of this build, and attempts to
+#   pre-process them. The method returns 1 on success.
+# ------------------------------------------------------------------------------
+
+sub _pre_process {
+  my $self = shift;
+
+  # Go through source packages/files to see if PP is required
+  my @srcfiles = ();
+  for my $package (values %{ $self->{PACKAGE} }) {
+    next unless $package->requirepp;
+
+    $package->scan_dependency (HEADER_ONLY => 1);
+
+    push @srcfiles, grep ({$_->is_type_or (qw/FPP C/)} $package->srcfile);
+  }
+
+  return 1 unless @srcfiles;
+
+  my %task     = ();
+  my $flagsext = $self->config->setting (qw/OUTFILE_EXT FLAGS/);
+  my $pdoneext = $self->config->setting (qw/OUTFILE_EXT PDONE/);
+
+  # Set up tasks for each source file
+  for my $srcfile (@srcfiles) {
+    my $command  = $srcfile->is_type ('FPP') ? 'FPP' : 'CPP';
+    my @pck_list = $srcfile->get_package_list;
+    my @pknames  = split '__', pop (@pck_list);
+
+    # Set up a PP build task for each source file
+    my $target    = $srcfile->base . $pdoneext;
+    my $ppkeyname = join ('__', ($command . 'KEYS' , @pknames)) . $flagsext;
+    my $flagsname = join ('__', ($command . 'FLAGS', @pknames)) . $flagsext;
+
+    # Issue warning for duplicated tasks
+    if (exists $task{$target}) {
+      w_report 'Warning: ', $target, ': unable to create task for: ',
+               $srcfile->src, ': task already exists for: ',
+               $task{$target}->srcfile->src;
+      next;
+    }
+
+    $task{$target} = Fcm::BuildTask->new (
+      CONFIG     => $self->config,
+      TARGET     => $target,
+      TARGETPATH => $self->{PATH}{DONE},
+      SRCFILE    => $srcfile,
+      DEPENDENCY => [$ppkeyname, $flagsname, ($srcfile->dep ('H'))],
+      ACTIONTYPE => 'PP',
+    );
+
+    # Set up update ppkeys/flags build tasks for each source file/package
+    for my $i (0 .. $#pknames) {
+      my $name  = join '__', @pknames [0 .. $i];     # package name
+      my $dname = join '__', @pknames [0 .. $i - 1]; # dependent package name
+
+      for my $flag (qw/KEYS FLAGS/) {
+        my $fullflag = $command . $flag;
+        my $target   = join '__', ($fullflag, $name);
+        my $depend   = $dname ? join '__', ($fullflag, $dname) : $fullflag;
+
+        $target .= $flagsext;
+        $depend .= $flagsext;
+
+        next if exists $task{$target};
+
+        $task{$target} = Fcm::BuildTask->new (
+          CONFIG     => $self->config,
+          TARGET     => $target,
+          TARGETPATH => $self->{PATH}{FLAGS},
+          DEPENDENCY => [$depend],
+          ACTIONTYPE => 'UPDATE',
+        );
+      }
+    }
+  }
+
+  # Set up update global ppkeys/flags build tasks
+  for my $command (qw/CPP FPP/) {
+    for my $flag ('', qw/KEYS FLAGS/) {
+      my $target = $command . $flag . $flagsext;
+
+      $task{$target} = Fcm::BuildTask->new (
+        CONFIG     => $self->config,
+        TARGET     => $target,
+        TARGETPATH => $self->{PATH}{FLAGS},
+        ACTIONTYPE => 'UPDATE',
+      );
+    }
+  }
+
+  # Set up build tasks to copy all header files
+  for my $package (values %{ $self->{PACKAGE} }) {
+    my @files = grep {$_->is_type (qw/CPP INCLUDE/)} $package->srcfile;
+
+    # Author's note: may also want to issue warning for duplicated tasks
+
+    for my $file (@files) {
+      $task{$file->base} = Fcm::BuildTask->new (
+        CONFIG     => $self->config,
+        TARGET     => $file->base,
+        TARGETPATH => $self->{PATH}{INC},
+        SRCFILE    => $file,
+        DEPENDENCY => [$file->dep ('H')],
+        ACTIONTYPE => 'COPY',
+      );
+    }
+  }
+
+  # Build all PP tasks
+  my $count = 0;
+
+  for my $task (values %task) {
+    next unless $task->actiontype eq 'PP';
+
+    my $rc = $task->action (TASKLIST => \%task);
+    $count++ if $rc;
+  }
+
+  print 'Number of pre-processed files: ', $count, "\n"
+    if $self->config->verbose and $count;
+
+  # Change path and file type of pre-processed source files
+  for my $task (values %task) {
+    next unless $task->actiontype eq 'PP';
+
+    # Remove header dependencies from source file
+    my %dep = $task->srcfile->dep ();
+    for my $key (keys %dep) {
+      delete $dep{$key} if $dep{$key} eq 'H';
+    }
+    $task->srcfile->dep (\%dep);
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_generate_f9x_interface ();
+#
+# DESCRIPTION
+#   This internal method obtains a list of Fortran 9X source files in the
+#   source packages of this build, and attempts to generate an interface block
+#   file for each of the Fortran 9X source files. The method returns 1 on
+#   success.
+# ------------------------------------------------------------------------------
+
+sub _generate_f9x_interface {
+  my $self = shift;
+
+  # Go through source packages/files for Fortran 9x source files with
+  # standalone subroutines or functions
+  my @srcfiles = ();
+  for my $package (values %{ $self->{PACKAGE} }) {
+    next unless $package->current;
+
+    push @srcfiles, grep {
+      $_->is_type_or (qw/FORTRAN9X FPP9X/) and
+      uc ($_->select_tool ('GENINTERFACE')) ne 'NONE' and
+      not $_->is_type_or (qw/PROGRAM MODULE INCLUDE/)
+    } $package->srcfile;
+  }
+
+  my $flagsext = $self->config->setting (qw/OUTFILE_EXT FLAGS/);
+  my $pdoneext = $self->config->setting (qw/OUTFILE_EXT PDONE/);
+
+  # Set up build task to generate interface files for all selected Fortran 9x
+  # sources
+  my %task         = ();
+  for my $srcfile (@srcfiles) {
+    my $target  = $srcfile->interfacebase . $pdoneext;
+    my @pknames = split '__', ($srcfile->get_package_list)[-1];
+    my $flag    = join ('__', ('GENINTERFACE', @pknames)) . $flagsext;
+
+    $task{$target} = Fcm::BuildTask->new (
+      CONFIG     => $self->config,
+      TARGET     => $target,
+      TARGETPATH => $self->{PATH}{DONE},
+      SRCFILE    => $srcfile,
+      DEPENDENCY => [$flag],
+      ACTIONTYPE => 'GENINTERFACE',
+    );
+
+    # Set up build tasks for each source file/package flags file for interface
+    # generator tool
+    for my $i (0 .. $#pknames) {
+      my $name   = join '__', @pknames [0 .. $i];     # package name
+      my $dname  = join '__', @pknames [0 .. $i - 1]; # dependent package name
+
+      my $target = join '__', ('GENINTERFACE', $name);
+      my $depend = $dname ? join '__', ('GENINTERFACE', $dname) : 'GENINTERFACE';
+
+      $target .= $flagsext;
+      $depend .= $flagsext;
+
+      next if exists $task{$target};
+
+      $task{$target} = Fcm::BuildTask->new (
+        CONFIG     => $self->config,
+        TARGET     => $target,
+        TARGETPATH => $self->{PATH}{FLAGS},
+        DEPENDENCY => [$depend],
+        ACTIONTYPE => 'UPDATE',
+      );
+    }
+  }
+
+  # Set up build task to update the flags file for interface generator tool
+  {
+    my $target     = 'GENINTERFACE' . $flagsext;
+    $task{$target} = Fcm::BuildTask->new (
+      CONFIG     => $self->config,
+      TARGET     => $target,
+      TARGETPATH => $self->{PATH}{FLAGS},
+      ACTIONTYPE => 'UPDATE',
+    );
+  }
+
+  my $count = 0;
+
+  # Performs task
+  for my $task (values %task) {
+    next unless $task->actiontype eq 'GENINTERFACE';
+
+    my $rc = $task->action (TASKLIST => \%task);
+    $count++ if $rc;
+  }
+
+  print 'Number of generated interfaces: ', $count, "\n"
+    if $self->config->verbose and $count;
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_scan_dependency ();
+#
+# DESCRIPTION
+#   This internal method goes through each source package to obtain dependency
+#   information from their source files. It returns 1 on success.
+# ------------------------------------------------------------------------------
+
+sub _scan_dependency {
+  my $self = shift;
+
+  # Go through source packages/files
+  my $count = 0;
+
+  for my $package (values %{ $self->{PACKAGE} }) {
+    my $rc = $package->scan_dependency;
+    $count++ if $rc;
+  }
+
+  print 'Scanned files in ', $count, ' package(s) for dependency', "\n"
+    if $self->config->verbose and $count;
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_set_targets ();
+#
+# DESCRIPTION
+#   This internal method determines the default targets to be built.
+# ------------------------------------------------------------------------------
+
+sub _set_targets {
+  my $self = shift;
+
+  # Targets of the build
+  if (not @{ $self->{TARGET} }) {
+    # Build targets not specified by user, default to building all main programs
+    my @programs = ();
+
+    # Get all main programs from all packages
+    for my $package (values %{ $self->{PACKAGE} }) {
+      my @srcfiles = grep {$_->exebase} $package->srcfile;
+
+      for (@srcfiles) {
+        push @programs, $_->exebase;
+      }
+    }
+
+    @programs = sort (@programs);
+
+    if (@programs) {
+      # Build main programs, if there are any
+      @{ $self->{TARGET} } = @programs;
+
+    } else {
+      # No main program in source tree, build the default library
+      @{ $self->{TARGET} } = (
+        'lib' . $self->{LIB}{''} . $self->config->setting (qw/OUTFILE_EXT LIB/),
+      );
+    }
+  }
+
+  return @{ $self->{TARGET} };
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_write_make_rules ();
+#
+# DESCRIPTION
+#   This internal method writes the included make rules if necessary.
+# ------------------------------------------------------------------------------
+
+sub _write_make_rules {
+  my $self   = shift;
+  my $return = 1;
+
+  # Get list of all packages
+  my %allpcks = $self->allpcks;
+
+  # Get list of types that cannot have duplicated targets
+  my @no_duplicated_target_types = split (
+    /,/, $self->config->setting ('NO_DUPLICATED_TARGET_TYPE'),
+  );
+
+  my $unusual = 0;
+  my $count   = 0;
+  my %targets;
+  for my $name (sort keys %allpcks) {
+    # Check whether package is an actual package of the build
+    my $package  = exists $self->{PACKAGE}{$name} ? $self->{PACKAGE}{$name} : '';
+
+    # Regenerate make rules if this flag is set to true
+    my $regen_mk;
+
+    # Register non-word package name
+    if (not $name =~ /^\w*$/) {
+      $self->config->assign_setting (
+        LABELS => ['FCM_PCK_OBJECTS', $name],
+        VALUE  => 'FCM_PCK_OBJECTS' . $unusual++,
+      );
+
+      # Set regenerate make rule flag to true
+      $regen_mk = 1;
+    }
+
+    # Cycle loop if not an actual package of the build
+    next unless $package;
+
+    # Check whether make rule for each source package needs updating. Yes if:
+    # 1. package name contains non-word characters
+    # 2. the config setting REGEN_MAKERULE is set to true
+    # 3. package is current and the make rule for the package is out of date
+    $regen_mk = (
+      $regen_mk or
+      $self->config->setting ('REGEN_MAKERULE') or
+      ($package->current and not $package->makerule_uptodate)
+    );
+
+    # Update make rule for source package, if necessary
+    $count += $package->write_makerule () if $regen_mk;
+
+    # Check for duplicated rules
+    for my $srcfile (sort $package->srcfile) {
+      next unless $srcfile->type;
+
+      my %rules = $srcfile->required_rules;
+
+      for my $key (sort keys %rules) {
+        if (exists $targets{$key}) {
+          # Duplicated target: warning for most file types
+          my $status = 'WARNING';
+
+          # Duplicated target: error for the following file types
+          if (@no_duplicated_target_types and
+              $srcfile->is_type_or (@no_duplicated_target_types) and
+              $targets{$key}->is_type_or (@no_duplicated_target_types)) {
+            $status = 'ERROR';
+            $return = 0;
+          }
+
+          # Report the warning/error
+          w_report $status, ': ', $key, ': duplicated targets for building:';
+          w_report '       ', $targets{$key}->src;
+          w_report '       ', $srcfile->src;
+
+        } else {
+          $targets{$key} = $srcfile;
+        }
+      }
+    }
+  }
+
+  # Diagnostic
+  print 'Updated make rules for ', $count, ' package(s).', "\n"
+    if $count and $self->config->verbose;
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_write_makefile ();
+#
+# DESCRIPTION
+#   This internal method writes the "Makefile" for this build.
+# ------------------------------------------------------------------------------
+
+sub _write_makefile {
+  my $self = shift;
+
+  # Makefile header
+  # ----------------------------------------------------------------------------
+  my $makefile = '# Automatic Makefile' . "\n\n";
+
+  # Name of the build
+  $makefile .= 'FCM_BUILD_NAME = ' . $self->{NAME} . "\n" if $self->{NAME};
+  
+  # Location of FCM config file
+  $makefile .= 'FCM_BLD_CFG = ' . $self->cfg->src . "\n";
+
+  # Targets of the build
+  $makefile .= 'FCM_BLD_TARGETS = ' . join (' ', ($self->_set_targets)) . "\n\n";
+
+  # Perl library
+  # ----------------------------------------------------------------------------
+  {
+    my $libdir  = dirname (dirname ($INC{'Fcm/Build.pm'}));
+    my @libpath = split /:/, ($ENV{PERL5LIB} ? $ENV{PERL5LIB} : '');
+
+    if (not grep (m/$libdir/, @libpath)) {
+      $makefile .= 'export PERL5LIB := ' . $libdir;
+      $makefile .= ':$(PERL5LIB)' if exists $ENV{PERL5LIB};
+      $makefile .= "\n\n";
+    }
+  }
+
+  # Build directories
+  # ----------------------------------------------------------------------------
+  my @keys    = ('ROOT', sort grep {$_ ne 'ROOT'} keys (%{ $self->{DIR} }));
+  my $rootdir = $self->{DIR}{ROOT};
+
+  # Build sub-directories
+  for my $name (@keys) {
+    my $dir = $self->{DIR}{$name};
+    $dir    =~ s/^$rootdir/\$(FCM_ROOTDIR)/ unless $name eq 'ROOT';
+
+    $makefile .= 'export FCM_' . $name . 'DIR = ' . $dir . "\n";
+  }
+
+  $makefile .= "\n";
+
+  # Build sub-directory paths
+  for my $name (@keys) {
+    my @path = @{ $self->{PATH}{$name} };
+    shift @path;
+
+    $makefile .= 'export FCM_' . $name . 'PATH = ' .
+                 join (':', ('$(FCM_' . $name . 'DIR)', @path)) . "\n";
+  }
+
+  $makefile .= "\n";
+
+  # Build tools
+  # ----------------------------------------------------------------------------
+  # List of build tools
+  my $tool          = $self->config->setting ('TOOL');
+
+  # List of tools local to FCM, (will not be exported)
+  my %localtool     = map {($_, 1)} split (    # map into a hash table
+    /,/, $self->config->setting ('LOCALTOOL'), # split comma separated list
+  );
+
+  # Export required tools
+  my $unusual_count = 0;
+  for my $name (sort keys %$tool) {
+    # Ignore local tools
+    my $topname = (split (/__/, $name))[0];
+    next if exists $localtool{$topname};
+
+    if ($name =~ /^\w+$/) {
+      # Tools with normal name, just export it as an environment variable
+      $makefile .= 'export ' . $name . ' = ' . $tool->{$name} . "\n";
+
+    } else {
+      # Tools with unusual characters, export using a label/value pair
+      $makefile .= 'export FCM_UNUSUAL_TOOL_LABEL' . $unusual_count . ' = ' .
+                   $name . "\n";
+      $makefile .= 'export FCM_UNUSUAL_TOOL_VALUE' . $unusual_count . ' = ' .
+                   $tool->{$name} . "\n";
+      $unusual_count++;
+    }
+  }
+
+  $makefile .= "\n";
+
+  # Verbose mode
+  # ----------------------------------------------------------------------------
+  $makefile .= 'export FCM_VERBOSE ?= ' . $self->config->verbose . "\n\n";
+
+  # VPATH
+  # ----------------------------------------------------------------------------
+  # $name is internal name of build sub-directories
+  # $type is the type of files
+  for my $name (sort keys %{ $self->config->setting ('VPATH') }) {
+    my @types = split /,/, $self->config->setting ('VPATH', $name);
+
+    for my $type (sort @types) {
+      # If $type is EMPTY, it is a file with no file name extension
+      if (uc ($type) eq 'EMPTY') {
+        $makefile .= 'vpath % $(FCM_' . $name . 'PATH)' . "\n";
+
+      } elsif ($type =~ s/^(in|out)://i) {
+        if (uc ($1) eq 'IN') {
+          # If $type begins with IN:<type>, it is a list of file extensions that
+          # can be found under the INFILE_EXT hash in the configuration setting,
+          # with <type> matching a keyword in the values of the hash.
+          my %infile_ext = %{ $self->config->setting ('INFILE_EXT') };
+
+          for my $ext (sort keys %infile_ext) {
+            $makefile .= 'vpath %.' . $ext . ' $(FCM_' . $name . 'PATH)' . "\n"
+              if grep {$_ eq $type} split /::/, $infile_ext{$ext};
+          }
+
+        } else {
+          # If $type begins with OUT:<type>, it is the value of a hash element
+          # in the OUTFILE_EXT hash in the configuration setting, with <type>
+          # matching the key.
+          my $ext    = $self->config->setting ('OUTFILE_EXT', $type);
+          $makefile .= 'vpath %' . $ext . ' $(FCM_' . $name . 'PATH)' . "\n";
+        }
+
+      } else {
+        # Otherwise, $type is a VPATH pattern recognised by "make".
+        $makefile .= 'vpath ' . $type . ' $(FCM_' . $name . 'PATH)' . "\n";
+      }
+    }
+  }
+
+  # VPATH for dummy files
+  $makefile .= 'vpath %.dummy $(FCM_DONEDIR)' . "\n";
+  $makefile .= "\n";
+
+  # Default targets
+  # ----------------------------------------------------------------------------
+  $makefile .= '.PHONY : all clean' . "\n\n";
+  $makefile .= 'all : $(FCM_BLD_TARGETS)' . "\n\n";
+  $makefile .= 'clean : ' . "\n";
+  $makefile .= "\t" . 'rm -rf';
+  for my $dir (qw/BIN LIB OBJ DONE/) {
+    $makefile .= ' $(FCM_' . $dir . 'DIR)' if exists $self->{DIR}{$dir};
+  }
+  $makefile .= "\n";
+  $makefile .= "\t" . 'rm -f lib__*' .
+               $self->config->setting (qw/OUTFILE_EXT LIB/) .
+               ' *' . $self->config->setting (qw/OUTFILE_EXT OBJ/) . "\n";
+  $makefile .= "\n";
+
+  # Targets for copy dummy
+  $makefile .= $self->config->setting (qw/MISC CPDUMMY/) . ' :' . "\n";
+  $makefile .= "\t" . 'touch $@' . "\n\n";
+
+  # Targets for all (non-main-program) objects and libraries
+  # ----------------------------------------------------------------------------
+  my %allpcks = $self->allpcks;
+  for my $key (reverse sort keys %allpcks) {
+    # Objects variable
+    my $var;
+    if ($self->config->setting ('FCM_PCK_OBJECTS', $key)) {
+      # Package name contains unusual characters, use predefined variable
+      $var = $self->config->setting ('FCM_PCK_OBJECTS', $key);
+
+    } else {
+      # Normal package name, prefix the package name with "OBJECTS__"
+      # Top level package, simply set to "OBJECTS"
+      $var = $key ? join ('__', ('OBJECTS', $key)) : 'OBJECTS';
+    }
+
+    # Export top level OBJECTS variable
+    # but keep sub-package OBJECTS variables local to the Makefile
+    $makefile .= ($var eq 'OBJECTS' ? 'export ' : '') . $var . ' =';
+
+    # Add objects from children
+    if (@{ $allpcks{$key} }) {
+      # List of sub-packages of current package
+      my @deps   = map {
+        if ($self->config->setting ('FCM_PCK_OBJECTS', $_)) {
+          # Package name contains unusual characters, use predefined variable
+          '$(' . $self->config->setting ('FCM_PCK_OBJECTS', $_) . ')';
+
+        } else {
+          # Normal package name, prefix the package name with "OBJECTS__"
+          '$(OBJECTS__' . $_ . ')';
+        }
+      } @{ $allpcks{$key} };
+
+      $makefile .= ' ' . join (' ', sort @deps);
+    }
+
+    # Add its own objects
+    if (exists $self->{PACKAGE}{$key}) {
+      # List of source files in the current package
+      my @files = sort {$a->base cmp $b->base} $self->{PACKAGE}{$key}->srcfile;
+
+      for my $file (@files) {
+        # Consider compilable source files only
+        next unless $file->objbase;
+
+        # Ignore main programs and Fortran BLOCKDATA program units
+        next if $file->is_type_or (qw/PROGRAM BLOCKDATA/);
+
+        # Add to object list
+        $makefile .= ' ' . $file->objbase;
+      }
+    }
+
+    $makefile .= "\n\n";
+
+    # Library target
+    my $lib = exists ($self->{LIB}{$key}) ? $self->{LIB}{$key} : $key;
+    $lib    = 'lib' . $lib . $self->config->setting (qw/OUTFILE_EXT LIB/);
+
+    $makefile .= $lib . ' : $(' . $var . ')' . "\n";
+    $makefile .= "\t" . 'fcm_internal archive $@ $(^F)' . "\n\n";
+  }
+
+  # Targets for top level and package flags files and dummy dependencies
+  my %src_tool   = %{ $self->config->setting ('SRC_TOOL') };
+  my %flags_tool = (LD => '', LDFLAGS => '');
+
+  for my $key (keys %src_tool) {
+    $flags_tool{$src_tool{$key}{FLAGS}} = $src_tool{$key}{COMPILER}
+      if exists $src_tool{$key}{FLAGS};
+
+    $flags_tool{$src_tool{$key}{PPKEYS}} = ''
+      if exists $src_tool{$key}{PPKEYS};
+  }
+
+  my $ext = $self->config->setting (qw/OUTFILE_EXT FLAGS/);
+  for my $name (sort keys %flags_tool) {
+    # Flags files for tool command
+    if ($flags_tool{$name}) {
+      $makefile .= $flags_tool{$name} . $ext . ' :' . "\n";
+      $makefile .= "\t" . 'touch ' . catfile ('$(FCM_FLAGSDIR)', '$@') . "\n\n";
+    }
+
+    # Top level flags files
+    $makefile .= $name . $ext . ' :';
+    $makefile .= ' ' . $flags_tool{$name} . $ext if $flags_tool{$name};
+    $makefile .= "\n\t" . 'touch ' . catfile ('$(FCM_FLAGSDIR)', '$@') . "\n\n";
+
+    # Package level flags files
+    for my $key (sort keys %allpcks) {
+      next unless @{ $allpcks{$key} }; # ignore packages without children
+
+      my $depend  = $key ? join '__', ($name, $key) : $name;
+      my @targets = sort map {$name . '__' . $_ . $ext} @{ $allpcks{$key} };
+
+      $makefile .= join (' ', @targets) . ' : ' . $depend . $ext . "\n";
+      $makefile .= "\t" . 'touch ' . catfile ('$(FCM_FLAGSDIR)', '$@') .
+                   "\n\n";
+    }
+  }
+
+  # Include source package make rules
+  # ----------------------------------------------------------------------------
+  for my $package (sort {$a->name cmp $b->name} values %{ $self->{PACKAGE} }) {
+    my $mkbase = $package->name . $self->config->setting (qw/OUTFILE_EXT MK/);
+    my $mkfile = find_file_in_path ($mkbase, $self->{PATH}{BLD});
+
+    if ($mkfile) {
+      if (index ($mkfile, $self->{DIR}{BLD}) == 0) {
+        $mkfile = catfile '$(FCM_BLDDIR)',
+                  substr ($mkfile, length ($self->{DIR}{BLD}) + 1);
+
+      } elsif (index ($mkfile, $self->{DIR}{ROOT}) == 0) {
+        $mkfile = catfile '$(FCM_ROOTDIR)',
+                  substr ($mkfile, length ($self->{DIR}{ROOT}) + 1);
+      }
+
+      $makefile .= 'include ' . $mkfile . "\n";
+
+    } else {
+      my $pck = join ('::', split (/__/, $package->name));
+      w_report 'Warning: no make rule file for source package: ', $pck;
+    }
+  }
+
+  $makefile .= "\n" . '# EOF' . "\n";
+
+  # Print Makefile
+  # ----------------------------------------------------------------------------
+  $self->_create_build_dir ('BLD');
+  my $out = catfile (
+    $self->{DIR}{BLD}, $self->config->setting (qw/MISC MAKEFILE/),
+  );
+
+  # Check whether an old file exists, if so compare current contents with it
+  my $old = '';
+
+  if (-r $out) {
+    open OLD, '<', $out or croak 'Cannot open "', $out, '" (', $!, '), abort';
+    my @lines = readline 'OLD';
+    close OLD;
+
+    $old = join ('', @lines);
+  }
+
+  # Update Makefile if changed
+  if ($old ne $makefile) {
+    open OUT, '>', $out or croak 'Cannot open "', $out, '" (', $!, '), abort';
+    print OUT $makefile;
+    close OUT or croak 'Cannot close "', $out, '" (', $!, '), abort';
+
+    print 'Updated Makefile: ', $out, "\n" if $self->config->verbose;
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_invoke_make (
+#     TARGETS => \@targets,
+#     JOBS    => $jobs,
+#     ARCHIVE => $archive,
+#   );
+#
+# DESCRIPTION
+#   This internal method invokes the "make" command to make the build.
+#
+# ARGUMENTS
+#   TARGETS - Specify targets to be built. If set, these targets will be built
+#             instead of the ones specified in the build configuration file.
+#   JOBS    - Specify number of jobs that can be handled by "make". If set,
+#             the value must be a natural integer. If not set, the default
+#             value is 1 (i.e. run "make" in serial mode).
+#   ARCHIVE - If set to "true", invoke the "archive" mode. Most build files and
+#             directories created by this build will be archived using the
+#             "tar" command. If not set, the default is not to invoke the
+#             "archive" mode.
+# ------------------------------------------------------------------------------
+
+sub _invoke_make {
+  my $self = shift;
+  my %args = @_;
+
+  # Build the make command from the specified targets
+  my @targets  = exists $args{TARGETS} ? @{ $args{TARGETS} } : qw/all/;
+  my $jobs     = exists $args{JOBS}    ? $args{JOBS}         : 1;
+  my $archive  = exists $args{ARCHIVE} ? $args{ARCHIVE}      : 0;
+  my $verbose  = $self->config->verbose;
+
+  # Create the required build directories
+  for my $dir (qw/BIN DONE ETC INC FLAGS LIB OBJ TMP/) {
+    $self->_create_build_dir ($dir);
+  }  
+
+  my @commands = ();
+  my @make_cmd = ($self->config->setting (qw/TOOL MAKE/));
+  push @make_cmd, split (/\s+/, $self->config->setting (qw/TOOL MAKEFLAGS/));
+  push @make_cmd, $self->config->setting (qw/TOOL MAKE_SILENT/)
+    unless $verbose > 2;
+
+  if ($jobs > 1) { # multi-process "make"
+    my $make_job = $self->config->setting (qw/TOOL MAKE_JOB/);
+
+    # Setup the "make" commands for each target
+    while (my $target = shift @targets) {
+      if ($target eq 'clean') { # Do not run "clean" in parallel
+        push @commands, [@make_cmd, $target];
+
+      } else {
+        push @commands, [@make_cmd, $make_job, $jobs, $target];
+      }
+    }
+
+  } else { # single process "make"
+
+    # Setup the "make" command
+    push @commands, [@make_cmd, @targets];
+
+  }
+  
+  # Run the make command
+  my $rc  = 0;
+  my $cwd = cwd;
+  print 'cd ', $self->{DIR}{BLD}, "\n" if $verbose > 2;
+  chdir $self->{DIR}{BLD};
+  while (my $cmd = shift @commands) {
+    $| = 1; # flush STDOUT before running "make"
+    print timestamp_command (&get_command_string ($cmd)) if $verbose > 2;
+    $| = 0;
+    &run_command ($cmd, ERROR => 'warn', RC => \$rc);
+    print timestamp_command (&get_command_string ($cmd), 'End') if $verbose > 2;
+    last if $rc;
+  }
+  print 'cd ', $cwd, "\n" if $verbose > 2;
+  chdir $cwd;
+
+  return $rc ? undef : 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->_remove_empty_dirs ();
+#
+# DESCRIPTION
+#   This internal method removes empty build directories.
+# ------------------------------------------------------------------------------
+
+sub _remove_empty_dirs {
+  my $self = shift;
+
+  for my $name (qw/BIN CACHE DONE ETC FLAGS INC LIB PPSRC OBJ TMP/) {
+    opendir DIR, $self->{DIR}{$name};
+    my @files = readdir DIR;
+    @files    = grep !/^\.\.?$/, @files;
+    closedir DIR;
+
+    if (not @files) {
+      print 'Remove directory: ', $self->{DIR}{$name}, "\n"
+        if $self->config->verbose > 1;
+      rmdir $self->{DIR}{$name};
+    }
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->_tar_build_dirs ();
+#
+# DESCRIPTION
+#   This internal method creates TAR archives for selected build directories.
+# ------------------------------------------------------------------------------
+
+sub _tar_build_dirs {
+  my $self = shift;
+
+  # Create TAR archives if necessary
+  my $cwd = cwd;
+
+  my $tar      = $self->config->setting (qw/OUTFILE_EXT TAR/);
+  my @tar_dirs = split /,/, $self->config->setting (qw/TAR_DIRS/);
+  my $verbose  = $self->config->verbose;
+
+  for my $name (@tar_dirs) {
+    my $dir = $self->{DIR}{$name};
+
+    if (-d $dir) {
+      my $base = basename ($dir);
+      print 'cd ', dirname ($dir), "\n" if $verbose > 2;
+      chdir dirname ($dir);
+
+      my $rc = &run_command (
+        [qw/tar -c -f/, $base . $tar, $base],
+        PRINT => $verbose > 1, ERROR => 'warn',
+      );
+
+      &run_command ([qw/rm -rf/, $base], PRINT => $verbose > 1) if not $rc;
+    }
+  }
+
+  print 'cd ', $cwd, "\n" if $verbose > 2;
+  chdir $cwd;
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $self->_create_runenv_script ();
+#
+# DESCRIPTION
+#   This internal method creates the runtime environment script if necessary.
+# ------------------------------------------------------------------------------
+
+sub _create_runenv_script {
+  my $self = shift;
+
+  # More diagnostic on how to use the build
+  my @bin_dirs = grep {-d} @{ $self->{PATH}{BIN} };
+  my $etc_dir  = -d $self->{DIR}{ETC} ? $self->{DIR}{ETC} : undef;
+
+  if (@bin_dirs or $etc_dir) {
+    # Create a runtime environment script if necessary
+    my $run_env_sh_base = $self->config->setting (qw/MISC RUN_ENV_SH/);
+    my $run_env_sh      = catfile $self->{DIR}{ROOT}, $run_env_sh_base;
+
+    open FILE, '>', $run_env_sh
+      or croak $run_env_sh, ': cannot open (', $!, '), abort';
+    print FILE '#!/usr/bin/ksh', "\n";
+    print FILE 'export PATH=', join (':', @bin_dirs), ':$PATH', "\n"
+      if @bin_dirs;
+    print FILE 'export FCM_ETCDIR=', $self->{DIR}{ETC}, "\n" if $etc_dir;
+    close FILE or croak $run_env_sh, ': cannot close (', $!, '), abort';
+
+    # Create symbolic link in bin/ sub-directory for backward compatibility
+    if (-d $self->{DIR}{BIN}) {
+      my $file = catfile ($self->{DIR}{BIN}, $run_env_sh_base);
+      
+      # Remove old link if necessary
+      unlink $file if -l $file and readlink ($file) ne $run_env_sh;
+
+      # Create the new link
+      symlink $run_env_sh, $file if not -l $file;
+    }
+
+    # Information on the location/usage of the runtime environment script
+    if ($self->config->verbose > 1 and $run_env_sh) {
+      print '# ', '-' x 78, "\n";
+      print '# To use this build, source the following shell script:', "\n";
+      print '. ', $run_env_sh, "\n";
+      print '# ', '-' x 78, "\n";
+    }
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_create_lib_excl_dep ();
+#
+# DESCRIPTION
+#   This internal method creates a set of exclude dependency configurations for
+#   libraries of this build.
+# ------------------------------------------------------------------------------
+
+sub _create_lib_excl_dep {
+  my $self = shift;
+
+  if (-d $self->{DIR}{LIB}) {
+    $self->_create_build_dir ('ETC');
+    
+    my %allpcks  = $self->allpcks;
+    my $cfgext   = $self->config->setting (qw/OUTFILE_EXT CFG/);
+    my %cfglabel = %{ $self->config->setting ('CFG_LABEL') };
+
+    for my $key (reverse sort keys %allpcks) {
+      my $outcfg = Fcm::CfgFile->new (CONFIG => $self->config);
+
+      # Include configurations from sub-packages
+      for my $subpck (@{ $allpcks{$key} }) {
+        my $base = 'lib' . $subpck . $cfgext;
+        ($base = $self->{LIB}{$subpck}) =~ s/\.\w+$/$cfgext/
+          if exists ($self->{LIB}{$subpck});
+        my $file = catfile ('$HERE', $base);
+
+        $outcfg->add_line (LABEL => $cfglabel{INC}, VALUE => $file)
+          if -r catfile ($self->{DIR}{ETC}, $base);;
+      }
+
+      # Exclude dependency for source files in current package
+      if (exists $self->{PACKAGE}{$key}) {
+        my @srcfiles = $self->{PACKAGE}{$key}->srcfile;
+
+        for my $srcfile (@srcfiles) {
+          if ($srcfile->is_type ('INCLUDE')) {
+            if ($srcfile->is_type ('CPP')) {
+              $outcfg->add_line (
+                LABEL => $cfglabel{EXCL_DEP},
+                VALUE => 'H::' . $srcfile->base,
+              );
+
+            } elsif ($srcfile->is_type ('INTERFACE')) {
+              $outcfg->add_line (
+                LABEL => $cfglabel{EXCL_DEP},
+                VALUE => 'INTERFACE::' . $srcfile->base,
+              );
+
+            } else {
+              $outcfg->add_line (
+                LABEL => $cfglabel{EXCL_DEP},
+                VALUE => 'INC::' . $srcfile->base,
+              );
+            }
+
+          } elsif ($srcfile->is_type ('SOURCE')) {
+            next if $srcfile->is_type_or (qw/PROGRAM BLOCKDATA/);
+
+            if ($srcfile->is_type ('FORTRAN')) {
+              if ($srcfile->is_type (qw/FORTRAN MODULE/)) {
+                $outcfg->add_line (
+                  LABEL => $cfglabel{EXCL_DEP},
+                  VALUE => 'USE::' . $srcfile->root,
+                );
+
+              } else {
+                $outcfg->add_line (
+                  LABEL => $cfglabel{EXCL_DEP},
+                  VALUE => 'INTERFACE::' . $srcfile->interfacebase,
+                ) if $srcfile->interfacebase;
+
+                $outcfg->add_line (
+                  LABEL => $cfglabel{EXCL_DEP},
+                  VALUE => 'OBJ::' . $srcfile->root,
+                );
+              }
+
+            } else {
+              $outcfg->add_line (
+                LABEL => $cfglabel{EXCL_DEP},
+                VALUE => 'OBJ::' . $srcfile->root,
+              );
+            }
+          }
+        }
+      }
+
+      # Name of configuration file, follows the name of library
+      my $outbase ='lib' . $key . $cfgext;
+      ($outbase = $self->{LIB}{$key}) =~ s/\.\w+$/$cfgext/
+        if exists ($self->{LIB}{$key});
+      my $outfile = catfile ($self->{DIR}{ETC}, $outbase);
+
+      # Write to configuration file
+      $outcfg->print_cfg ($outfile);
+    }
+
+    # Information on the location/usage of the exclude dependency configurations
+    if ($self->config->verbose > 1) {
+      my $etcdir = $self->{DIR}{ETC};
+      print '# ', '-' x 78, "\n";
+      print <<EOF;
+# To use a library archive of this build in another FCM build, you need to
+# include in the new build configuration the corresponding configuration file
+# that has the relevant exclude dependency information. These configurations
+# files can be found in $etcdir.
+EOF
+      print '# ', '-' x 78, "\n";
+    }
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_create_build_dir ();
+#   $self->_create_build_dir ($label);
+#
+# DESCRIPTION
+#   This internal method creates a build directory. If $label is specified,
+#   the method will attempt to create a named sub-directory according to
+#   $label. Otherwise, the method attempts to create the build root
+#   directory. Returns the name of the directory if it is created successfully
+#   or if it already exists.
+# ------------------------------------------------------------------------------
+
+sub _create_build_dir {
+  my $self  = shift;
+  my $label = $_[0] ? uc $_[0] : 'ROOT';
+
+  my $dir = undef;
+
+  # Make sure the variable is set
+  if ($self->{DIR}{$label}) {
+    $dir = $self->{DIR}{$label};
+
+    # Expand relative path if necessary
+    $dir = catfile $self->{DIR}{ROOT}, $dir if $dir =~ /^\w/;
+
+  } else {
+    if ($label eq 'ROOT') {
+      w_report 'Error: build root directory not set.';
+      return;
+
+    } elsif ($self->config->setting ('DIR', $label)) {
+      $dir = catfile $self->{DIR}{ROOT}, $self->config->setting ('DIR', $label);
+
+    } else {
+      carp 'Directory label "', $label, '" not recognised';
+      return undef;
+    }
+  }
+
+  # Set up the bld directory, if required
+  if (not -d $dir) {
+    print 'Make directory: ', $dir, "\n" if $self->config->verbose > 1;
+    mkpath $dir or croak 'Cannot create directory "', $dir, '"';
+  }
+
+  $self->{DIR}{$label} = $dir unless $self->{DIR}{$label};
+
+  return $dir;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_get_inherited_paths ($name);
+#
+# DESCRIPTION
+#   This recursive internal method returns a list containing the search path
+#   for a build directory named by the internal label $name. (Please note that
+#   a build directory will only be placed into the search path if the
+#   directory exists.)
+# ------------------------------------------------------------------------------
+
+sub _get_inherited_paths {
+  my $self = shift;
+  my $name = shift;
+
+  return () unless $name and exists $self->{DIR}{$name};
+
+  my @path = ();
+
+  # Recursively inherit the search path for a this type of build directory
+  for my $use (@{ $self->{USE} }) {
+    my @cur_path = $use->_get_inherited_paths ($name);
+    unshift @path, @cur_path;
+  }
+
+  # Place the path of the current build in the front
+  unshift @path, $self->{DIR}{$name};
+
+  return @path;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_get_inherited_srcdirs ($name);
+#
+# DESCRIPTION
+#   This recursive internal method returns a list containing the search path
+#   for a source directory named by the internal package label $name. (Please
+#   note that a source directory will only be placed into the search path if
+#   the directory exists.)
+# ------------------------------------------------------------------------------
+
+sub _get_inherited_srcdirs {
+  my $self = shift;
+  my $name = shift;
+
+  return () unless $name;
+
+  my @path = ();
+
+  # Recursively inherit the search path for this source directory
+  my $key = 'SRCDIR__' . $name;
+  if ($self->_inherit_ok ($key)) {
+    for my $use (@{ $self->{USE} }) {
+      my @cur_path = $use->_get_inherited_srcdirs ($name);
+      unshift @path, @cur_path;
+    }
+  }
+
+  # Place the path of the current source in the front
+  unshift @path, $self->{SRCDIR}{$name}
+    if exists $self->{SRCDIR}{$name} and -d $self->{SRCDIR}{$name};
+
+  return @path;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_get_inherited_items ($type);
+#
+# DESCRIPTION
+#   This recursive internal method returns a list containing an inherited
+#   build item of the type $type. (Depending of $type, the returned list can
+#   be an array or a hash.)
+# ------------------------------------------------------------------------------
+
+sub _get_inherited_items {
+  my $self = shift;
+  my $type = shift;
+
+  return () if not exists $self->{$type};
+
+  if (ref $self->{$type} eq 'ARRAY') {
+
+    my @items = ();
+
+    # Recursively inherit from used builds
+    if ($self->{INHERIT}{$type}) {
+      for my $use (@{ $self->{USE} }) {
+        my @cur_items = $use->_get_inherited_items ($type);
+
+        for my $item (@cur_items) {
+          my $type_item = $type . '__' . $item;
+
+          # Check inheritance option of current item
+          next unless $self->_inherit_ok ($type_item);
+
+          # The statement ensures that there is no duplication
+          push @items, $item unless grep {$_ eq $item} @items;
+        }
+      }
+    }
+
+    # Items in current build
+    if (@{ $self->{$type} }) {
+      for my $item (@{ $self->{$type} }) {
+        # The statement ensures that there is no duplication
+        push @items, $item unless grep {$_ eq $item} @items;
+      }
+    }
+
+    return @items;
+
+  } elsif (ref $self->{$type} eq 'HASH') {
+
+    my %items = ();
+
+    # Recursively inherit from used builds
+    if ($self->{INHERIT}{$type}) {
+      for my $use (@{ $self->{USE} }) {
+        my %cur_items = $use->_get_inherited_items ($type);
+
+        for my $name (keys %cur_items) {
+          my $type_name = $type . '__' . $name;
+
+          # Check inheritance option of current item
+          next unless $self->_inherit_ok ($type_name);
+
+          # "Closer" ancestors overrides more "distant" ones
+          $items{$name} = $cur_items{$name};
+        }
+      }
+    }
+
+    # Items in current build
+    if (%{ $self->{$type} }) {
+      for my $name (keys %{ $self->{$type} }) {
+        # Settings in current build override inherited settings
+        $items{$name} = $self->{$type}{$name};
+      }
+    }
+
+    return %items;
+
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_require_pp ($name);
+#
+# DESCRIPTION
+#   This internal method returns true if source package $name requires
+#   pre-processing.
+# ------------------------------------------------------------------------------
+
+sub _require_pp {
+  my $self = shift;
+  my $name = $_[0];
+
+  my $rc    = 0;
+  my @names = 'PP';
+  push @names, (split /__/, $name);
+
+  # Check whether pre-process flag exists, going up the source package hierarchy
+  do {
+    my $cur_name = join '__', @names;
+    if (exists $self->{PP}{$cur_name}) {
+      $rc = $self->{PP}{$cur_name};
+      return $rc;
+    }
+  } while pop @names;
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_inherit_ok ($name);
+#
+# DESCRIPTION
+#   This internal method returns true if it is OK to inherit an item specified
+#   by $name, (where $name is a double underscore "__" delimited positional
+#   list of source package names).
+# ------------------------------------------------------------------------------
+
+sub _inherit_ok {
+  my $self  = shift;
+  my $name  = $_[0];
+
+  my $rc    = 1;
+  my @names = split /__/, $name;
+
+  # Check whether INHERIT flag exists, going up the source package hierarchy
+  do {
+    my $cur_name = join '__', @names;
+    if (exists $self->{INHERIT}{$cur_name}) {
+      $rc = $self->{INHERIT}{$cur_name};
+      return $rc;
+    }
+  } while pop @names;
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/BuildTask.pm
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/BuildTask.pm	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/BuildTask.pm	(revision 5058)
@@ -0,0 +1,406 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::BuildTask
+#
+# DESCRIPTION
+#   This class hosts information of a build task in the FCM build system.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+package Fcm::BuildTask;
+
+# Standard pragma
+use strict;
+use warnings;
+
+# Standard modules
+use Carp;
+use File::Compare;
+use File::Basename;
+use File::Path;
+use File::Spec::Functions;
+
+# FCM component modules
+use Fcm::Util;
+use Fcm::Timer;
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $task = Fcm::BuildTask->new (
+#     CONFIG     => $config,
+#     TARGET     => $target,
+#     TARGETPATH => \@targetpath,
+#     SRCFILE    => $srcfile,
+#     DEPENDENCY => \@dependency,
+#     TASKTYPE   => $tasktype,
+#   );
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::BuildTask class.
+#
+# ARGUMENTS
+#   CONFIG     - reference to a Fcm::Config instance
+#   TARGET     - target name for this task
+#   TARGETPATH - search path for the target
+#   SRCFILE    - reference to input Fcm::SrcFile instance
+#   DEPENDENCY - list of dependencies for this target
+#   TASKTYPE   - type of task to build the target
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self  = {
+    CONFIG      => exists $args{CONFIG}     ? $args{CONFIG}     : &main::cfg,
+    TARGET      => exists $args{TARGET}     ? $args{TARGET}     : undef,
+    TARGETPATH  => exists $args{TARGETPATH} ? $args{TARGETPATH} : [],
+    SRCFILE     => exists $args{SRCFILE}    ? $args{SRCFILE}    : undef,
+    DEPENDENCY  => exists $args{DEPENDENCY} ? $args{DEPENDENCY} : [],
+    ACTIONTYPE  => exists $args{ACTIONTYPE} ? $args{ACTIONTYPE} : undef,
+
+    OUTPUT      => undef,
+    OUTPUTMTIME => undef,
+  };
+
+  bless $self, $class;
+  return $self;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $config = $task->config;
+#
+# DESCRIPTION
+#   This method returns a reference to the Fcm::Config instance.
+# ------------------------------------------------------------------------------
+
+sub config {
+  my $self = shift;
+
+  return $self->{CONFIG};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $srcfile = $task->srcfile;
+#   $task->srcfile ($srcfile);
+#
+# DESCRIPTION
+#   This method returns the reference to the input Fcm::SrcFile instance
+#   associated with this task. If an argument is specified, the reference is
+#   modified to the value given by the argument.
+# ------------------------------------------------------------------------------
+
+sub srcfile {
+  my $self = shift;
+
+  if (@_) {
+    $self->{SRCFILE} = $_[0];
+  }
+
+  return $self->{SRCFILE};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $actiontype = $task->actiontype;
+#   $task->actiontype ($actiontype);
+#
+# DESCRIPTION
+#   This method returns the action type of this task. If an argument is
+#   specified, the action type of this task is set to the value of the
+#   argument.
+# ------------------------------------------------------------------------------
+
+sub actiontype {
+  my $self = shift;
+
+  if (@_) {
+    $self->{ACTIONTYPE} = $_[0];
+  }
+
+  return $self->{ACTIONTYPE};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $output = $task->output;
+#   $task->output ($output);
+#
+# DESCRIPTION
+#   This method returns the name of the output file after the task has been
+#   performed successfully. If an argument is specified, it sets the output
+#   file (and its last modified time) to the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub output {
+  my $self = shift;
+
+  if (@_) {
+    $self->{OUTPUT}      = $_[0];
+    $self->{OUTPUTMTIME} = (stat $self->{OUTPUT}) [9];
+  }
+
+  return $self->{OUTPUT};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $time = $task->outputmtime;
+#
+# DESCRIPTION
+#   This method returns the modified time of the output file.
+# ------------------------------------------------------------------------------
+
+sub outputmtime {
+  my $self = shift;
+
+  return $self->{OUTPUTMTIME};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $task->action (TASKLIST => \%tasklist);
+#
+# DESCRIPTION
+#   This method performs the task action and sets the output accordingly. The
+#   argument TASKLIST must be a reference to a hash containing the other tasks
+#   of the build, which this task may depend on. The keys of the hash must the
+#   name of the target names of the tasks, and the values of the hash must be
+#   the references to the corresponding Fcm::BuildTask instances. The method
+#   returns true if the task has been performed to create a new version of the
+#   target.
+# ------------------------------------------------------------------------------
+
+sub action {
+  my $self     = shift;
+  my %args     = @_;
+  my $tasklist = exists $args{TASKLIST} ? $args{TASKLIST} : {};
+
+  return unless $self->actiontype;
+
+  my $uptodate     = 1;
+  my $dep_uptodate = 1;
+
+  # Check if dependencies are up to date
+  # ----------------------------------------------------------------------------
+  for my $depend (@{ $self->{DEPENDENCY} }) {
+    if (exists $tasklist->{$depend}) {
+      if (not $tasklist->{$depend}->output) {
+        # Dependency task output is not set, performs its task action
+        if ($tasklist->{$depend}->action (TASKLIST => $tasklist)) {
+          $uptodate     = 0;
+          $dep_uptodate = 0;
+        }
+      }
+
+    } elsif ($self->config->verbose > 1) {
+      w_report 'Warning: Task for "', $depend,
+               '" does not exist, may be required by ', $self->{TARGET};
+    }
+  }
+
+  # Check if the target exists in the search path
+  # ----------------------------------------------------------------------------
+  if (@{ $self->{TARGETPATH} }) {
+    my $output = find_file_in_path ($self->{TARGET}, $self->{TARGETPATH});
+    $self->output ($output) if $output;
+  }
+
+  # Target is out of date if it does not exist
+  if ($uptodate) {
+    $uptodate = 0 if not $self->output;
+  }
+
+  # Check if current target is older than its dependencies
+  # ----------------------------------------------------------------------------
+  if ($uptodate) {
+    for my $depend (@{ $self->{DEPENDENCY} }) {
+      next unless exists $tasklist->{$depend};
+
+      if ($tasklist->{$depend}->outputmtime > $self->outputmtime) {
+        $uptodate     = 0;
+        $dep_uptodate = 0;
+      }
+    }
+
+    if ($uptodate and ref $self->srcfile) {
+      $uptodate = 0 if $self->srcfile->mtime > $self->outputmtime;
+    }
+  }
+
+  if ($uptodate) {
+    # Current target and its dependencies are up to date
+    # --------------------------------------------------------------------------
+    if ($self->actiontype eq 'PP') {
+      # "done" file up to date, set name of pre-processed source file
+      # ------------------------------------------------------------------------
+      my $base     = $self->srcfile->root . lc ($self->srcfile->ext);
+      my @pck_list = $self->srcfile->get_package_list;
+      pop @pck_list;
+      my @pknames  = split '__', pop (@pck_list);
+      my @path     = map {
+        catfile ($_, @pknames);
+      } @{ $self->config->setting (qw/PATH PPSRC/) };
+      my $oldfile = find_file_in_path ($base, \@path);
+      $self->srcfile->ppsrc ($oldfile);
+    }
+
+  } else {
+    # Perform action is not up to date
+    # --------------------------------------------------------------------------
+    # (For GENINTERFACE and PP, perform action if "done" file not up to date)
+    my $new_output = @{ $self->{TARGETPATH} }
+                     ? catfile ($self->{TARGETPATH}[0], $self->{TARGET})
+                     : $self->{TARGET};
+
+    # Create destination container directory if necessary
+    my $destdir = dirname $new_output;
+
+    if (not -d $destdir) {
+      print 'Make directory: ', $destdir, "\n" if $self->config->verbose > 2;
+      mkpath $destdir;
+    }
+
+    # List of actions
+    if ($self->actiontype eq 'UPDATE') {
+      # Action is UPDATE: Update file
+      # ------------------------------------------------------------------------
+      print 'Update: ', $new_output, "\n" if $self->config->verbose > 2;
+      touch_file $new_output
+        or croak 'Unable to update "', $new_output, '", abort';
+      $self->output ($new_output);
+
+    } elsif ($self->actiontype eq 'COPY') {
+      # Action is COPY: copy file to destination if necessary
+      # ------------------------------------------------------------------------
+      my $copy_required = ($dep_uptodate and $self->output and -r $self->output)
+                          ? compare ($self->output, $self->srcfile->src)
+                          : 1;
+
+      if ($copy_required) {
+        # Set up copy command
+        &run_command (
+          ['cp', $self->srcfile->src, $destdir],
+          TIME => $self->config->verbose > 2,
+        );
+
+        $self->output ($new_output);
+
+      } else {
+        $uptodate = 1;
+      }
+
+    } elsif ($self->actiontype eq 'PP' or $self->actiontype eq 'GENINTERFACE') {
+      # Action is PP or GENINTERFACE: process file
+      # ------------------------------------------------------------------------
+      my ($newlines, $base, @path);
+
+      if ($self->actiontype eq 'PP') {
+        # Invoke the pre-processor on the source file
+        # ----------------------------------------------------------------------
+        # Get lines in the pre-processed source
+        $newlines = $self->srcfile->pre_process;
+        $base     = $self->srcfile->root . lc ($self->srcfile->ext);
+
+        # Get search path for the existing pre-processed file
+        my @pck_list = $self->srcfile->get_package_list;
+        pop @pck_list;
+        my @pknames  = split '__', pop (@pck_list);
+        @path        = map {
+          catfile ($_, @pknames);
+        } @{ $self->config->setting (qw/PATH PPSRC/) };
+
+      } else { # if ($self->actiontype eq 'GENINTERFACE')
+        # Invoke the interface generator
+        # ----------------------------------------------------------------------
+        # Get new interface lines
+        $newlines = $self->srcfile->gen_interface;
+
+        # Get search path for the existing interface file
+        $base     = $self->srcfile->interfacebase;
+        @path     = @{ $self->config->setting (qw/PATH INC/) },
+      }
+
+
+      # If pre-processed or interface file exists,
+      # compare its content with new lines to see if it has been updated
+      my $update_required = 1;
+      my $oldfile = find_file_in_path ($base, \@path);
+
+      if ($oldfile and -r $oldfile) {
+        # Read old file
+        open FILE, '<', $oldfile;
+        my @oldlines = readline 'FILE';
+        close FILE;
+
+        # Compare old contents and new contents
+        if (@oldlines eq @$newlines) {
+          $update_required = grep {
+            $oldlines[$_] ne $newlines->[$_];
+          } (0 .. $#oldlines);
+        }
+      }
+
+      if ($update_required) {
+        # Update the pre-processed source or interface file
+        # ----------------------------------------------------------------------
+        # Determine container directory of the  pre-processed or interface file
+        my $newfile = @path ? catfile ($path[0], $base) : $base;
+
+        # Create the container directory if necessary
+        if (not -d $path[0]) {
+          print 'Make directory: ', $path[0], "\n"
+            if $self->config->verbose > 1;
+          mkpath $path[0];
+        }
+
+        # Update the pre-processor or interface file
+        open FILE, '>', $newfile
+          or croak 'Cannot write to "', $newfile, '" (', $!, '), abort';
+        print FILE @$newlines;
+        close FILE
+          or croak 'Cannot write to "', $newfile, '" (', $!, '), abort';
+        print 'Generated: ', $newfile, "\n" if $self->config->verbose > 1;
+
+        # Set the name of the pre-processed file
+        $self->srcfile->ppsrc ($newfile) if $self->actiontype eq 'PP';
+
+        # Set the "current" flag of the container source package to "true"
+        $self->srcfile->srcpackage->current (1);
+
+      } else {
+        # Content in pre-processed source or interface file is up to date
+        # ----------------------------------------------------------------------
+        $uptodate = 1;
+
+        # Set the name of the pre-processed file
+        $self->srcfile->ppsrc ($oldfile) if $self->actiontype eq 'PP';
+      }
+
+      # Update the "done" file
+      print 'Update: ', $new_output, "\n" if $self->config->verbose > 2;
+      touch_file $new_output
+        or croak 'Unable to update "', $new_output, '", abort';
+      $self->output ($new_output);
+
+    } else {
+      carp 'Action type "', $self->actiontype, "' not supported";
+    }
+  }
+
+  return not $uptodate;
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/CfgFile.pm
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/CfgFile.pm	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/CfgFile.pm	(revision 5058)
@@ -0,0 +1,862 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::CfgFile
+#
+# DESCRIPTION
+#   This class is used for reading and writing FCM config files. A FCM config
+#   file is a line-based text file that provides information on how to perform
+#   a particular task using the FCM system.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+package Fcm::CfgFile;
+
+# Standard pragma
+use warnings;
+no warnings 'uninitialized';
+use strict;
+
+# Standard modules
+use Carp;
+use File::Basename;
+use File::Path;
+use File::Spec;
+use File::Spec::Functions;
+
+# FCM component modules
+use Fcm::Util;
+
+# Local module variables
+my $expand_type   = 'bld|ext'; # config file type that needs variable expansions
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $cfgfile = Fcm::CfgFile->new (CONFIG=> $config, SRC => $src, TYPE => $type);
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::CfgFile class.
+#
+# ARGUMENTS
+#   CONFIG - reference to a Fcm::Config instance
+#   SRC    - configuration file source
+#   TYPE   - type of expected configuration file
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self = {
+    CONFIG     => exists $args{CONFIG} ? $args{CONFIG} : &main::cfg,
+    SRC        => exists $args{SRC}    ? $args{SRC}    : undef,
+    TYPE       => exists $args{TYPE}   ? $args{TYPE}   : undef,
+
+    # Version of the configuration file
+    VERSION    => undef,
+
+    # List of references to hash tables for each line in the file
+    LINES      => [],
+
+    # Actual source of configuration file
+    ACTUAL_SRC => undef,
+    PEGREV     => undef,
+  };
+  bless $self, $class;
+  return $self;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $config = $cfgfile->config;
+#
+# DESCRIPTION
+#   This method returns a reference to the Fcm::Config instance.
+# ------------------------------------------------------------------------------
+
+sub config {
+  my $self = shift;
+
+  return $self->{CONFIG};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $src = $cfgfile->src ();
+#   $cfgfile->src ($src);
+#
+# DESCRIPTION
+#   This method returns the specified source of the configuration file. If an
+#   argument is specified, the source of the configuration file is modified to
+#   the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub src {
+  my $self = shift;
+
+  if (@_) {
+    $self->{SRC} = shift;
+  }
+
+  return $self->{SRC};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $src = $cfgfile->actual_src ();
+#
+# DESCRIPTION
+#   This method returns the actual source of the configuration file. If an
+#   argument is specified, the source of the configuration file is modified to
+#   the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub actual_src {
+  my $self = shift;
+
+  return $self->{ACTUAL_SRC};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rev = $cfgfile->pegrev ();
+#
+# DESCRIPTION
+#   This method returns the peg revision of the configuration file.
+# ------------------------------------------------------------------------------
+
+sub pegrev {
+  my $self = shift;
+
+  return $self->{PEGREV};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $type = $cfgfile->type ();
+#   $cfgfile->type ($type);
+#
+# DESCRIPTION
+#   This method returns the configuration file type. If an argument is
+#   specified, the type is set to the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub type {
+  my $self = shift;
+
+  if (@_) {
+    $self->{TYPE} = shift;
+  }
+
+  return $self->{TYPE};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $version = $cfgfile->version ();
+#
+# DESCRIPTION
+#   This method returns the version of the configuration file.
+# ------------------------------------------------------------------------------
+
+sub version {
+  my $self = shift;
+
+  return $self->{VERSION};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @lines = $cfgfile->lines ();
+#
+# DESCRIPTION
+#   This method returns an array containing all "lines" in the configuration
+#   file. Each "line" is a reference to a hash table with the following keys:
+#
+#   SRC     - the source of the configuration file
+#   NUMBER  - the line number in the source
+#   LABEL   - the label of the of the configuration line
+#   VALUE   - the value of the configuration line
+#   COMMENT - comment in the configuration line
+# ------------------------------------------------------------------------------
+
+sub lines {
+  my $self = shift;
+
+  return @{ $self->{LINES} };
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $line = $cfgfile->line ($line_number);
+#
+# DESCRIPTION
+#   This method return a "line" at $line_number in the configuration file. A
+#   "line" is a reference to a hash table with the following keys:
+#
+#   SRC     - the source of the configuration file
+#   NUMBER  - the line number in the source
+#   LABEL   - the label of the of the configuration line
+#   VALUE   - the value of the configuration line
+#   COMMENT - comment in the configuration line
+# ------------------------------------------------------------------------------
+
+sub line {
+  my $self     = shift;
+  my $line_num = shift;
+
+  if (exists $self->{LINES}[$line_num]) {
+    return $self->{LINES}[$line_num]; # returns a ref to a label:value pair hash
+
+  } else {
+    return undef;
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $cfgfile->add_line (
+#     LABEL   => $label,
+#     VALUE   => $value,
+#     COMMENT => $comment,
+#     SRC     => $src,
+#     NUMBER  => $line_number
+#   );
+#
+# DESCRIPTION
+#   This method adds a "line" to the configuration file. LABEL is the
+#   configuration line label. VALUE is the configuration line value. COMMENT
+#   is the comment in the line. VALUE should only be specified if LABEL is
+#   specified. COMMENT can be specified without LABEL. In such case, the whole
+#   line is a comment line. A blank line is inserted if no argument is
+#   specified. SRC can be specified to indicate the name of the source file
+#   from which this line is obtained. If not specified, the source file of the
+#   current configuration file is used. NUMBER can be specified to indicate
+#   the line number of the source file from which this line is obtained.
+# ------------------------------------------------------------------------------
+
+sub add_line {
+  my $self = shift;
+  my %args = @_;
+
+  my $line = {
+    SRC     => exists $args{SRC    } ? $args{SRC    } : $self->actual_src,
+    NUMBER  => exists $args{NUMBER } ? $args{NUMBER } : 0,
+    LABEL   => exists $args{LABEL  } ? $args{LABEL  } : '',
+    VALUE   => exists $args{VALUE  } ? $args{VALUE  } : '',
+    COMMENT => exists $args{COMMENT} ? $args{COMMENT} : '',
+  };
+
+  push @{ $self->{LINES} }, $line;
+
+  return $line;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $cfgfile->add_comment_block ($line1, [$line2, ...]);
+#
+# DESCRIPTION
+#   This method adds a comment block to the configuration file. Each argument
+#   represents a line in the comment block.
+# ------------------------------------------------------------------------------
+
+sub add_comment_block {
+  my $self = shift;
+
+  $self->add_line (COMMENT => '-' x 78,);
+
+  while (my $line = shift @_) {
+    $self->add_line (COMMENT => $line,)
+  }
+
+  $self->add_line (COMMENT => '-' x 78,);
+  $self->add_line;
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $cfgfile->add_header ();
+#
+# DESCRIPTION
+#   This method adds a header to the configuration file, with its type and
+#   type version. It returns 1 on success.
+# ------------------------------------------------------------------------------
+
+sub add_header {
+  my $self = shift;
+
+  return undef unless $self->{TYPE};
+
+  $self->{VERSION} = $self->config->setting ('CFG_VERSION', uc ($self->{TYPE}))
+    if not $self->{VERSION};
+
+  $self->add_comment_block ('File header');
+
+  $self->add_line (
+    LABEL => $self->config->setting (qw/CFG_LABEL CFGFILE TYPE/),
+    VALUE => $self->{TYPE},
+  );
+
+  $self->add_line (
+    LABEL => $self->config->setting (qw/CFG_LABEL CFGFILE VERSION/),
+    VALUE => $self->{VERSION},
+  );
+
+  $self->add_line;
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $exist = $cfg->label_exists ($label);
+#   @lines = $cfg->label_exists ($label);
+#
+# DESCRIPTION
+#   This method returns the (number of) "lines" with their LABEL matching the
+#   argument $label.
+# ------------------------------------------------------------------------------
+
+sub label_exists {
+  my $self  = shift;
+  my $label = shift;
+
+  return grep {$_->{LABEL} eq $label} @{ $self->{LINES} };
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $mtime = $cfgfile->mtime ();
+#
+# DESCRIPTION
+#   This method returns the modified time of the configuration file source.
+# ------------------------------------------------------------------------------
+
+sub mtime {
+  my $self  = shift;
+  my $mtime = undef;
+
+  if (-f $self->{SRC}) {
+    $mtime = (stat $self->{SRC})[9];
+  }
+
+  return $mtime;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $read = $cfgfile->read_cfg ();
+#
+# DESCRIPTION
+#   This method reads the current configuration file. It returns the number of
+#   lines read from the config file, or "undef" if it fails. The result is
+#   placed in the LINES array of the current instance, and can be accessed via
+#   the "lines" method.
+# ------------------------------------------------------------------------------
+
+sub read_cfg {
+  my $self = shift;
+
+  my @lines = $self->_get_cfg_lines;
+
+  # List of CFG labels
+  my %cfg_labels = %{ $self->config->setting ('CFG_LABEL') };
+
+  # List of CFG types that need INC declarations expansion
+  my %exp_inc    = ();
+  for (split (/,/, $self->config->setting ('CFG_EXP_INC'))) {
+    $exp_inc{uc ($_)} = 1;
+  }
+
+  # List of CFG labels that are reserved keywords
+  my %cfg_keywords = ();
+  for (split (/,/, $self->config->setting ('CFG_KEYWORD'))) {
+    $cfg_keywords{$cfg_labels{$_}} = 1;
+  }
+
+  # Loop each line, to separate lines into label : value pairs
+  my $cont = undef;
+  my $here = undef;
+  for my $line_num (1 .. @lines) {
+    my $line = $lines[$line_num - 1];
+    chomp $line;
+
+    my $label   = '';
+    my $value   = '';
+    my $comment = '';
+
+    # If this line is a continuation, set $start to point to the line that
+    # starts this continuation. Otherwise, set $start to undef
+    my $start   = defined ($cont) ? $self->line ($cont) : undef;
+
+    if ($line =~ /^(\s*#.*)$/) { # comment line
+      $comment = $1;
+
+    } elsif ($line =~ /\S/) {    # non-blank line
+      if (defined $cont) {
+        # Previous line has a continuation mark
+        $value = $line;
+
+        # Separate value and comment
+        if ($value =~ s/((?:\s+|^)#\s+.*)$//) {
+          $comment = $1;
+        }
+
+        # Remove leading spaces
+        $value =~ s/^\s*\\?//;
+
+        # Expand environment variables
+        $value = $self->_expand_variable ($value, 1) if $value;
+
+        # Expand internal variables
+        $value = $self->_expand_variable ($value) if $value;
+
+        # Get "line" that begins the current continuation
+        ($start->{VALUE} .= $value) =~ s/\\$//;
+
+      } else {
+        # Previous line does not have a continuation mark
+        if ($line =~ /^\s*(\S+)(?:\s+(.*))?$/) {
+          # Check line contains a valid label:value pair
+          $label = $1;
+          $value = defined ($2) ? $2 : '';
+
+          # Separate value and comment
+          if ($value =~ s/((?:\s+|^)#\s+.*)$//) {
+            $comment = $1;
+          }
+
+          # Remove trailing spaces
+          $value =~ s/\s+$//;
+
+          # Value begins with $HERE?
+          $here  = ($value =~ /\$\{?HERE\}?(?:[^A-Z_]|$)/);
+
+          # Expand environment variables
+          $value = $self->_expand_variable ($value, 1) if $value;
+
+          # Expand internal variables
+          $value = $self->_expand_variable ($value) if $value;
+        }
+      }
+
+      # Determine whether current line ends with a continuation mark
+      if ($value =~ s/\\$//) {
+        $cont = scalar ($self->lines) unless $cont;
+
+      } else {
+        $cont = undef;
+      }
+    }
+
+    if (exists $exp_inc{uc ($self->type)} and
+        uc ($start ? $start->{LABEL} : $label) eq $cfg_labels{INC} and
+        not defined $cont) {
+      # Current configuration file requires expansion of INC declarations
+      # The start/current line is an INC declaration
+      # The current line is not a continuation or is the end of the continuation
+
+      # Get lines from an "include" configuration file
+      my $src = ($start ? $start->{VALUE} : $value);
+      $src   .= '@' . $self->pegrev if $here and $self->pegrev;
+
+      if ($src) {
+        # Invoke a new instance to read the source
+        my $cfg = Fcm::CfgFile->new (
+          SRC    => expand_tilde ($src),
+          TYPE   => $self->type,
+          CONFIG => $self->config,
+        );
+
+        $cfg->read_cfg;
+
+        # Add lines to the "LINES" array in the current configuration file
+        $comment = 'INC ' . $src . ' ';
+        $self->add_line (
+          COMMENT => $comment . '# Start',
+          NUMBER  => ($start ? $start->{NUMBER} : $line_num),
+        );
+        $self->add_line (%{ $_ }) for (($cfg->lines));
+        $self->add_line (COMMENT => $comment . '# End');
+
+      } else {
+        $self->add_line (NUMBER  => $line_num);
+        w_report 'Warning: ', $self->actual_src, ': line ', $line_num,
+                 ': empty INC declaration.' if $self->config->verbose > 2;
+      }
+
+    } else {
+      # Push label:value pair into "LINES" array
+      $self->add_line (
+        LABEL   => $label,
+        VALUE   => ($label ? $value : ''),
+        COMMENT => $comment,
+        NUMBER  => $line_num,
+      );
+    }
+
+    next if defined $cont; # current line not a continuation
+
+    my $slabel = ($start ? $start->{LABEL} : $label);
+    my $svalue = ($start ? $start->{VALUE} : $value);
+    next unless $slabel;
+
+    # Check config file type and version
+    if (uc ($slabel) eq $cfg_labels{CFGFILE}{TYPE}) {
+      $self->type ($svalue);
+
+    } elsif (uc ($slabel) eq $cfg_labels{CFGFILE}{VERSION}) {
+      $self->version ($svalue);
+    }
+
+    # Set internal variable
+    $slabel =~ s/^\%//; # Remove leading "%" from label
+
+    $self->config->assign_variable (
+      LABEL => $slabel,
+      VALUE => $svalue,
+    ) unless exists $cfg_keywords{$slabel};
+  }
+
+  return $self->lines;
+
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $cfgfile->print_cfg ($file);
+#
+# DESCRIPTION
+#   This method prints the content of current configuration file. If no
+#   argument is specified, it prints output to the standard output. If $file
+#   is specified, and is a writable file name, the output is sent to the file.
+#   If the file already exists, its content is compared to the current output.
+#   Nothing will be written if the content is unchanged. Otherwise, for typed
+#   configuration files, the existing file is renamed using a prefix that
+#   contains its last modified time. The method returns 1 if there is no
+#   error.
+# ------------------------------------------------------------------------------
+
+sub print_cfg {
+  my $self = shift;
+  my $file = shift;
+
+  # Count maximum number of characters in the labels, (for pretty printing)
+  my $max_label_len = 0;
+  for my $line (@{ $self->{LINES} }) {
+    next unless $line->{LABEL};
+    my $label_len  = length $line->{LABEL};
+    $max_label_len = $label_len if $label_len > $max_label_len;
+  }
+
+  # Output string
+  my $out = '';
+
+  # Append each line of the config file to the output string
+  for my $line (@{ $self->{LINES} }) {
+    my $label   = $line->{LABEL};
+    my $value   = $line->{VALUE};
+    my $comment = $line->{COMMENT};
+
+    if ($label) {
+      # Line up label/value for pretty printing
+      $label   = $label . ' ' x ($max_label_len - length ($label));
+      $comment =~ s/^\s+/ / if $comment;
+
+      $out .= $label;
+      $out .= ' ' . $value if defined $value;
+
+    } else {
+      # Make sure comments begin with a "#"
+      $comment = '# ' . $comment if $comment and $comment !~ /^\s*($|#)/;
+      $comment =~ s/^\s*//;
+    }
+
+    $out .= $comment if $comment;
+    $out .= "\n";
+  }
+
+  if ($out) {
+
+    my $old_select = select;
+
+    # Open file if necessary
+    if ($file) {
+      # Make sure the host directory exists and is writable
+      my $dirname = dirname $file;
+      if (not -d $dirname) {
+        print 'Make directory: ', $dirname, "\n" if $self->config->verbose;
+        mkpath $dirname;
+      }
+      croak 'Cannot write to config file directory: "', $dirname, '", abort'
+        unless -d $dirname and -w $dirname;
+
+      # If config file already exists, make sure it is writable
+      if (-f $file) {
+        if (-r $file) {
+          # Read old config file to see if content has changed
+          open IN, '<', $file;
+          my $in_lines = '';
+          while (my $line = <IN>) {
+            $in_lines .= $line;
+          }
+          close IN;
+
+          # Return if content is up-to-date
+          if ($in_lines eq $out) {
+            print 'No change in ', lc ($self->{TYPE}), ' cfg: ', $file,
+                  "\n" if $self->config->verbose > 1 and $self->{TYPE};
+            return;
+          }
+        }
+
+        if (-w $file) {
+          if ($self->{TYPE}) {
+            # Existing config file writable, rename it using its time stamp
+            my $mtime = (stat $file)[9];
+            my ($sec, $min, $hour, $mday, $mon, $year) = (gmtime $mtime)[0 .. 5];
+            my $timestamp = sprintf '%4d%2.2d%2.2d_%2.2d%2.2d%2.2d_',
+                            $year + 1900, $mon + 1, $mday, $hour, $min, $sec;
+            my $oldfile   = catfile $dirname, $timestamp . basename ($file);
+            rename $file, $oldfile;
+            print 'Renamed existing ', lc ($self->{TYPE}), ' cfg: ',
+                  $oldfile, "\n" if $self->config->verbose > 1;
+          }
+        } else {
+          # Existing config file not writable, throw an error
+          croak 'Config file "', $file, '" not writable, abort';
+        }
+      }
+
+      # Open file and select file handle
+      open OUT, '>', $file
+        or croak 'Cannot write to config file "', $file, '" (', $!, '), abort';
+      select OUT;
+    }
+
+    # Print output
+    print $out;
+
+    # Close file if necessary
+    if ($file) {
+      select $old_select;
+      close OUT;
+
+      if ($self->{TYPE} and $self->config->verbose > 1) {
+        print 'Generated ', lc ($self->{TYPE}), ' cfg: ', $file, "\n";
+      } elsif ($self->config->verbose > 2) {
+        print 'Generated cfg: ', $file, "\n";
+      }
+    }
+
+  } else {
+
+    # Warn if nothing to print
+    my $warning = 'Empty configuration';
+    $warning   .= ' - nothing written to file: "' . $file . '"' if $file;
+    carp $warning if $self->{TYPE};
+
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @lines = $self->_get_cfg_lines ();
+#
+# DESCRIPTION
+#   This internal method reads from a configuration file residing in a
+#   Subversion repository or in the normal file system.
+# ------------------------------------------------------------------------------
+
+sub _get_cfg_lines {
+  my $self  = shift;
+  my @lines = ();
+
+  my $verbose = $self->config->verbose;
+
+  # Expand URL keywords if necessary
+  {
+    my $src = expand_url_keyword (URL => $self->src, CFG => $self->config);
+    $self->src ($src) if $src ne $self->src;
+  }
+
+  if (&is_url ($self->src)) {
+    # Config file resides in a SVN repository
+    # --------------------------------------------------------------------------
+    # Set URL source and version
+    my $src = $self->src;
+    my $rev = 'HEAD';
+
+    # Extract version from source if it exists
+    if ($src =~ s/@(.+)$//) {
+      $rev = $1;
+    }
+
+    # Expand revision keyword, if required
+    $rev = expand_rev_keyword (REV => $rev, URL => $src, HEAD => 1);
+
+    # Check whether URL is a config file
+    my $rc;
+    my @cmd = (qw/svn cat/, $src . '@' . $rev);
+    @lines = &run_command (
+      \@cmd, METHOD => 'qx', DEVNULL => 1, RC => \$rc, ERROR => 'ignore',
+    );
+
+    # Error in "svn cat" command
+    if ($rc) {
+      # See whether specified config file is a known type
+      my %cfgname = %{ $self->config->setting ('CFG_NAME') };
+      my $key     = uc $self->type;
+      my $file    = exists $cfgname{$key} ? $cfgname{$key} : '';
+
+      # If config file is a known type, specified URL may be a directory
+      if ($file) {
+        # Check whether a config file with a default name exists in the URL
+        my $sep  = $self->config->setting (qw/MISC DIR_SEPARATOR/);
+        my $path = $src . $sep . $file;
+        my @cmd  = (qw/svn cat/, $path . '@' . $rev);
+
+        @lines = &run_command (
+          \@cmd, METHOD => 'qx', DEVNULL => 1, RC => \$rc, ERROR => 'ignore',
+        );
+
+        # Check whether a config file with a default name exists under the "cfg"
+        # sub-directory of the URL
+        if ($rc) {
+          my $cfgdir = $self->config->setting (qw/DIR CFG/);
+          $path   = $src . $sep . $cfgdir . $sep . $file;
+          my @cmd = (qw/svn cat/, $path . '@' . $rev);
+
+          @lines  = &run_command (
+            \@cmd, METHOD => 'qx', DEVNULL => 1, RC => \$rc, ERROR => 'ignore',
+          );
+        }
+
+        $src = $path unless $rc;
+      }
+    }
+
+    if ($rc) {
+      # Error in "svn cat"
+      croak 'Unable to locate config file from "', $self->src, '", abort';
+
+    } else {
+      # Print diagnostic, if necessary
+      if ($verbose and $self->type and $self->type =~ /$expand_type/) {
+        print '# Config file (', $self->type, '): ', $src;
+        print '@', $rev if $rev;
+        print "\n";
+      }
+    }
+
+    # Record the actual source location
+    $self->{PEGREV    } = $rev;
+    $self->{ACTUAL_SRC} = $src;
+
+  } else {
+    # Config file resides in the normal file system
+    # --------------------------------------------------------------------------
+    my $src = $self->src;
+
+    if (-d $src) { # Source is a directory
+      croak 'Config file "', $src, '" is a directory, abort' if not $self->type;
+
+      # Get name of the config file by looking at the type
+      my %cfgname = %{ $self->config->setting ('CFG_NAME') };
+      my $key     = uc $self->type;
+      my $file    = exists $cfgname{$key} ? $cfgname{$key} : '';
+
+      if ($file) {
+        my $cfgdir = $self->config->setting (qw/DIR CFG/);
+
+        # Check whether a config file with a default name exists in the
+        # specified path, then check whether a config file with a default name
+        # exists under the "cfg" sub-directory of the specified path
+        if (-f catfile $self->src, $file) {
+          $src = catfile $self->src, $file;
+
+        } elsif (-f catfile ($self->src, $cfgdir, $file)) {
+          $src = catfile $self->src, $cfgdir, $file;
+
+        } else {
+          croak 'Unable to locate config file from "', $self->src, '", abort';
+        }
+
+      } else {
+        croak 'Unknown config file type "', $self->type, '", abort';
+      }
+    }
+
+    if (-r $src) {
+      open FILE, '<', $src;
+      print '# Config file (', $self->type, '): ', $src, "\n"
+        if $verbose and $self->type and $self->type =~ /$expand_type/;
+
+      @lines = readline 'FILE';
+      close FILE;
+
+    } else {
+      croak 'Unable to read config file "', $src, '", abort';
+    }
+
+    # Record the actual source location
+    $self->{ACTUAL_SRC} = $src;
+  }
+
+  return @lines;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = $self->_expand_variable ($string[, $env]);
+#
+# DESCRIPTION
+#   This internal method expands variables in $string. If $env is specified
+#   and is true, it expands environment variables. Otherwise, it expands
+#   local variables.
+# ------------------------------------------------------------------------------
+
+sub _expand_variable {
+  my ($self, $string, $env) = @_;
+
+  # Pattern for environment/local variable
+  my $pattern = $env ? '\$\{?([A-Z][A-Z0-9_]+)\}?' : '%\{?([\w:]+)\}?';
+
+  while ($string and $string =~ /$pattern/) {
+    my $var_label = $1; # variable label
+
+    # Get variable value from environment or local configuration
+    my $variable = $env
+                   ? (exists $ENV{$var_label} ? $ENV{$var_label} : undef)
+                   : $self->config->variable ($var_label);
+
+    $variable = dirname ($self->actual_src)
+      if $env and $var_label eq 'HERE' and not defined $variable;
+
+    # Substitute match with value of variable
+    if (defined $variable) {
+      $string =~ s/$pattern/$variable/;
+
+    } else {
+      w_report 'Warning: ', $self->actual_src, ': variable "',
+               ($env ? '$' : '%'), $var_label, '" not expanded.';
+      last;
+    }
+  }
+
+  return $string;
+}
+
+1;
+
+__END__
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/Cm.pm
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/Cm.pm	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/Cm.pm	(revision 5058)
@@ -0,0 +1,2243 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::Cm
+#
+# DESCRIPTION
+#   This module contains the FCM code management functionalities and wrappers
+#   to Subversion commands.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+package Fcm::Cm;
+
+# Standard pragma
+use warnings;
+use strict;
+
+# Standard modules
+our (@ISA, @EXPORT, @EXPORT_OK);
+use Getopt::Long;
+use File::Basename;
+use File::Path;
+use File::Spec;
+use File::Temp qw/tempfile/;
+use Cwd;
+
+# FCM component modules
+use Fcm::CmBranch;
+use Fcm::CmUrl;
+use Fcm::Util;
+
+sub cm_command;
+
+require Exporter;
+@ISA = qw(Exporter);
+@EXPORT = qw(
+  cm_command
+);
+
+# Function declaration:
+sub cm_add;
+sub cm_branch;
+sub cm_commit;
+sub cm_conflicts;
+sub cm_delete;
+sub cm_diff;
+sub cm_merge;
+sub cm_mkpatch;
+sub cm_svn;
+sub cm_switch;
+sub _construct_branch_url;
+
+# ------------------------------------------------------------------------------
+
+my @subcommand_url = qw/
+  blame     praise annotate ann
+  branch    br
+  cat
+  checkout  co
+  copy      cp
+  delete    del    remove   rm
+  diff      di
+  export
+  import
+  info
+  list      ls
+  lock
+  log
+  merge
+  mkdir
+  mkpatch
+  move      mv     rename   ren
+  propdel   pdel   pd
+  propedit  pedit  pe
+  propget   pget   pg
+  proplist  plist  pl
+  propset   pset   ps
+  switch    sw
+  unlock
+/; # List of subcommands that accept URL inputs
+
+my @subcommand_rev = qw/
+  blame     praise annotate ann
+  branch    br
+  cat
+  checkout  co
+  copy      cp
+  diff      di
+  export
+  info
+  list      ls
+  log
+  merge
+  mkpatch
+  move      mv     rename   ren
+  propdel   pdel   pd
+  propedit  pedit  pe
+  propget   pget   pg
+  proplist  plist  pl
+  propset   pset   ps
+  switch    sw
+  update    up
+/; # List of subcommands that accept revision inputs
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &cm_command ($function);
+#
+# DESCRIPTION
+#   This is the generic FCM code management wrapper. It calls the correct FCM
+#   code management function or a wrapper to a Subversion command based on the
+#   value of the argument $function.
+# ------------------------------------------------------------------------------
+
+sub cm_command {
+
+  my ($function) = shift @_;
+
+  # Expand URL keywords if necessary
+  if (grep {$_ eq $function} @subcommand_url) {
+    for my $arg (@ARGV) {
+      my $var = expand_url_keyword (URL => $arg);
+      $arg = $var if $arg ne $var;
+    }
+  }
+
+  # Expand revision keywords (for -r or --revision options) if necessary
+  if (grep {$_ eq $function} @subcommand_rev) {
+    my @new_argv = ();
+
+    while (defined (my $arg = shift @ARGV)) {
+      if ($arg eq '--revision') {
+        # Long --revision option, must be followed by a space before the
+        # revision argument
+        push @new_argv, $arg;
+
+      } elsif ($arg =~ s/^-r//) {
+        # Short -r option, may be followed by the revision argument with or
+        # without a space in between
+        push @new_argv, '--revision';
+        unshift @ARGV, $arg if $arg;
+
+      } else {
+        # Other option or argument
+        push @new_argv, $arg;
+        next;
+      }
+
+      # First revision number/keyword
+      my $rev1 = '';
+
+      # Get the next argument from the list
+      $arg = shift @ARGV;
+
+      if (index ($arg, '{') == 0) {
+        # A revision date argument may contain a space. Therefore, it may need
+        # the next argument(s) from the list
+        while (index ($arg, '}') == -1) {
+          my $shift = shift @ARGV;
+          last unless $shift;
+          $arg     .= ' ' . $shift;
+        }
+
+        $arg  =~ s/^(\{.+?\})//;
+        $rev1 = $1;
+
+      } else {
+        # Other revision argument
+        $arg  =~ s/^(\S+?)(?::|$)//;
+        $rev1 = $1;
+      }
+
+      # The rest of $arg is the second revision number/keyword
+      my $rev2 = $arg;
+      $rev2 =~ s/^:*//;
+
+      # A revision date argument may contain a space. Therefore, it may need
+      # the next argument(s) from the list
+      if (index ($rev2, '{') == 0) {
+        while (index ($rev2, '}') == -1) {
+          my $shift = shift @ARGV;
+          last unless $shift;
+          $rev2    .= ' ' . $shift;
+        }
+      }
+
+      # Expand revision keyword if necessary
+      if ($rev1 !~ /^(?:\d+|HEAD|BASE|COMMITTED|PREV|\{.+\})$/i or
+          $rev2 !~ /^(?:\d+|HEAD|BASE|COMMITTED|PREV|\{.+\})$/i) {
+        # Find out the associated URLs by inspecting the argument list
+        my $url1 = '';
+        my $url2 = '';
+
+        for (@new_argv, @ARGV) {
+          my $arg = Fcm::CmUrl->new (URL => $_);
+          next unless $arg->is_url;
+
+          if ($url1) {
+            $url2 = $arg->url_peg;
+            last;
+
+          } else {
+            $url1 = $arg->url_peg;
+          }
+        }
+
+        # Argument list does not contain a URL, try "svn info" on WC
+        $url1 = &get_url_of_wc () if not $url1;
+        $url2 = $url1 if not $url2;
+
+        # Expand 1st revision keyword if necessary
+        $rev1 = expand_rev_keyword (REV => $rev1, URL => $url1)
+          if $rev1 !~ /^(?:\d+|HEAD|BASE|COMMITTED|PREV|\{.+\})$/i;
+
+        # Expand 2nd revision keyword if necessary
+        $rev2 = expand_rev_keyword (REV => $rev2, URL => $url2)
+          if $rev2 and $rev2 !~ /^(?:\d+|HEAD|BASE|COMMITTED|PREV|\{.+\})$/i;
+      }
+
+      # Append revision argument to argument list
+      push @new_argv, ($rev2 ? $rev1 . ':' . $rev2 : $rev1);
+    }
+
+    @ARGV = @new_argv;
+  }
+
+  # Expand revision keywords (for peg revision TARGET@REVSION) if necessary
+  for (@ARGV) {
+    if (m#^(\w+://\S+)@(\S+)$#) {
+      my $url = $1;
+      my $rev = $2;
+
+      my $new_rev = expand_rev_keyword (URL => $url, REV => $rev, HEAD => 1);
+
+      $_ = $url . '@' . $new_rev if $new_rev ne $rev;
+    }
+  }
+
+  # List of special sub-commands recognised by FCM
+  my %subcommand = (
+    ADD       => [qw/add/],
+    BRANCH    => [qw/branch br/],
+    COMMIT    => [qw/commit ci/],
+    CONFLICTS => [qw/conflicts cf/],
+    CHECKOUT  => [qw/checkout co/],
+    DELETE    => [qw/delete del remove rm/],
+    DIFF      => [qw/diff di/],
+    MERGE     => [qw/merge/],
+    MKPATCH   => [qw/mkpatch/],
+    SWITCH    => [qw/switch sw/],
+  );
+
+  if (grep {$_ eq $function} @{ $subcommand{ADD} }) {
+    cm_add;
+
+  } elsif (grep {$_ eq $function} @{ $subcommand{BRANCH} }) {
+    cm_branch;
+
+  } elsif (grep {$_ eq $function} @{ $subcommand{CHECKOUT} }) {
+    # Check whether the last argument is a PATH.
+    # If so, check whether it is a working copy.
+    # Otherwise, check whether the current directory is a working copy.
+    # If current working direcory (or PATH) is a working copy, fail the command.
+    if (@ARGV) {
+      my $arg  = Fcm::CmUrl->new (URL => $ARGV [-1]);
+      my $path = $arg->is_url ? cwd () : $ARGV [-1];
+
+      e_report $path, ': already a working copy, abort checkout.'
+        if &is_wc ($path);
+    }
+
+    # Invoke checkout
+    cm_svn ('checkout');
+
+  } elsif (grep {$_ eq $function} @{ $subcommand{COMMIT} }) {
+    cm_commit;
+
+  } elsif (grep {$_ eq $function} @{ $subcommand{CONFLICTS} }) {
+    cm_conflicts;
+
+  } elsif (grep {$_ eq $function} @{ $subcommand{DELETE} }) {
+    cm_delete;
+
+  } elsif (grep {$_ eq $function} @{ $subcommand{DIFF} }) {
+    cm_diff;
+
+  } elsif (grep {$_ eq $function} @{ $subcommand{MERGE} }) {
+    cm_merge;
+
+  } elsif (grep {$_ eq $function} @{ $subcommand{MKPATCH} }) {
+    cm_mkpatch;
+
+  } elsif (grep {$_ eq $function} @{ $subcommand{SWITCH} }) {
+    cm_switch;
+
+  } else {
+    cm_svn ($function);
+  }
+
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &Fcm::Cm::cm_add ();
+#
+# DESCRIPTION
+#   This is a wrapper to "svn add". It adds an extra functionality to check
+#   for any files or directories reported by "svn status" as not under version
+#   control, and to prompt the user whether these files or directories should
+#   be added.
+# ------------------------------------------------------------------------------
+
+sub cm_add {
+
+  # Print usage message if requested
+  if (defined ($ARGV[0]) and grep {$_ eq $ARGV[0]} qw(--help -h)) {
+    print <<EOF;
+usage: fcm add [options] [args]
+
+Valid options:
+  -c [--check]  : Check for any files or directories reported by svn status as
+                  not under version control and add them.
+  <SVN options> : Standard options to svn add as described below ...
+
+EOF
+
+    &run_command ([qw/svn add --help/], PRINT => 1, METHOD => 'exec');
+  }
+
+  if (defined ($ARGV[0]) and grep {$_ eq $ARGV[0]} qw(--check -c)) {
+    # The --check option is specified, add any new files
+    # Execute "svn status", print lines starting with a "?"
+    my $pat    = '^\?.{4}\s*';
+    my @status = grep /$pat.*/, &run_command ([qw/svn status/], METHOD => 'qx');
+    print @status if @status;
+
+    # Get list of "?" files
+    my @files  = map {chomp; s/$pat//; $_} @status;
+    my $reply  = '';
+
+    # Execute "add" command depending on user reply
+    for my $file (@files) {
+      # Get a user reply, unless previous reply is "a" for "all"
+      $reply = &main::get_input (
+        TITLE   => 'fcm add',
+        MESSAGE => "Add file '$file'?",
+        TYPE    => 'yna',
+        DEFAULT => 'n',
+      ) unless $reply eq "a";
+
+      # Add current $file if reply is "y" for "yes" or "a" for "all"
+      &run_command ([qw/svn add/, $file]) if $reply =~ /^[ya]$/;
+    }
+
+  } else {
+    # The --check option is not specified, just call "svn add"
+    cm_svn ("add");
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &Fcm::Cm::cm_branch ();
+#
+# DESCRIPTION
+#   This is a FCM command to check information, create or delete a branch in
+#   a Subversion repository.
+# ------------------------------------------------------------------------------
+
+sub cm_branch {
+  my $usage = <<EOF;
+branch: Create, delete or display information of a branch
+usage: 1. fcm branch [--info] [OPTIONS] [TARGET]
+       2. fcm branch --delete [OPTIONS] [TARGET]
+       3. fcm branch --create [OPTIONS] [SOURCE]
+       4. fcm branch --list   [OPTIONS] [SOURCE]
+
+  1. --info or -i: Display information about a branch. This is the default
+     option if --create, --delete and --list are not specified.
+
+  2. --delete or -d: Delete a branch.
+
+  3. --create or -c: Create a new branch from SOURCE. The --name option must be
+     used to specify a short name for the new branch.
+
+  4. --list or -l: List all the branches owned by the current user in SOURCE. If
+     the --user option is specified with a list of users, list all the branches
+     owned by these users instead of the current user.
+
+  TARGET (and SOURCE) can be an URL or a Subversion working copy. Otherwise,
+  the current working directory must be a working copy. For --info and
+  --delete, the specified URL (or the URL of the working copy) must be a URL
+  under a valid branch in a standard FCM project. For --create and --list, it
+  must be a URL under a standard FCM project.
+
+Valid options with --info and --delete:
+  -v [--verbose]        : Print extra information.
+  -a [--show-all]       : Set --show-children, --show-other and --show-siblings.
+  --show-children       : Report children of the current branch.
+  --show-other          : Report custom/ reverse merges into the current branch.
+  --show-siblings       : Report merges with siblings of the current branch.
+
+Valid options with --delete and --create:
+  --non-interactive     : Do no interactive prompting. This option implies
+                          --svn-non-interactive.
+  --password arg        : Specify a password for write access to the repository.
+  --svn-non-interactive : Do no interactive prompting at commit time. This
+                          option is implied by --non-interactive.
+
+Valid options with --create and --list:
+  -r [--revision] arg   : Specify the operative revision of the SOURCE for
+                          creating the branch.
+
+Valid options with --create:
+  --branch-of-branch    : If this option is specified and the SOURCE is a
+                          branch, it will create a new branch from the SOURCE
+                          branch. Otherwise, the branch is created from the
+                          trunk.
+  -k [--ticket] arg     : Specify one (or more) Trac ticket. If specified, the
+                          command will add to the commit log the line "Relates
+                          to ticket #<ticket>". Multiple tickets can be set by
+                          specifying this option multiple times, or by
+                          specifying the tickets in a comma-separated list.
+  -n [--name] arg       : Specify a short name for the branch, which should
+                          contain only word characters, i.e. [A-Za-z0-9_].
+  --rev-flag arg        : Specify a flag for determining the prefix of the
+                          branch name. The flag can be the the string "NORMAL",
+                          "NUMBER" or "NONE".  "NORMAL" is the default
+                          behaviour, in which the branch name will be prefixed
+                          with a Subversion revision number if the revision is
+                          not associated with a registered FCM revision
+                          keyword. If the revision is registered with a FCM
+                          revision keyword, the keyword will be used in place
+                          of the number. If "NUMBER" is specified, the branch
+                          name will always be prefixed with a Subversion
+                          revision number. If "NONE" is specified, the branch
+                          name will not be prefixed by a revision number or
+                          keyword.
+  -t [--type] arg       : Specify the type of the branch to be created. It must
+                          be one of the following:
+                            DEV::USER   - a development branch for the user
+                            DEV::SHARE  - a shared development branch
+                            DEV         - same as DEV::USER
+                            TEST::USER  - a test branch for the user
+                            TEST::SHARE - a shared test branch
+                            TEST        - same as TEST::USER
+                            PKG::USER   - a package branch for the user
+                            PKG::SHARE  - a shared package branch
+                            PKG::CONFIG - a configuration branch
+                            PKG::REL    - a release branch
+                            PKG         - same as PKG::USER
+                            CONFIG      - same as PKG::CONFIG
+                            REL         - same as PKG::REL
+                            SHARE       - same as DEV::SHARE
+                            USER        - same as DEV::USER
+                          If not specified, the default is to create a
+                          development branch for the current user, i.e.
+                          DEV::USER.
+
+Valid options with --list:
+  -u [--user] arg       : Specify a colon-separated list of users. List branches
+                          owned by these users instead of the current user.
+  -v [--verbose]        : Print Subversion URL instead of FCM URL keywords.
+EOF
+
+  # Print usage message if requested
+  if (defined ($ARGV[0]) and grep {$_ eq $ARGV[0]} qw(--help -h)) {
+    print $usage;
+    return 1;
+  }
+
+  # Process command line options
+  # ----------------------------------------------------------------------------
+  my (
+    $info,
+    $delete,
+    $create,
+    $list,
+    $branch_of_branch,
+    $name,
+    $non_interactive,
+    $password,
+    $rev,
+    $rev_flag,
+    $show_all,
+    $show_children,
+    $show_other,
+    $show_siblings,
+    $svn_non_interactive,
+    @tickets,
+    $type,
+    @userlist,
+    $verbose,
+  );
+  GetOptions (
+    'info|i'              => \$info,
+    'delete|d'            => \$delete,
+    'create|c'            => \$create,
+    'list|l'              => \$list,
+    'branch-of-branch'    => \$branch_of_branch,
+    'name|n=s'            => \$name,
+    'non-interactive'     => \$non_interactive,
+    'password=s'          => \$password,
+    'revision|r=s'        => \$rev,
+    'rev-flag=s'          => \$rev_flag,
+    'show-all|a'          => \$show_all,
+    'show-children'       => \$show_children,
+    'show-other'          => \$show_other,
+    'show-siblings'       => \$show_siblings,
+    'svn-non-interactive' => \$svn_non_interactive,
+    'ticket|k=s'          => \@tickets,
+    'type|t=s'            => \$type,
+    'user|u=s'            => \@userlist,
+    'verbose|v'           => \$verbose,
+  );
+
+  my $num_options = 0;
+  $num_options++ if defined $info;
+  $num_options++ if defined $delete;
+  $num_options++ if defined $create;
+  $num_options++ if defined $list;
+
+  # Report invalid usage
+  # ----------------------------------------------------------------------------
+  e_report $usage if $num_options > 1;
+
+  # Get URL of repository or branch
+  # ----------------------------------------------------------------------------
+  my $url;
+  if ($ARGV[0]) {
+    $url = Fcm::CmUrl->new (URL => $ARGV[0]);
+
+    if (not $url->is_url) {
+      # An argument is specified and is not a URL
+      # Assume that it is a path with a working copy
+      if (&is_wc ($ARGV[0])) {
+        $url = Fcm::CmUrl->new (URL => &get_url_of_wc ($ARGV[0]));
+
+      } else {
+        e_report $ARGV[0], ': is not a working copy, abort.';
+      }
+    }
+
+  } else {
+    # An argument is not specified
+    # Assume that the current directory is a working copy
+    if (&is_wc ()) {
+      $url = Fcm::CmUrl->new (URL => &get_url_of_wc ());
+
+    } else {
+      e_report 'The current directory is not a working copy, please specify a ',
+               'URL or a path to a working copy, abort.';
+    }
+  }
+
+  # Ensure $url->url_peg is a URL of a standard FCM project
+  e_report $url->url_peg, ': not a URL of a standard FCM project, abort.'
+    if not $url->project_url;
+
+  if ($create) {
+    # The --create option is specified, create a branch
+    # --------------------------------------------------------------------------
+
+    # Check branch type flags
+    if ($type) {
+      $type = uc ($type);
+
+      if ($type =~ /^(USER|SHARE)$/) {
+        $type = 'DEV::' . $1;
+
+      } elsif ($type =~ /^(CONFIG|REL)$/) {
+        $type = 'PKG::' . $1;
+
+      } elsif ($type =~ /^(DEV|TEST|PKG)$/) {
+        $type = $1 . '::USER';
+
+      } elsif ($type !~ /^(?:DEV|TEST|PKG)::(?:USER|SHARE)$/ and
+               $type !~ /^PKG::(?:CONFIG|REL)/) {
+        e_report $type, ': is not a valid type flag, abort.';
+      }
+
+    } else {
+      $type = 'DEV::USER';
+    }
+
+    # Check branch name
+    e_report 'The option --name must be used to specify a branch name, abort.'
+      if not $name;
+
+    e_report $name, ': invalid characters in name, abort.' if $name !~ /^\w+$/;
+
+    # Check revision flag is valid
+    if ($rev_flag) {
+      $rev_flag = uc ($rev_flag);
+
+      e_report $rev_flag, ': invalid argument to the --rev-flag option, abort.'
+        if $rev_flag !~ /^(?:NORMAL|NUMBER|NONE)$/;
+
+    } else {
+      $rev_flag = 'NORMAL';
+    }
+
+    # Handle multiple tickets
+    @tickets = split (/,/, join (',', @tickets));
+    s/^#// for (@tickets);
+    @tickets = sort {$a <=> $b} @tickets;
+
+    # Determine whether to create a branch of a branch
+    $url->branch ('trunk') unless $branch_of_branch;
+
+    # Create the branch
+    my $branch = Fcm::CmBranch->new;
+    $branch->create (
+      SRC                 => $url,
+      TYPE                => $type,
+      NAME                => $name,
+      PASSWORD            => $password,
+      REV_FLAG            => $rev_flag,
+      TICKET              => \@tickets,
+      REV                 => $rev,
+      NON_INTERACTIVE     => $non_interactive,
+      SVN_NON_INTERACTIVE => $svn_non_interactive,
+    );
+
+  } elsif ($list) {
+    # The option --list is specified
+    # List branches owned by current or specified users
+    # --------------------------------------------------------------------------
+    # Get URL of the project "branches/" sub-directory
+    $url->subdir ('');
+    $url->branch ('');
+    my @list = map {$_, 1} $url->branch_list ($rev);
+
+    if (@userlist) {
+      # Sort list of users
+      @userlist = sort (split /:/, join (':', @userlist));
+
+    } else {
+      # No user specified, add currrent user to list
+      push @userlist, $ENV{LOGNAME} unless @userlist;
+    }
+
+    # Filter branches matching user list
+    my @branches;
+    for my $branch (@list) {
+      next unless $branch =~ m#/([^/]+)/[^/]+/*$#;
+
+      my $user = $1;
+
+      push @branches, $branch if grep {$user eq $_} @userlist;
+    }
+
+    # Output, number of branches found
+    print scalar (@branches), ' ',
+          (scalar (@branches) > 1 ? 'branches' : 'branch'), ' found for ',
+          join (', ', @userlist), ' in ', $url->project_url_peg,
+          ($rev ? (' at r', $rev) : ()), "\n";
+
+    if (@branches) {
+      # Output the URL of each branch
+      if (not $verbose) {
+        my $project = $url->project_url;
+        my $keyword = &get_url_keyword (URL => $project);
+        @branches = map {s#^$project/+branches#$keyword-br#; $_} @branches
+          if defined $keyword;
+      }
+      @branches = map {$_ . "\n"} sort @branches;
+      print @branches;
+
+    } else {
+      # No branch found, exit with an error code
+      exit 1;
+    }
+
+  } else {
+    # The option --info or --delete is specified
+    # Report branch information (and/or delete a branch)
+    # --------------------------------------------------------------------------
+    # Set verbose level
+    &main::cfg->verbose ($verbose ? 1 : 0);
+
+    # Set up the branch, report any error
+    my $branch = Fcm::CmBranch->new (URL => $url->url_peg);
+    e_report $branch->url_peg, ': not a branch, abort.' unless $branch->branch;
+
+    e_report $branch->url_peg, ': does not exist, abort.'
+      unless $branch->url_exists;
+
+    # Remove the sub-directory part of the URL
+    $branch->subdir ('');
+
+    # Report branch info
+    $branch->display_info (
+      SHOW_CHILDREN => ($show_all || $show_children),
+      SHOW_OTHER    => ($show_all || $show_other   ),
+      SHOW_SIBLINGS => ($show_all || $show_siblings),
+    );
+
+    # Delete branch if --delete is specified
+    $branch->del (
+      PASSWORD            => $password,
+      NON_INTERACTIVE     => $non_interactive,
+      SVN_NON_INTERACTIVE => $svn_non_interactive,
+    ) if $delete;
+  }
+
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &Fcm::Cm::cm_commit ();
+#
+# DESCRIPTION
+#   This is a FCM wrapper to the "svn commit" command.
+# ------------------------------------------------------------------------------
+
+sub cm_commit {
+
+  # Print usage message if requested
+  if (defined ($ARGV[0]) and grep {$_ eq $ARGV[0]} qw(--help -h)) {
+    print <<EOF;
+commit (ci): Send changes from your working copy to the repository.
+usage: fcm commit [PATH]
+
+  Invoke your favourite editor to prompt you for a commit log message. Send
+  changes from your working copy to the repository. Update your working copy
+  following the commit.
+
+Valid options:
+  --dry-run             : Allows you to add to the commit message without
+                          committing.
+  --svn-non-interactive : Do no interactive prompting at commit time.
+  --password arg        : Specify a password ARG.
+EOF
+    return 1;
+  }
+
+  my ($dry_run, $svn_non_interactive, $password);
+  GetOptions (
+    'dry-run'             => \$dry_run,
+    'svn-non-interactive' => \$svn_non_interactive,
+    'password'            => \$password,
+  );
+
+  # The remaining argument is the path to a working copy
+  my ($path) = @ARGV;
+
+  if ($path) {
+    # Check that specified path exists
+    e_report $path, ': does not exist, abort.' if not -e $path;
+
+  } else {
+    # No argument specified, use current working directory
+    $path = cwd ();
+  }
+
+  # Make sure we are in a working copy
+  e_report $path, ': not a working copy, abort.' if not &is_wc ($path);
+
+  # Make sure we are at the top level of the working copy
+  # (otherwise we might miss any template commit message)
+  my $dir = &get_wct ($path);
+
+  if ($dir ne cwd ()) {
+    chdir $dir or die 'Cannot change directory to: ', $dir;
+    print 'Committing changes from ', $dir, ' ...', "\n";
+  }
+
+  # Get update status of working copy
+  # Check working copy files are not in conflict, missing, or out of date
+  my @status = &run_command ([qw/svn status --show-updates/], METHOD => 'qx');
+  unless (defined $dry_run) {
+    my (@conflict, @missing, @outdate);
+
+    for (@status) {
+      if (/^C/) {
+        push @conflict, $_;
+        next;
+      }
+
+      if (/^!/) {
+        push @missing, $_;
+        next;
+      }
+
+      if (/^.{7}\*/) {
+        push @outdate, $_;
+        next;
+      }
+
+      # Check that all files which have been added have the svn:executable
+      # property set correctly (in case the developer adds a script before they
+      # remember to set the execute bit)
+      next unless /^A.{7} *\d+ +(.*)/;
+      my $file = $1;
+
+      next unless -f $file;
+      my @command = (-x $file)
+                    ? (qw/svn propset -q svn:executable */, $file)
+                    : (qw/svn propdel -q svn:executable/  , $file);
+      &run_command (\@command);
+    }
+
+    # Abort commit if files are in conflict, missing, or out of date
+    if (@conflict or @missing or @outdate) {
+      w_report 'File(s) in conflict:', "\n", @conflict if @conflict;
+      w_report 'File(s) missing:'    , "\n", @missing  if @missing;
+      w_report 'File(s) out of date:', "\n", @outdate  if @outdate;
+      e_report 'Abort commit.';
+    }
+  }
+
+  # Read in any existing message
+  my $ci_mesg = Fcm::CmCommitMessage->new ();
+  $ci_mesg->read_file;
+
+  # Execute "svn status" for a list of changed items
+  @status = grep !/^\?/, &run_command ([qw/svn status/], METHOD => 'qx');
+
+  # Abort if there is no change in the working copy
+  if (not @status) {
+    print 'No change in working copy, abort.', "\n";
+    return;
+  }
+
+  # Get associated URL of current working copy
+  my $url = Fcm::CmUrl->new (URL => &get_url_of_wc ());
+
+  # Include URL, or project, branch and sub-directory info in @status
+  unshift @status, "\n";
+
+  if ($url->project and $url->branch) {
+    unshift @status, (
+      '[Project: ' . $url->project                           . ']' . "\n",
+      '[Branch : ' . $url->branch                            . ']' . "\n",
+      '[Sub-dir: ' . ($url->subdir ? $url->subdir : '<top>') . ']' . "\n",
+    );
+
+  } else {
+    unshift @status, '[URL: ' . $url->url . ']' . "\n";
+  }
+
+  # Use a temporary file to store the final commit log message
+  $ci_mesg->ignore_mesg (@status);
+  my $logfile = $ci_mesg->edit_file (TEMP => 1);
+
+  # Check with the user to see if he/she wants to go ahead
+  my $reply = 'n';
+  if (not defined $dry_run) {
+    # Add extra warning for trunk commit
+    my $mesg = $url->is_trunk
+      ? "\n" .
+        '*** WARNING: YOU ARE COMMITTING TO THE TRUNK.' . "\n" .
+        '*** Please ensure that your change conforms to your project\'s ' .
+        'working practices.' . "\n\n"
+      : '';
+    $mesg   .= 'Would you like to commit this change?';
+
+    # Prompt the user
+    $reply = &main::get_input (
+      TITLE   => 'fcm commit',
+      MESSAGE => $mesg,
+      TYPE    => 'yn',
+      DEFAULT => 'n',
+    );
+  }
+
+  if ($reply eq 'y') {
+    # Commit the change if user replies "y" for "yes"
+    my @command = (
+      qw/svn commit -F/, $logfile,
+      ($svn_non_interactive  ? '--non-interactive'       : ()),
+      (defined $password     ? ('--password', $password) : ()),
+    );
+    my $rc;
+    &run_command (\@command, RC => \$rc, ERROR => 'warn');
+
+    if ($rc) {
+      # Commit failed
+      # Write temporary commit log content to commit log message file
+      $ci_mesg->write_file;
+
+      # Fail the command
+      e_report;
+    }
+
+    # Remove commit message file
+    unlink $ci_mesg->file;
+
+    # Update the working copy
+    print 'Performing update to make sure your working copy is at this new ',
+          'revision ...', "\n";
+    &run_command ([qw/svn update/]);
+
+  } else {
+    # Abort commit if dry run or user replies "n" for "no"
+    w_report 'Commit aborted by user.' unless $dry_run;
+
+    # Write temporary commit log content to commit log message file
+    $ci_mesg->write_file;
+  }
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &Fcm::Cm::cm_conflicts ();
+#
+# DESCRIPTION
+#   This is a FCM command for resolving conflicts within working copy using a
+#   graphical merge tool.
+# ------------------------------------------------------------------------------
+
+sub cm_conflicts {
+
+  # Print usage message if requested
+  if (defined ($ARGV[0]) and grep {$_ eq $ARGV[0]} qw(--help -h)) {
+    print <<EOF;
+conflicts: Use graphical tool to resolve any conflicts within your working copy.
+usage: fcm conflicts [PATH]
+
+  Invoke the xxdiff graphical merge tool to help you resolve conflicts in your
+  working copy. It prompts you to run "svn resolved" each time you have
+  resolved the conflicts in a file.
+EOF
+    return 1;
+  }
+
+  # Path to the working copy
+  my $path = $ARGV[0];
+  $path    = cwd () if not $path;
+
+  # Check for any files with conflicts
+  my @status = grep /^C.{4} *(.*)/, &run_command (
+    [qw/svn st/, ($path eq cwd () ? () : $path)], METHOD => 'qx',
+  );
+  my @files  = map {m/^C.{4} *(.*)/; $1} @status;
+
+  # Save current working directory
+  my $topdir = cwd ();
+
+  for my $file (@files) {
+    # Print name of file in conflicts
+    print "Conflicts in file: $file\n";
+
+    # Determine directory and base name of file in conflicts
+    my $base = basename $file;
+    my $dir  = dirname $file;
+
+    # Change to container directory of file in conflicts
+    chdir File::Spec->catfile ($topdir, $dir) or die "Directory change to $dir failed";
+
+    # Use "svn info" to determine conflict marker files
+    my @info = &run_command ([qw/svn info/, $base], METHOD => 'qx');
+
+    # Ignore if $base is a binary file
+    if (-B $base) {
+      w_report $base,
+               ': ignoring binary file, please resolve conflicts manually.';
+      next;
+    }
+
+    # Get conflicts markers files
+    my ($older, $mine, $yours);
+
+    for (@info) {
+      $older = $1 if (/^Conflict Previous Base File: (.*)/);
+      $mine  = $1 if (/^Conflict Previous Working File: (.*)/);
+      $yours = $1 if (/^Conflict Current Base File: (.*)/);
+    }
+
+    if ((stat $base)[9] > (stat $mine)[9]) {
+      # If $base is newer, it may contain saved changes
+      my $reply = &main::get_input (
+        TITLE   => 'fcm conflicts',
+        MESSAGE => 'Existing changes in ' . $base . ' will be overwritten.' .
+                   "\n" . 'Do you wish to continue?',
+        TYPE    => 'yn',
+        DEFAULT => 'n',
+      );
+
+      next if $reply ne 'y';
+    }
+
+    # Launch "xxdiff" to allow user to perform graphical merging
+    my $xxdiffrc;
+    my @command  = (qw/xxdiff -m -M/, $base, qw/-O -X/, $mine, $older, $yours);
+    my ($decision) = &run_command (
+      \@command, METHOD => 'qx', RC => \$xxdiffrc, ERROR => 'ignore',
+    );
+    die &get_command_string (\@command), ' failed' if $xxdiffrc and ! $decision;
+    chomp $decision;
+
+    # Perform different actions depending on the user's decision
+    if ($decision eq "NODECISION") {
+      print "No decision taken\n";
+
+    } elsif ($decision eq "MERGED" and $xxdiffrc != 0) {
+      print "Merge conflicts were not all resolved\n";
+
+    } else {
+      # User has MERGED, ACCEPTED or REJECTED all changes
+      if ($decision eq "MERGED") {
+        print "All merge conflicts resolved\n";
+
+      } else {
+        print "You have chosen to $decision all the changes\n";
+      }
+
+      # Prompt user to run "svn resolved" on the file
+      my $reply = &main::get_input (
+        TITLE   => 'fcm conflicts',
+        MESSAGE => 'Would you like to run "svn resolved"?',
+        TYPE    => 'yn',
+        DEFAULT => 'n',
+      );
+
+      # If reply is "yes"...
+      &run_command ([qw/svn resolved/, $base]) if $reply eq 'y';
+    }
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &Fcm::Cm::cm_delete ();
+#
+# DESCRIPTION
+#   This is a wrapper to "svn delete". It adds an extra functionality to check
+#   for any files or directories reported by "svn status" as missing, and to
+#   prompt the user whether these files or directories should be deleted.
+# ------------------------------------------------------------------------------
+
+sub cm_delete {
+
+  # Print usage message if requested
+  if (defined ($ARGV[0]) and grep {$_ eq $ARGV[0]} qw(--help -h)) {
+    print <<EOF;
+usage: fcm delete [options] [args]
+
+Valid options:
+  -c [--check]  : Check for any files or directories reported by svn status as
+                  missing and delete them.
+  <SVN options> : Standard options to svn delete as described below ...
+
+EOF
+
+    &run_command ([qw/svn delete --help/], PRINT => 1, METHOD => 'exec');
+  }
+
+  if (defined ($ARGV[0]) and grep {$_ eq $ARGV[0]} qw(--check -c)) {
+    # The --check option is specified, delete any missing files
+    # Execute "svn status", print lines starting with a "!"
+    my $pat    = '^!.{4}\s*';
+    my @status = grep /$pat.*/, &run_command ([qw/svn status/], METHOD => 'qx');
+    print @status if @status;
+
+    # Get list of "!" files
+    my @files  = map {chomp; s/$pat//; $_} @status;
+    my $reply  = '';
+
+    # Execute "delete" command depending on user reply
+    for my $file (@files) {
+      # Get a user reply, unless previous reply is "a" for "all"
+      $reply = &main::get_input (
+        TITLE   => 'fcm delete',
+        MESSAGE => "Delete file '$file'?",
+        TYPE    => 'yna',
+        DEFAULT => 'n',
+      ) unless $reply eq "a";
+
+      # Delete current $file if reply is "y" for "yes" or "a" for "all"
+      &run_command ([qw/svn delete/, $file]) if $reply =~ /^[ya]$/;
+    }
+
+  } else {
+    # The --check option is not specified, just call "svn delete"
+    cm_svn ("delete");
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &Fcm::Cm::cm_diff ();
+#
+# DESCRIPTION
+#   This is a wrapper to "svn diff". It adds two extra functionalities. The
+#   first one allows the command to show differences relative to the base of
+#   the branch. The second one allows differences to be displayed via a
+#   graphical tool.
+# ------------------------------------------------------------------------------
+
+sub cm_diff {
+
+  # Print usage message if requested
+  if (defined ($ARGV[0]) and grep {$_ eq $ARGV[0]} qw(--help -h)) {
+    print <<EOF;
+usage: 1. fcm diff --branch [OPTIONS] [TARGET]
+       2. fcm diff [OPTIONS] [ARGS]
+
+  1. --branch or -b: Show differences relative to the base of the target branch,
+     i.e. the changes available for merging from the target branch into its
+     parent. If TARGET is specified, it must either be a URL or a working copy.
+     Otherwise, the target is the the current directory which must be a working
+     copy. The target URL must be a branch in a standard FCM project.
+
+  2. See description of "svn diff" below.
+
+Valid options:
+  -g [--graphical] : Use a graphical diff tool to display the differences. This
+                     option should not be used in combination with --diff-cmd.
+  <SVN options>    : Standard options to "svn diff" as described below.
+
+Valid options with --branch:
+  --diff-cmd arg        : As described below in the help for "svn diff".
+  -g [--graphical]      : As described above.
+  -t [--trac]           : If TARGET is a URL, use Trac to display the diff.
+  --wiki                : If TARGET is a URL, print Trac link for the diff.
+  -x [--extensions] arg : As described below in the help for "svn diff".
+
+EOF
+
+    &run_command ([qw/svn diff --help/], PRINT => 1, METHOD => 'exec');
+  }
+
+  # Set up environment for graphical diff
+  # Use environment variable if set, otherwise use default setting
+  my $env = 'FCM_GRAPHIC_DIFF';
+  $ENV{$env} = &main::cfg->setting (qw/TOOL GRAPHIC_DIFF/)
+    unless exists $ENV{$env} or not &main::cfg->setting (qw/TOOL GRAPHIC_DIFF/);
+
+  # Check for the --branch options
+  # ----------------------------------------------------------------------------
+  my $branch = grep {$_ eq '-b' or $_ eq '--branch'} @ARGV;
+
+  if (not $branch) {
+    # The --branch option not specified, just call "svn diff"
+    # Convert the --graphical to qw/--diff-cmd fcm_graphical_diff/
+    @ARGV = map {
+      ($_ eq '-g' or $_ eq '--graphical')
+      ? (qw/--diff-cmd fcm_graphic_diff/)
+      : $_
+    } @ARGV;
+
+    # Execute the command
+    cm_svn ('diff');
+  }
+
+  # The --branch option is specified
+  # ----------------------------------------------------------------------------
+
+  # Determine whether the --graphical option is specified,
+  # if so set the appropriate command
+  # ----------------------------------------------------------------------------
+  my ($diff_cmd, $extensions, $graphical, $trac, $wiki);
+  GetOptions (
+    'b|branch'       => \$branch,
+    'diff-cmd=s'     => \$diff_cmd,
+    'x|extensions=s' => \$extensions,
+    'g|graphical'    => \$graphical,
+    't|trac'         => \$trac,
+    'wiki'           => \$wiki,
+  );
+
+  my @diff_cmd = ();
+  
+  if ($graphical) {
+    @diff_cmd = (qw/--diff-cmd fcm_graphic_diff/);
+
+  } elsif ($diff_cmd) {
+    @diff_cmd = ('--diff-cmd', $diff_cmd);
+
+    push @diff_cmd, '--extensions', split (/\s+/, $extensions) if $extensions;
+  }
+
+  # The remaining argument should either be a URL or a PATH
+  my ($url_arg, $path_arg);
+
+  if (@ARGV) {
+    my $arg = Fcm::CmUrl->new (URL => $ARGV[0]);
+
+    if ($arg->is_url) {
+      $url_arg = $ARGV[0];
+
+    } else {
+      $path_arg = $ARGV[0];
+    }
+  }
+
+  # Get repository and branch information
+  # ----------------------------------------------------------------------------
+  my ($url, $path);
+  if (defined $url_arg) {
+    # If a URL is specified, get repository and branch information from it
+    $url = Fcm::CmBranch->new (URL => $url_arg);
+
+  } else {
+    # Get repository and branch information from the specified path or the
+    # current directory if it is a working copy
+    $path = $path_arg ? $path_arg : cwd ();
+    e_report $path, ': not a working copy, abort.' unless &is_wc ($path);
+
+    $url  = Fcm::CmBranch->new (URL => &get_url_of_wc ($path));
+  }
+
+  # Check that URL is a standard FCM branch
+  e_report $url->url_peg, ': not a standard FCM branch, abort.'
+    unless $url->is_branch;
+
+  # Save and remove sub-directory part of the URL
+  my $subdir = $url->subdir ();
+  $url->subdir ('');
+
+  # Check that $url exists
+  e_report $url->url_peg, ': not a valid URL, abort.' unless $url->url_exists;
+
+  # Compare current branch with its parent
+  # ----------------------------------------------------------------------------
+  my $parent = Fcm::CmBranch->new (URL => $url->parent->url);
+  $parent->pegrev ($url->pegrev) if $url->pegrev;
+
+  e_report $parent->url, ': branch parent no longer exists',
+           ($parent->pegrev ? ' at ' . $parent->pegrev : ''), ', abort.'
+    unless $parent->url_exists;
+
+  my $base = $parent->base_of_merge_from ($url);
+
+  # Ensure the correct diff (syntax) is displayed
+  # ----------------------------------------------------------------------------
+  # Reinstate the sub-tree part into the URL
+  $url->subdir ($subdir);
+  $base->subdir ($subdir);
+
+  # Ensure the branch URL has a peg revision
+  $url->pegrev ($url->svninfo (FLAG => 'Last Changed Rev')) if not $url->pegrev;
+
+  if ($trac or $wiki) {
+    # Trac/wiki
+    # --------------------------------------------------------------------------
+    if (not $url_arg) {
+      if (&run_command ([qw/svn status/], METHOD => 'qx')) {
+        w_report 'WARNING: the working copy at "', ($path_arg ? $path_arg : '.'),
+                 '" contains local changes, which cannot be displayed in Trac.';
+      }
+    }
+
+    # Trac wiki syntax
+    my $wiki_syntax = 'diff:' . $base->path_peg . '//' . $url->path_peg;
+
+    if ($wiki) {
+      # Print Trac wiki syntax only
+      print $wiki_syntax, "\n";
+
+    } else { # if $trac
+      # Use Trac to view "diff"
+      my $browser  = &main::cfg->setting (qw/MISC WEB_BROWSER/);
+      die 'ERROR: web browser not set, abort' if not $browser;
+
+      my $trac_url = &get_browser_url (URL => $url->project_url);
+      e_report 'ERROR: ', $url->project_url,
+               ': not associated with a Trac URL, abort.'
+        if not $trac_url;
+
+      $trac_url =~ s#/browser/.*$#/intertrac/$wiki_syntax#;
+
+      &run_command ([$browser, $trac_url], METHOD => 'exec', PRINT => 1);
+    }
+
+  } else {
+    # Execute the "diff" command
+    # --------------------------------------------------------------------------
+    my @command = (
+      qw/svn diff/, @diff_cmd,
+      '--old', $base->url_peg,
+      '--new', ($url_arg ? $url->url_peg : ($path_arg ? $path_arg : '.')),
+    );
+    &run_command (\@command, PRINT => 1);
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &Fcm::Cm::cm_merge ();
+#
+# DESCRIPTION
+#   This is a wrapper to "svn merge".
+# ------------------------------------------------------------------------------
+
+sub cm_merge {
+
+  # Print usage message if requested
+  if (defined ($ARGV[0]) and grep {$_ eq $ARGV[0]} qw(--help -h)) {
+    print <<EOF;
+merge: Merge changes from a source into your working copy.
+usage: 1. fcm merge SOURCE
+       2. fcm merge --custom  --revision N[:M] SOURCE
+          fcm merge --custom  URL[\@REV1] URL[\@REV2]
+       3. fcm merge --reverse --revision [M:]N
+
+  1. If neither --custom nor --reverse is specified, the command merges changes
+     automatically from SOURCE into your working copy. SOURCE must be a valid
+     URL[\@REV] of a branch in a standard FCM project. The base of the merge
+     will be calculated automatically based on the common ancestor and latest
+     merge information between the SOURCE and the branch of the working copy.
+
+  2. If --custom is specified, the command can be used in two forms.
+  
+     In the first form, it performs a custom merge from the specified
+     changeset(s) of SOURCE into your working copy. SOURCE must be a valid
+     URL[\@REV] of a branch in a standard FCM project. If a single revision is
+     specified, the merge delta is (N - 1):N of SOURCE. Otherwise, the merge
+     delta, is N:M of SOURCE, where N < M.
+     
+     In the second form, it performs a custom merge using the delta between the
+     two specified branch URLs. For each URL, if a peg revision is not
+     specified, the command will peg the URL with its last changed revision.
+
+  3. If --reverse is specified, the command performs a reverse merge of the
+     changeset(s) specified by the --revision option. If a single revision is
+     specified, the merge delta is N:(N - 1). Otherwise, the merge delta is
+     M:N, where M > N. Note that you do not have to specify a SOURCE for a
+     reverse merge, because the SOURCE should always be the branch your working
+     copy is pointing to.
+  
+  The command provide a commit log message template following the merge.
+
+Valid options:
+  --dry-run          : Try operation but make no changes.
+  --non-interactive  : Do no interactive prompting.
+  -r [--revision] arg: Specify a (range of) revision number(s).
+  --verbose          : Print extra information.
+EOF
+    return 1;
+  }
+
+  # Options
+  # ----------------------------------------------------------------------------
+  my ($custom, $dry_run, $non_interactive, $reverse, $rev, $verbose);
+  GetOptions (
+    'custom'          => \$custom,
+    'dry-run'         => \$dry_run,
+    'non-interactive' => \$non_interactive,
+    'reverse'         => \$reverse,
+    'revision|r=s'    => \$rev,
+    'verbose|v'       => \$verbose,
+  );
+
+  # Find out the URL of the working copy
+  # ----------------------------------------------------------------------------
+  my ($target, $wct);
+  if (&is_wc ()) {
+    $wct = &get_wct ();
+
+    if ($wct ne cwd ()) {
+      print 'Change directory to top of working copy: ', $wct, "\n";
+      chdir $wct or die 'Cannot change directory to: ', $wct;
+    }
+
+    $target = Fcm::CmBranch->new (URL => &get_url_of_wc ($wct));
+
+  } else {
+    e_report 'The current directory is not a working copy, abort.';
+  }
+
+  e_report 'Your working copy URL does not exist at the HEAD revision, abort.'
+    unless $target->url_exists;
+
+  # The target must be at the top of a branch
+  # $subdir will be used later to determine whether the merge is allowed or not
+  my $subdir = $target->subdir;
+  $target->subdir ('') if $subdir;
+
+  # Check for any local modifications
+  # ----------------------------------------------------------------------------
+  return
+    if ! ($dry_run or $non_interactive) and &_abort_modified_wc ('fcm merge');
+
+  # Determine the SOURCE URL
+  # ----------------------------------------------------------------------------
+  my $source;
+
+  if ($reverse) {
+    # Reverse merge, the SOURCE is the the working copy URL
+    $source = Fcm::CmBranch->new (URL => $target->url);
+
+  } else {
+    # Automatic/custom merge, argument 1 is the SOURCE of the merge
+    my $source_url = shift (@ARGV);
+    e_report 'Error: argument 1 must be the URL/name of a source branch in ',
+             'automatic/custom mode, abort.'
+      if not $source_url;
+
+    $source = &_construct_branch_url ($source_url, $target);
+  }
+
+  # Parse the revision option
+  # ----------------------------------------------------------------------------
+  my @revs;
+  if ($reverse or $custom) {
+    if ($reverse and not $rev) {
+      e_report 'Error: a revision (range) must be specified with ',
+               '--revision in reverse mode, abort.'
+    }
+
+    @revs = split (/:/, $rev) if $rev;
+  }
+
+  # Determine the merge delta and the commit log message
+  # ----------------------------------------------------------------------------
+  my (@delta, $mesg);
+  my $separator = '-' x 80 . "\n";
+
+  if ($reverse) {
+    # Reverse merge
+    # --------------------------------------------------------------------------
+    if (@revs == 1) {
+      $revs[1] = ($revs[0] - 1);
+
+    } else {
+      @revs = sort {$b <=> $a} @revs;
+    }
+
+    $source->pegrev ($source->svninfo (FLAG => 'Last Changed Rev'))
+      unless $source->pegrev;
+    $source->subdir ($subdir);
+
+    # "Delta" of the "svn merge" command
+    @delta = ('-r' . $revs[0] . ':' . $revs[1], $source->url_peg);
+
+    # Template message
+    $mesg = 'Reversed r' . $revs[0] .
+            (($revs[1] < $revs[0] - 1) ? ':' . $revs[1] : '') . ' of ' .
+            $source->path . "\n";
+
+  } elsif ($custom) {
+    # Custom merge
+    # --------------------------------------------------------------------------
+    if (@revs) {
+      # Revision specified
+      # ------------------------------------------------------------------------
+      # Only one revision N specified, use (N - 1):N as the delta
+      unshift @revs, ($revs[0] - 1) if @revs == 1;
+
+      $source->pegrev ($source->svninfo (FLAG => 'Last Changed Rev'))
+        unless $source->pegrev;
+      $source->subdir ($subdir);
+      $target->subdir ($subdir);
+
+      # "Delta" of the "svn merge" command
+      @delta = ('-r' . $revs[0] . ':' . $revs[1], $source->url_peg);
+
+      # Template message
+      $mesg = 'Custom merge into ' . $target->path . ': r' . $revs[1] .
+              ' cf. r' . $revs[0] . ' of ' . $source->path_peg . "\n";
+
+    } else {
+      # Revision not specified
+      # ------------------------------------------------------------------------
+      # Get second source URL
+      my $source2_url = shift (@ARGV);
+      e_report 'Error: argument 2 must be the URL/name of a source branch in ',
+               'custom mode when --revision is not specified, abort.'
+        if not $source2_url;
+
+      my $source2 = &_construct_branch_url ($source2_url, $target);
+
+      $source->pegrev  ($source->svninfo  (FLAG => 'Last Changed Rev'))
+        unless $source->pegrev;
+      $source2->pegrev ($source2->svninfo (FLAG => 'Last Changed Rev'))
+        unless $source2->pegrev;
+      $source->subdir  ($subdir);
+      $source2->subdir ($subdir);
+      $target->subdir  ($subdir);
+
+      # "Delta" of the "svn merge" command
+      @delta = ($source->url_peg, $source2->url_peg);
+
+      # Template message
+      $mesg = 'Custom merge into ' . $target->path . ': ' . $source->path_peg .
+              ' cf. ' . $source2->path_peg . "\n";
+    }
+
+  } else {
+    # Automatic merge
+    # --------------------------------------------------------------------------
+    # Check to ensure source branch is not the same as the target branch
+    e_report 'Error: cannot merge ', $source->branch,
+             ' to its own working copy, abort.'
+      if $source->branch eq $target->branch;
+
+    # Only allow the merge if the source and target are "directly related"
+    # --------------------------------------------------------------------------
+    my $anc = $target->ancestor ($source);
+    e_report 'Error: source and target are not directly related' unless
+      ($anc->url eq $target->url and $anc->url_peg eq $source->parent->url_peg)
+      or
+      ($anc->url eq $source->url and $anc->url_peg eq $target->parent->url_peg)
+      or
+      ($anc->url eq $source->parent->url and $anc->url eq $target->parent->url);
+
+    # Check for available merges from the source
+    # --------------------------------------------------------------------------
+    my @revs = $target->avail_merge_from ($source, 1);
+
+    if (@revs) {
+      print 'Available Merge', (@revs > 1 ? 's' : ''), ' From ',
+            $source->path_peg, ':';
+
+      if ($verbose) {
+        # Verbose mode, print log messages of available merges
+        print "\n";
+
+        for (@revs) {
+          print $separator, $source->display_svnlog ($_);
+        }
+
+        print $separator;
+
+      } else {
+        # Normal mode, list revisions of available merges
+        print ' ', join (' ', @revs), "\n";
+      }
+
+    } else {
+      w_report 'No merge available from ', $source->path_peg, ', abort.';
+      return;
+    }
+
+    # If more than one merge available, prompt user to enter a revision number
+    # to merge from, default to $revs [0]
+    # --------------------------------------------------------------------------
+    my $reply = ($non_interactive or @revs == 1) ? $revs[0] : &main::get_input (
+      TITLE   => 'fcm merge',
+      MESSAGE => 'Please enter the revision you wish to merge from',
+      DEFAULT => $revs [0],
+    );
+
+    if (not defined ($reply)) {
+      w_report 'Merge aborted by user.';
+      return;
+    }
+
+    # Expand revision keyword if necessary
+    if ($reply) {
+      $reply = expand_rev_keyword (REV => $reply, URL => $target->project_url);
+    }
+
+    # Check that the reply is a number in the available merges list
+    e_report $reply, ': not a revision in the list of available merges.'
+      unless (grep {$_ == $reply} @revs);
+
+    $source->pegrev ($1) if ($reply =~ /^(\d+)/);
+
+    # If the working copy top is pointing to a sub-directory of a branch,
+    # we need to check whether the merge will result in losing changes made in
+    # other sub-directories of the source.
+    if ($subdir and not $target->allow_subdir_merge_from ($source, $subdir)) {
+      e_report 'SOURCE contains changes outside the current sub-directory.', "\n",
+               'Please use a full tree for the merge, abort.';
+    }
+
+    # Calculate the base of the merge
+    my $base = $target->base_of_merge_from ($source);
+
+    # $source and $base must take into account the sub-directory
+    my $s = Fcm::CmBranch->new (URL => $source->url_peg);
+    my $b = Fcm::CmBranch->new (URL => $base->url_peg);
+
+    $s->subdir ($subdir) if $subdir;
+    $b->subdir ($subdir) if $subdir;
+
+    # Diagnostic
+    print 'About to merge in changes from ', $s->path_peg, ' compared with ',
+          $b->path_peg, "\n";
+
+    # Delta of the "svn merge" command
+    @delta = ($b->url_peg, $s->url_peg);
+
+    # Template message
+    $mesg = 'Merged into ' . $target->path . ': ' . $source->path_peg .
+            ' cf. ' . $base->path_peg . "\n";
+  }
+
+  # Run "svn merge" in "--dry-run" mode to see the result
+  # ----------------------------------------------------------------------------
+  my @out   = &run_command (
+    [qw/svn merge --dry-run/, @delta],
+    METHOD => 'qx', PRINT => ($dry_run and $verbose),
+  );
+
+  # Abort merge if it will result in no change
+  if (not @out) {
+    print 'This merge will not result in any change, abort.', "\n";
+    return;
+  }
+
+  # Report result of "svn merge --dry-run"
+  if (not $non_interactive) {
+    print 'This merge will result in the following change',
+          (@out > 1 ? 's' : ''), ':', "\n";
+    print $separator, @out, $separator;
+  }
+
+  return if $dry_run;
+
+  # Prompt the user to see if (s)he would like to go ahead
+  # ----------------------------------------------------------------------------
+  my $reply = $non_interactive ? 'y' : &main::get_input (
+    TITLE   => 'fcm merge',
+    MESSAGE => 'Would you like to go ahead with the merge?',
+    TYPE    => 'yn',
+    DEFAULT => 'n',
+  );
+
+  # Go ahead with merge only if user replies "y"
+  if ($reply eq "y") {
+    print "Performing merge ...\n";
+    &run_command ([qw/svn merge/, @delta], PRINT => $verbose);
+
+  } else {
+    w_report 'Merge aborted by user.';
+    return;
+  }
+
+  # Prepare the commit log
+  # ----------------------------------------------------------------------------
+  # Read in any existing message
+  my $ci_mesg = Fcm::CmCommitMessage->new;
+  $ci_mesg->read_file;
+  $ci_mesg->auto_mesg ($mesg, ($ci_mesg->auto_mesg));
+  $ci_mesg->write_file;
+
+  if ($verbose) {
+    print <<EOF;
+${separator}The following line has been added to your commit message file:
+$mesg
+EOF
+  }
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &Fcm::Cm::cm_mkpatch ();
+#
+# DESCRIPTION
+#   This is a FCM command to create a patching script from particular revisions
+#   of a URL.
+# ------------------------------------------------------------------------------
+
+sub cm_mkpatch {
+  my $usage = <<EOF;
+mkpatch: Create patches from specified revisions of a URL
+usage: fcm mkpatch [OPTIONS] URL [OUTDIR]
+
+  URL must be the URL of a branch in a FCM project. If the URL is a
+  sub-directory of a branch, it will use the root of the branch.
+
+  Create patches from specified revisions of the specified URL. If OUTDIR is
+  specified, the output is sent to OUTDIR. Otherwise, the output will be sent
+  to a default location in the current directory (\$PWD/fcm-mkpatch-out). The
+  output directory will contain the patch for each revision as well as a script
+  for importing the patch.
+
+  If a revision is specified with the --revision option, it will attempt to
+  create a patch based on the changes at that revision. If a revision is not
+  specified, it will attempt to create a patch based on the changes at the HEAD
+  revision. If a revision range is specified, it will attempt to create a patch
+  for each revision in that range (including the change in the lower range)
+  where changes have taken place in the URL. No output will be written if there
+  is no change in the given revision (range).
+
+  The --exclude option can be used to exclude a path in the URL. The specified
+  path must be a relative path of the URL. Glob patterns such as * and ? are
+  acceptable. Changes in an excluded path will not be considered in the patch.
+  A changeset containing changes only in the excluded path will not be
+  considered at all.
+
+  The --organisation option can be used to specify the name of your
+  organisation. The command will attempt to parse the commit log message for
+  each revision in the patch. It will remove all merge templates, replace links
+  to Trac tickets with a simple string, and add information about the original
+  changeset. If you specify the name of your organisation, it will replace Trac
+  ticket links such as "ticket:123" to "Original \$organisation ticket 123",
+  and report the orginal changeset with a message such as "Original
+  \$organisation changeset 1000". Otherwise, it will report without the
+  organisation name, e.g. "Original ticket 123" and "Original  changeset 1000".
+
+Valid options:
+  --exclude       arg : Exclude a path in the URL. Multiple paths can be
+                        specified by using a colon-separated list of paths, or
+                        by specifying this option multiple times.
+  --organisation  arg : Specify the name of your organisation.
+  -r [--revision] arg : Specify a revision number or a revision number range.
+EOF
+
+  # Print usage message if requested
+  if (defined ($ARGV[0]) and grep {$_ eq $ARGV[0]} qw(--help -h)) {
+    print $usage;
+    return 1;
+  }
+
+  # Process command line options and arguments
+  # ----------------------------------------------------------------------------
+  my (@exclude, $organisation, $revision);
+  GetOptions (
+    'exclude=s'      => \@exclude,
+    'organisation=s' => \$organisation,
+    'r|revision=s'   => \$revision,
+  );
+
+  # Excluded paths, convert glob into regular patterns
+  @exclude = split (/:/, join (':', @exclude));
+  for (@exclude) {
+    s#\*#[^/]*#; # match any number of non-slash character
+    s#\?#[^/]#;  # match a non-slash character
+    s#/*$##;     # remove trailing slash
+  }
+
+  # Organisation prefix
+  $organisation = $organisation ? $organisation : 'original';
+
+  # Make sure revision option is set correctly
+  my @revs = $revision ? split (/:/, $revision) : ();
+  @revs    = @revs [0, 1] if @revs > 2;
+
+  # Arguments
+  my ($u, $outdir) = @ARGV;
+
+  if (not $u) {
+    print $usage;
+    return 1;
+  }
+
+  my $url = Fcm::CmUrl->new (URL => $u);
+  e_report $u, ': URL is not a URL, abort.' if not $url->is_url;
+  e_report $u, ': URL does not exist, abort.' if not $url->url_exists;
+  e_report $u, ': URL is not a valid branch in a FCM project, abort.'
+    if not $url->branch;
+
+  $url->subdir ('');
+
+  if (@revs) {
+    # If HEAD revision is given, convert it into a number
+    # --------------------------------------------------------------------------
+    for my $rev (@revs) {
+      $rev = $url->svninfo (FLAG => 'Revision') if uc ($rev) eq 'HEAD';
+    }
+
+  } else {
+    # If no revision is given, use the HEAD
+    # --------------------------------------------------------------------------
+    $revs[0] = $url->svninfo (FLAG => 'Revision');
+  }
+
+  $revs[1] = $revs[0] if @revs == 1;
+
+  # Check that output directory is set
+  # ----------------------------------------------------------------------------
+  $outdir = File::Spec->catfile (cwd (), 'fcm-mkpatch-out') if not $outdir;
+
+  if (-e $outdir) {
+    # Ask user to confirm removal of old output directory if it exists
+    my $reply = &main::get_input (
+      TITLE   => 'fcm mkpatch',
+      MESSAGE => 'Output location ' . $outdir . ' exists. OK to overwrite?',
+      TYPE    => 'yn',
+      DEFAULT => 'n',
+    );
+
+    if ($reply ne 'y') {
+      w_report 'fcm mkpatch: command aborted by user.';
+      return 1;
+    }
+
+    rmtree $outdir or die $outdir, ': cannot remove';
+  }
+
+  # (Re-)create output directory
+  mkpath $outdir or die $outdir, ': cannot create';
+  print 'Output directory: ', $outdir, "\n";
+
+  # Get and process log of URL
+  # ----------------------------------------------------------------------------
+  my @script     = (); # output script, from the log
+  my %log        = $url->svnlog (REV => \@revs);
+  my $url_path   = $url->path;
+  my $file_count = 0;
+
+  for my $rev (sort {$a <=> $b} keys %log) {
+    # Look at the changed paths for each revision
+    my @paths;
+
+    # Skip excluded paths if necessary
+    PATH: for my $path (sort keys %{ $log{$rev}{paths} }) {
+      for my $exclude (@exclude) {
+        (my $file = $path) =~ s#^$url_path/*##;
+
+        next PATH if $file =~ m#^$exclude(?:/*|$)#;
+      }
+
+      push @paths, $path;
+    }
+
+    next unless @paths;
+
+    # Parse commit log message
+    my @msg = split /\n/, $log{$rev}{msg};
+    for (@msg) {
+      # Re-instate line break
+      $_ .= "\n";
+
+      # Remove line if it matches a merge template
+      $_ = '' if /^Reversed r\d+(?::\d+)? of \S+$/;
+      $_ = '' if /^Custom merge into \S+:.+$/;
+      $_ = '' if /^Merged into \S+: \S+ cf\. \S+$/;
+
+      # Modify Trac ticket link
+      s/(?:#|ticket:)(\d+)/[$organisation ticket $1]/g;
+
+      # Modify Trac changeset link
+      s/(?:r|changeset:)(\d+)/[$organisation changeset $1]/g;
+      s/\[(\d+)\]/[$organisation changeset $1]/g;
+    }
+
+    push @msg, '[' . $organisation . ' changeset ' . $rev . ']' . "\n";
+
+    # Write commit log message in a file
+    my $f_revlog = File::Spec->catfile ($outdir, $rev . '-log');
+    open FILE, '>', $f_revlog or die $f_revlog, ': cannot open (', $!, ')';
+    print FILE @msg;
+    close FILE or die $f_revlog, ': cannot close (', $!, ')';
+
+    # Create a directory for this revision in the output directory
+    my $outdir_rev = File::Spec->catfile ($outdir, $rev);
+    mkpath $outdir_rev or die $outdir_rev, ': cannot create';
+
+    # Handle modified/copy/new path, export the path, + script to copy/add it
+    for my $path (@paths) {
+      next unless $log{$rev}{paths}{$path}{action} =~ /^[AMR]$/;
+
+      (my $file = $path) =~ s#^$url_path/*##;
+
+      # Download the file using "svn export"
+      my $patch    = File::Spec->catfile ($outdir_rev, $file_count++);
+      my $url_file = $url->url . '/' . $file . '@' . $rev;
+      &run_command ([qw/svn export -q -r/, $rev, $url_file, $patch]);
+
+      (my $patch_path = $patch) =~ s#^$outdir/*##;
+
+      # Script to copy the file, if required
+      my $is_newfile = 0;
+      if ($log{$rev}{paths}{$path}{action} eq 'A') {
+        if (exists $log{$rev}{paths}{$path}{'copyfrom-path'}) {
+          # History exists for this file
+          my $copyfrom_path = $log{$rev}{paths}{$path}{'copyfrom-path'};
+          my $copyfrom_rev  = $log{$rev}{paths}{$path}{'copyfrom-rev'};
+
+          # Check whether file is copied from a file under the specified URL
+          # It is likely to be a new file if it is copied from outside of the
+          # specified URL.
+          $is_newfile = not ($copyfrom_path =~ s#^$url_path/*##);
+
+          if ($is_newfile) {
+            # File copied from outside of the specified URL
+            # If it is copied from a branch, follow its history, stop on copy
+            my $cp_url = Fcm::CmUrl->new (
+              URL => $url->root . $copyfrom_path . '@' . $copyfrom_rev,
+            );
+
+            # Log of the copied file
+            my %cp_log = $cp_url->svnlog (STOP_ON_COPY => 1);
+
+            # "First" revision of the copied file
+            my $cp_rev = (sort {$a <=> $b} keys %cp_log) [0];
+            my %attrib = exists $cp_log{$cp_rev}{paths}{$cp_url->path}
+                         ? %{ $cp_log{$cp_rev}{paths}{$cp_url->path} } : ();
+
+            # Check whether the "first" revision is copied from elsewhere.
+            if (exists $attrib{'copyfrom-path'}) {
+              # Check whether source exists in the current branch
+              my $cp_cp_url = Fcm::CmUrl->new (
+                URL => $url->root . $attrib{'copyfrom-path'} . '@' .
+                       $attrib{'copyfrom-rev'},
+              );
+
+              $cp_cp_url->branch ($url->branch);
+
+              # If source exists in current branch, set up copy from the source
+              if ($cp_cp_url->url_exists ($rev - 1)) {
+                $is_newfile     = 0;
+                (my $cp_cp_path = $cp_cp_url->path) =~ s#^$url_path/*##;
+
+                push @script, 'svn copy ' . $cp_cp_path .  ' ' . $file;
+              }
+            }
+
+          } else {
+            # File copied from a location under the specified URL
+            # Script to copy file
+            push @script, 'svn copy ' . $copyfrom_path .  ' ' . $file;
+          }
+
+        } else {
+          # History does not exist, must be a new file
+          $is_newfile = 1;
+        }
+      }
+
+      # Copy the "patch" into the file
+      push @script, 'cp -r ${fcm_patch_dir}/' . $patch_path . ' ' . $file;
+
+      # Script to add the file, if required
+      push @script, 'svn add ' . $file
+        if $log{$rev}{paths}{$path}{action} eq 'A' and $is_newfile;
+    }
+
+    # Handle deleted path, script to delete it
+    for my $path (@paths) {
+      next unless $log{$rev}{paths}{$path}{action} eq 'D';
+
+      (my $file = $path) =~ s#^$url_path/*##;
+
+      push @script, 'svn delete ' . $file;
+    }
+
+    # Script to commit the change
+    push @script, 'svn commit -F ${fcm_patch_dir}/' . $rev . '-log';
+    push @script, '';
+  }
+
+  # Write the script if necessary. Otherwise remove output directory
+  # ----------------------------------------------------------------------------
+  if (@script) {
+    # Add line break to each line in @script
+    @script = map {($_ ? $_ . ' || exit 1' . "\n" : "\n")} @script;
+
+    # Write script to output
+    my $out = File::Spec->catfile ($outdir, 'fcm-import-patch');
+    open FILE, '>', $out or die $out, ': cannot open (', $!, ')';
+
+    # Script header
+    print FILE <<EOF;
+#!/bin/sh
+# ------------------------------------------------------------------------------
+# NAME
+#   fcm-import-patch
+#
+# SYNOPSIS
+#   fcm-import-patch TARGET
+#
+# DESCRIPTION
+#   This script is generated automatically by the "fcm mkpatch" command,
+#   together with the revision "patches" it creates. The script imports the
+#   patches into TARGET, which must either be a URL or a working copy of a
+#   valid project tree that can accept the import of the patches.
+#
+#   Patch created from $organisation URL: $u
+# ------------------------------------------------------------------------------
+
+this=`basename \$0`
+
+# Check argument
+target=\$1
+
+# First argument must be a URL or working copy
+if [[ -z \$target ]]; then
+  echo "\$this: the first argument must be a URL or a working copy, abort." >&2
+  exit 1
+fi
+
+if [[ \$target == svn://*  || \$target == svn+ssh://* || \\
+      \$target == http://* || \$target == https://*   || \\
+      \$target == file://* ]]; then
+  # A URL, checkout a working copy in a temporary location
+  fcm_tmp_dir=`mktemp -d \$TMPDIR/\$0.XXXXXX`
+  fcm_working_copy=\$fcm_tmp_dir
+  svn checkout -q \$target \$fcm_working_copy || exit 1
+
+else
+  # A working copy, check that it does not have local changes
+  status=`svn status \$target`
+
+  if [[ -n \$status ]]; then
+    echo "\$target: working copy contains changes, abort." >&2
+    exit 1
+  fi
+
+  fcm_working_copy=\$target
+fi
+
+# Location of the patches, base on the location of this script
+cd `dirname \$0` || exit 1
+fcm_patch_dir=\$PWD
+
+# Change directory to the working copy
+cd \$fcm_working_copy || exit 1
+
+# Commands to apply patches
+EOF
+
+    # Script content
+    print FILE @script;
+
+    # Script footer
+    print FILE <<EOF;
+# Remove temporary working copy, if necessary
+if [[ -d \$fcm_tmp_dir && -w \$fcm_tmp_dir ]]; then
+  rm -rf \$fcm_tmp_dir
+fi
+
+echo "\$this: finished normally."
+#EOF
+EOF
+
+    close FILE or die $out, ': cannot close (', $!, ')';
+
+    # Add executable permission
+    chmod 0755, $out;
+
+    # Diagnostic
+    print $outdir, ': patch generated.', "\n";
+
+  } else {
+    # Remove output directory
+    rmtree $outdir or die $outdir, ': cannot remove';
+
+    # Diagnostic
+    w_report 'No patch is required, abort.';
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &Fcm::Cm::cm_svn ();
+#
+# DESCRIPTION
+#   This is a generic wrapper for all "other" Subversion commands.
+# ------------------------------------------------------------------------------
+
+sub cm_svn {
+  &run_command (
+    ['svn', @_, @ARGV],
+    PRINT => ($_[0] ne 'cat' and not grep {$_ eq '--xml'} @ARGV),
+    METHOD => 'exec',
+  );
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &Fcm::Cm::cm_switch ();
+#
+# DESCRIPTION
+#   This is a wrapper for the Subversion "switch" command.
+# ------------------------------------------------------------------------------
+
+sub cm_switch {
+  if (grep {$_ eq '-h' or $_ eq '--help'} @ARGV or not @ARGV) {
+    # Print usage message if requested
+    print <<EOF;
+usage: 1. switch URL [PATH]
+       2. switch --relocate FROM TO [PATH...]
+
+Note: if --relocate is not specified, "fcm switch" will only support the
+      options --non-interactive, -r [--revision] and -q [--quiet].
+
+EOF
+
+    &run_command ([qw/svn switch --help/], PRINT => 1, METHOD => 'exec');
+
+  } elsif (grep {$_ eq '--relocate'} @ARGV) {
+    # If "--relocate" is specified, call the command "as is"
+    cm_svn ('switch');
+  }
+
+  # "--help" and "--relocate" not specified, implement custom switch command
+
+  # Get command line options
+  my ($non_interactive, $rev, $quiet);
+  GetOptions (
+    'non-interactive' => \$non_interactive,
+    'revision|r=s'    => \$rev,
+    'quiet|q'         => \$quiet,
+  );
+
+  # The remaining arguments
+  $rev = 'HEAD' if not $rev;
+
+  # The remaining arguments
+  my ($newurl_arg, $path) = @ARGV;
+
+  # Make sure we are in a working copy
+  if ($path) {
+    e_report $path, ': does not exist, abort.' if not -e $path;
+
+  } else {
+    $path = cwd ();
+  }
+
+  e_report $path, ': not a working copy, abort.' if not &is_wc ($path);
+
+  # Make sure we are at the top level of the working copy
+  my $dir = &get_wct ($path);
+
+  # Check for merge template in the commit log file in the working copy
+  my $ci_mesg = Fcm::CmCommitMessage->new (DIR => $dir);
+  $ci_mesg->read_file;
+  e_report (
+    (($path eq $dir) ? $ci_mesg->base : $ci_mesg->file),
+    ': merge template exists, please remove it before running switch, abort.',
+  ) if $ci_mesg->auto_mesg;
+
+  # Check for any local modifications
+  return if ! $non_interactive and &_abort_modified_wc ('fcm switch', $dir);
+
+  # Get current URL information associated with the working copy
+  my $oldurl = Fcm::CmBranch->new (URL => &get_url_of_wc ($dir));
+
+  # Analyse new URL
+  my $newurl = &_construct_branch_url ($newurl_arg, $oldurl);
+
+  # Construct the switch command
+  my @command = (
+    qw/svn switch/,
+    ($non_interactive ? '--non-interactive' : ()),
+    ($rev             ? ('-r', $rev)        : ()),
+    ($quiet           ? '--quiet'           : ()),
+    $newurl->url,
+    ($dir eq cwd () ? () : $dir),
+  );
+
+  # Execute the command
+  &run_command (\@command, METHOD => 'exec', PRINT => 1);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $source = &_construct_branch_url ($src_url, $target);
+#
+# DESCRIPTION
+#   The function takes a string $src_url, which is normally the SOURCE URL
+#   argument for "merge" and "switch", and a target, which is an instance of a
+#   Fcm::CmBranch object with a valid URL of a standard FCM branch. It returns
+#   an instance of a Fcm::CmBranch object that represents a valid URL for
+#   $src_url.
+# ------------------------------------------------------------------------------
+
+sub _construct_branch_url {
+  my ($src_url, $target) = @_;
+
+  my $source = Fcm::CmBranch->new (URL => $src_url);
+
+  if (not $source->is_url) {
+    # Not a full URL, construct full URL based on current URL
+    $source->url_peg ($target->url_peg);
+
+    my $path    = '';
+    my $project = $target->project;
+
+    # Construct the branch URL
+    if ($src_url =~ m#^/*$project/(?:trunk|branches|tags)$#) {
+      # Argument contains the full path under the repository root
+      $path = $src_url;
+
+    } elsif ($src_url =~ m#^/*trunk/*(?:@\d+)?$# or
+             $src_url =~ m#^/*(?:trunk|branches|tags)/+#) {
+      # Argument contains the full branch name
+      $src_url =~ s#^/*##;
+      $path    = $target->project_path . '/' . $src_url;
+
+    } else {
+      # Argument contains the shorter branch name
+      $src_url =~ s#^/*##;
+      $path    = $target->project_path . '/branches/' . $src_url;
+    }
+
+    $source->path_peg ($path);
+  }
+
+  # Replace source sub-directory with the target sub-directory
+  $source->subdir ($target->subdir);
+
+  # Ensure that the branch name exists
+  e_report $src_url, ': not a valid URL, abort.'
+    if not $source->url_exists;
+
+  # Ensure that the branch name is valid
+  e_report $src_url, ': not a standard branch in a FCM project, abort.'
+    if not $source->branch;
+
+  # Ensure that the source and target URLs are in the same project
+  e_report 'Source and target URLs are in different projects, abort.'
+    if $source->project_url ne $target->project_url;
+
+  return $source;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &_abort_modified_wc ($title, [$wc]);
+#
+# DESCRIPTION
+#   The function checks for any local modifications in a working copy and
+#   prompts the user whether he/she wants to continue with the command. $title
+#   is the title of the current command. If $wc is specified, it must be the
+#   path to a working copy. Otherwise, the current working directory is used.
+# ------------------------------------------------------------------------------
+
+sub _abort_modified_wc {
+  my ($title, $wc) = @_;
+
+  my @status = &run_command ([qw/svn status/, ($wc ? $wc : ())], METHOD => 'qx');
+
+  if (@status) {
+    print 'You have local modifications:', "\n", @status;
+    my $reply = &main::get_input (
+      TITLE   => $title,
+      MESSAGE => 'Are you sure you want to continue?',
+      TYPE    => 'yn',
+      DEFAULT => 'n',
+    );
+
+    # Abort if user gives any reply other than "y"
+    if ($reply ne 'y') {
+      w_report $title, ': command aborted by user.';
+      return 1;
+    }
+  }
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/CmBranch.pm
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/CmBranch.pm	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/CmBranch.pm	(revision 5058)
@@ -0,0 +1,1199 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::CmBranch
+#
+# DESCRIPTION
+#   This class contains methods for manipulating a branch. It is a sub-class of
+#   Fcm::CmUrl, and inherits all methods from that class.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+package Fcm::CmBranch;
+@ISA = qw(Fcm::CmUrl);
+
+# Standard pragma
+use warnings;
+use strict;
+
+# Standard modules
+use Carp;
+use File::Spec;
+
+# FCM component modules
+use Fcm::CmCommitMessage;
+use Fcm::CmUrl;
+use Fcm::Util qw/run_command e_report w_report get_rev_keyword svn_date/;
+
+my @properties = (
+  'CREATE_REV',  # revision at which the branch is created
+  'DELETE_REV',  # revision at which the branch is deleted
+  'PARENT',      # reference to parent branch Fcm::CmBranch
+  'ANCESTOR',    # list of common ancestors with other branches
+                 # key = URL, value = ancestor Fcm::CmBranch
+  'LAST_MERGE',  # list of last merges from branches
+                 # key = URL@REV, value = [TARGET, UPPER, LOWER]
+  'AVAIL_MERGE', # list of available revisions for merging
+                 # key = URL@REV, value = [REV ...]
+  'CHILDREN',    # list of children of this branch
+  'SIBLINGS',    # list of siblings of this branch
+);
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $cm_branch = Fcm::CmBranch->new (
+#     CONFIG  => $config,
+#     URL     => $url,
+#   );
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::CmBranch class.
+#
+# ARGUMENTS
+#   CONFIG - reference to a Fcm::Config instance
+#   URL    - URL of a branch
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self = Fcm::CmUrl->new (%args);
+
+  $self->{$_} = undef for (@properties);
+
+  bless $self, $class;
+  return $self;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $url = $cm_branch->url_peg;
+#   $cm_branch->url_peg ($url);
+#
+# DESCRIPTION
+#   This method returns/sets the current URL.
+# ------------------------------------------------------------------------------
+
+sub url_peg {
+  my $self = shift;
+
+  if (@_) {
+    if (! $self->{URL} or $_[0] ne $self->{URL}) {
+      # Re-set URL and other essential variables in the SUPER-class
+      $self->SUPER::url_peg (@_);
+
+      # Re-set essential variables
+      $self->{$_} = undef for (@properties);
+    }
+  }
+
+  return $self->{URL};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rev = $cm_branch->create_rev;
+#
+# DESCRIPTION
+#   This method returns the revision at which the branch was created.
+# ------------------------------------------------------------------------------
+
+sub create_rev {
+  my $self = shift;
+
+  if (not $self->{CREATE_REV}) {
+    return unless $self->url_exists ($self->pegrev);
+
+    # Use "svn log" to find out the first revision of the branch
+    my %log = $self->svnlog (STOP_ON_COPY => 1);
+
+    # Look at log in ascending order
+    my $rev   = (sort {$a <=> $b} keys %log) [0];
+    my $paths = $log{$rev}{paths};
+
+    # Get revision when URL is first added to the repository
+    if (exists $paths->{$self->branch_path}) {
+      $self->{CREATE_REV} = $rev if $paths->{$self->branch_path}{action} eq 'A';
+    }
+  }
+
+  return $self->{CREATE_REV};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $parent = $cm_branch->parent;
+#
+# DESCRIPTION
+#   This method returns the parent (a Fcm::CmBranch object) of the current
+#   branch.
+# ------------------------------------------------------------------------------
+
+sub parent {
+  my $self = shift;
+
+  if (not $self->{PARENT}) {
+    # Use the log to find out the parent revision
+    my %log = $self->svnlog (REV => $self->create_rev);
+
+    if (exists $log{paths}{$self->branch_path}) {
+      my $path = $log{paths}{$self->branch_path};
+
+      if ($path->{action} eq 'A') {
+        if (exists $path->{'copyfrom-path'}) {
+          # Current branch is copied from somewhere, set the source as the parent
+          my $url = $self->root .  $path->{'copyfrom-path'};
+          my $rev = $path->{'copyfrom-rev'};
+          $self->{PARENT} = Fcm::CmBranch->new (URL => $url . '@' . $rev);
+
+        } else {
+          # Current branch is not copied from somewhere
+          $self->{PARENT} = $self;
+        }
+      }
+    }
+  }
+
+  return $self->{PARENT};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rev = $cm_branch->delete_rev;
+#
+# DESCRIPTION
+#   This method returns the revision at which the branch was deleted.
+# ------------------------------------------------------------------------------
+
+sub delete_rev {
+  my $self = shift;
+
+  if (not $self->{DELETE_REV}) {
+    return if $self->url_exists ('HEAD');
+
+    # Container of the current URL
+    (my $dir_url = $self->branch_url) =~ s#/+[^/]+/*$##;
+
+    # Use "svn log" on the container between a revision where the branch exists
+    # and the HEAD
+    my $dir = Fcm::CmUrl->new (URL => $dir_url);
+    my %log = $dir->svnlog (
+      REV => ['HEAD', ($self->pegrev ? $self->pegrev : $self->create_rev)],
+    );
+
+    # Go through the log to see when branch no longer exists
+    for my $rev (sort {$a <=> $b} keys %log) {
+      next unless exists $log{$rev}{paths}{$self->branch_path} and
+                  $log{$rev}{paths}{$self->branch_path}{action} eq 'D';
+
+      $self->{DELETE_REV} = $rev;
+      last;
+    }
+  }
+
+  return $self->{DELETE_REV};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $cm_branch->is_child_of ($branch);
+#
+# DESCRIPTION
+#   This method returns true if the current branch is a child of $branch.
+# ------------------------------------------------------------------------------
+
+sub is_child_of {
+  my ($self, $branch) = @_;
+
+  # The trunk cannot be a child branch
+  return if $self->is_trunk;
+
+  # If $branch is a branch, use name of $self to see when it is created
+  if ($branch->is_branch and $self->url =~ m#/r(\d+)_[^/]+/*$#) {
+    my $rev = $1;
+
+    # $self can only be a child if it is copied from a revision > the create
+    # revision of $branch
+    return if $rev < $branch->create_rev;
+  }
+
+  return if $self->parent->url ne $branch->url;
+
+  # If $branch is a branch, ensure that it is created before $self
+  return if $branch->is_branch and $self->create_rev <= $branch->create_rev;
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $cm_branch->is_sibling_of ($branch);
+#
+# DESCRIPTION
+#   This method returns true if the current branch is a sibling of $branch.
+# ------------------------------------------------------------------------------
+
+sub is_sibling_of {
+  my ($self, $branch) = @_;
+
+  # The trunk cannot be a sibling branch
+  return if $branch->is_trunk;
+
+  return if $self->parent->url ne $branch->parent->url;
+
+  # If the parent is a branch, ensure they are actually the same branch
+  return if $branch->parent->is_branch and
+            $self->parent->create_rev != $branch->parent->create_rev;
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_get_relatives ($relation);
+#
+# DESCRIPTION
+#   This method sets the $self->{$relation} variable by inspecting the list of
+#   branches at the current revision of the current branch. $relation can be
+#   either "CHILDREN" or "SIBLINGS".
+# ------------------------------------------------------------------------------
+
+sub _get_relatives {
+  my ($self, $relation) = @_;
+
+  my @branch_list = $self->branch_list;
+
+  $self->{$relation} = [];
+
+  # If we are searching for CHILDREN, get list of SIBLINGS, and vice versa
+  my $other = ($relation eq 'CHILDREN' ? 'SIBLINGS' : 'CHILDREN');
+  my %other_list;
+  if ($self->{$other}) {
+    %other_list = map {$_->url, 1} @{ $self->{$other} };
+  }
+
+  for my $u (@branch_list) {
+    # Ignore URL of current branch and its parent
+    next if $u eq $self->url;
+    next if $self->is_branch and $u eq $self->parent->url;
+
+    # Ignore if URL is a branch detected to be another type of relative
+    next if exists $other_list{$u};
+
+    # Construct new Fcm::CmBranch object from branch URL
+    my $url = $u . ($self->pegrev ? '@' . $self->pegrev : '');
+    my $branch = Fcm::CmBranch->new (URL => $url);
+
+    # Test whether $branch is a relative we are looking for
+    if ($relation eq 'CHILDREN') {
+      push @{ $self->{$relation} }, $branch if $branch->is_child_of ($self);
+
+    } else {
+      push @{ $self->{$relation} }, $branch if $branch->is_sibling_of ($self);
+    }
+  }
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @children = $cm_branch->children;
+#
+# DESCRIPTION
+#   This method returns a list of children (Fcm::CmBranch objects) of the
+#   current branch that exists in the current revision.
+# ------------------------------------------------------------------------------
+
+sub children {
+  my $self = shift;
+
+  $self->_get_relatives ('CHILDREN') if not $self->{CHILDREN};
+
+  return @{ $self->{CHILDREN} };
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @siblings = $cm_branch->siblings;
+#
+# DESCRIPTION
+#   This method returns a list of siblings (Fcm::CmBranch objects) of the
+#   current branch that exists in the current revision.
+# ------------------------------------------------------------------------------
+
+sub siblings {
+  my $self = shift;
+
+  $self->_get_relatives ('SIBLINGS') if not $self->{SIBLINGS};
+
+  return @{ $self->{SIBLINGS} };
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $ancestor = $cm_branch->ancestor ($branch);
+#
+# DESCRIPTION
+#   This method returns the common ancestor (a Fcm::CmBranch object) of a
+#   specified $branch and the current branch. The argument $branch must be a
+#   Fcm::CmBranch object. Both the current branch and $branch are assumed to be
+#   in the same project.
+# ------------------------------------------------------------------------------
+
+sub ancestor {
+  my ($self, $branch) = @_;
+
+  if (not exists $self->{ANCESTOR}{$branch->url_peg}) {
+    if ($self->url_peg eq $branch->url_peg) {
+      $self->{ANCESTOR}{$branch->url_peg} = $self;
+
+    } else {
+      # Get family tree of current branch, from trunk to current branch
+      my @this_family = ($self);
+      while (not $this_family [0]->is_trunk) {
+        unshift @this_family, $this_family [0]->parent;
+      }
+
+      # Get family tree of $branch, from trunk to $branch
+      my @that_family = ($branch);
+      while (not $that_family [0]->is_trunk) {
+        unshift @that_family, $that_family [0]->parent;
+      }
+
+      # Find common ancestor from list of parents
+      my $ancestor = undef;
+
+      while (not $ancestor) {
+        # $this and $that should both start as some revisions on the trunk.
+        # Walk down a generation each time it loops around.
+        my $this = shift @this_family;
+        my $that = shift @that_family;
+
+        if ($this->url eq $that->url) {
+          if ($this->is_trunk or $this->create_rev eq $that->create_rev) {
+            # $this and $that are the same branch
+            if (@this_family and @that_family) {
+              # More generations in both branches, try comparing the next
+              # generations.
+              next;
+
+            } else {
+              # End of lineage in one of the branches, ancestor is at the lower
+              # revision of the current URL.
+              if ($this->pegrev and $that->pegrev) {
+                $ancestor = $this->pegrev < $that->pegrev ? $this : $that;
+
+              } else {
+                $ancestor = $this->pegrev ? $this : $that;
+              }
+            }
+
+          } else {
+            # Despite the same URL, $this and $that are different branches as
+            # they are created at different revisions. The ancestor must be the
+            # parent with the lower revision. (This should not occur at the
+            # start.)
+            $ancestor = $this->parent->pegrev < $that->parent->pegrev
+                        ? $this->parent : $that->parent;
+          }
+
+        } else {
+          # Different URLs, ancestor must be the parent with the lower revision.
+          # (This should not occur at the start.)
+          $ancestor = $this->parent->pegrev < $that->parent->pegrev
+                      ? $this->parent : $that->parent;
+        }
+      }
+
+      $self->{ANCESTOR}{$branch->url_peg} = $ancestor;
+    }
+  }
+
+  return $self->{ANCESTOR}{$branch->url_peg};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   ($target, $upper, $lower) = $cm_branch->last_merge_from (
+#     $branch, $stop_on_copy,
+#   );
+#
+# DESCRIPTION
+#   This method returns a 3-element list with information of the last merge
+#   into the current branch from a specified $branch. The first element in the
+#   list $target (a Fcm::CmBranch object) is the target at which the merge was
+#   performed. (This can be the current branch or a parent branch up to the
+#   common ancestor with the specified $branch.) The second and third elements,
+#   $upper and $lower, (both Fcm::CmBranch objects), are the upper and lower
+#   ends of the source delta. If there is no merge from $branch into the
+#   current branch from their common ancestor to the current revision, this
+#   method will return an empty list. If $stop_on_copy is specified, it ignores
+#   merges from parents of $branch, and merges into parents of the current
+#   branch.
+# ------------------------------------------------------------------------------
+
+sub last_merge_from {
+  my ($self, $branch, $stop_on_copy) = @_;
+
+  if (not exists $self->{LAST_MERGE}{$branch->url_peg}) {
+    # Get "log" of current branch down to the common ancestor
+    my %log = $self->svnlog (
+      REV => [
+       ($self->pegrev ? $self->pegrev : 'HEAD'),
+       $self->ancestor ($branch)->pegrev,
+      ],
+
+      STOP_ON_COPY => $stop_on_copy,
+    );
+
+    my $cr = $self;
+
+    # Go down the revision log, checking for merge template messages
+    REV: for my $rev (sort {$b <=> $a} keys %log) {
+      # Loop each line of the log message at each revision
+      my @msg = split /\n/, $log{$rev}{msg};
+
+      # Also consider merges into parents of current branch
+      $cr = $cr->parent if ($cr->is_branch and $rev < $cr->create_rev);
+
+      for (@msg) {
+        # Ignore unless log message matches a merge template
+        next unless /Merged into \S+: (\S+) cf\. (\S+)/;
+
+        # Upper $1 and lower $2 ends of the source delta
+        my $u_path = $1;
+        my $l_path = $2;
+
+        # Add the root directory to the paths if necessary
+        $u_path = '/' . $u_path if substr ($u_path, 0, 1) ne '/';
+        $l_path = '/' . $l_path if substr ($l_path, 0, 1) ne '/';
+
+        # Only consider merges with specified branch (and its parent)
+        (my $path = $u_path) =~ s/@(\d+)$//;
+        my $u_rev = $1;
+
+        my $br = $branch;
+        $br    = $br->parent while (
+          $br->is_branch and $u_rev < $br->create_rev and not $stop_on_copy
+        );
+
+        next unless $br->branch_path eq $path;
+
+        # If $br is a parent of branch, ignore those merges with the parent
+        # above the branch point of the current branch
+        next if $br->pegrev and $br->pegrev < $u_rev;
+
+        # Set the return values
+        $self->{LAST_MERGE}{$branch->url_peg} = [
+          Fcm::CmBranch->new (URL => $cr->url . '@' . $rev), # target
+          Fcm::CmBranch->new (URL => $self->root . $u_path), # delta upper
+          Fcm::CmBranch->new (URL => $self->root . $l_path), # delta lower
+        ];
+
+        last REV;
+      }
+    }
+  }
+
+  return (exists $self->{LAST_MERGE}{$branch->url_peg}
+          ? @{ $self->{LAST_MERGE}{$branch->url_peg} } : ());
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @revs = $cm_branch->avail_merge_from ($branch[, $stop_on_copy]);
+#
+# DESCRIPTION
+#   This method returns a list of revisions of a specified $branch, which are
+#   available for merging into the current branch. If $stop_on_copy is
+#   specified, it will not list available merges from the parents of $branch.
+# ------------------------------------------------------------------------------
+
+sub avail_merge_from {
+  my ($self, $branch, $stop_on_copy) = @_;
+
+  if (not exists $self->{AVAIL_MERGE}{$branch->url_peg}) {
+    # Find out the revision of the upper delta at the last merge from $branch
+    # If no merge is found, use revision of common ancestor with $branch
+    my @last_merge = $self->last_merge_from ($branch);
+    my $rev        = $self->ancestor ($branch)->pegrev;
+    $rev           = $last_merge [1]->pegrev
+      if @last_merge and $last_merge [1]->pegrev > $rev;
+
+    # Get the "log" of the $branch down to $rev
+    my %log = $branch->svnlog (
+      REV          => [($branch->pegrev ? $branch->pegrev : 'HEAD'), $rev],
+      STOP_ON_COPY => $stop_on_copy,
+    );
+
+    # No need to include $rev itself, as it has already been merged
+    delete $log{$rev};
+
+    # No need to include the branch create revision
+    delete $log{$branch->create_rev}
+      if $branch->is_branch and exists $log{$branch->create_rev};
+
+    if (keys %log) {
+      # Check whether there is a latest merge from $self into $branch, if so,
+      # all revisions of $branch below that merge should become unavailable
+      my @last_merge_into = $branch->last_merge_from ($self);
+
+      if (@last_merge_into) {
+        for my $rev (keys %log) {
+          delete $log{$rev} if $rev < $last_merge_into [0]->pegrev;
+        }
+      }
+    }
+
+    # Available merges include all revisions above the branch creation revision
+    # or the revision of the last merge
+    $self->{AVAIL_MERGE}{$branch->url_peg} = [sort {$b <=> $a} keys %log];
+  }
+
+  return @{ $self->{AVAIL_MERGE}{$branch->url_peg} };
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $lower = $cm_branch->base_of_merge_from ($branch);
+#
+# DESCRIPTION
+#   This method returns the lower delta (a Fcm::CmBranch object) for the next
+#   merge from $branch.
+# ------------------------------------------------------------------------------
+
+sub base_of_merge_from {
+  my ($self, $branch) = @_;
+
+  # Base is the ancestor if there is no merge between $self and $branch
+  my $return = $self->ancestor ($branch);
+
+  # Get configuration for the last merge from $branch to $self
+  my @merge_from = $self->last_merge_from ($branch);
+
+  # Use the upper delta of the last merge from $branch, as all revisions below
+  # that have already been merged into the $self
+  $return = $merge_from [1]
+    if @merge_from and $merge_from [1]->pegrev > $return->pegrev;
+
+  # Get configuration for the last merge from $self to $branch
+  my @merge_into = $branch->last_merge_from ($self);
+
+  # Use the upper delta of the last merge from $self, as the current revision
+  # of $branch already contains changes of $self up to the peg revision of the
+  # upper delta
+  $return = $merge_into [1]
+    if @merge_into and $merge_into [0]->pegrev > $return->pegrev;
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $cm_branch->allow_subdir_merge_from ($branch, $subdir);
+#
+# DESCRIPTION
+#   This method returns true if a merge from the sub-directory $subdir in
+#   $branch  is allowed - i.e. it does not result in losing changes made in
+#   $branch outside of $subdir.
+# ------------------------------------------------------------------------------
+
+sub allow_subdir_merge_from {
+  my ($self, $branch, $subdir) = @_;
+
+  # Get revision at last merge from $branch or ancestor
+  my @merge_from = $self->last_merge_from ($branch);
+  my $last       = @merge_from ? $merge_from [1] : $self->ancestor ($branch);
+  my $rev        = $last->pegrev;
+
+  if ($branch->pegrev > $rev) {
+    # Inspect log from revision at last merge/ancestor to current revision
+    my %log = $branch->svnlog (REV => [$branch->pegrev, $rev + 1]);
+    my $br  = $branch;
+
+    # Check whether there are changes outside of $subdir
+    for my $rev (sort {$b <=> $a} keys %log) {
+      $br = $br->parent if $br->is_branch and $rev < $br->create_rev;
+
+      for my $path (keys %{ $log{$rev}{paths} }) {
+        next if $path eq $br->branch_path; # Ignore branch creation
+
+        my $p_subdir = substr ($path, length ($br->branch_path) + 1);
+
+        return if $p_subdir !~ m#^$subdir(?:/|$)#;
+      }
+    }
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $cm_branch->create (
+#     SRC                  => $src,
+#     TYPE                 => $type,
+#     NAME                 => $name,
+#     [PASSWORD            => $password,]
+#     [REV_FLAG            => $rev_flag,]
+#     [TICKET              => \@tickets,]
+#     [REV                 => $rev,]
+#     [NON_INTERACTIVE     => 1,]
+#     [SVN_NON_INTERACTIVE => 1,]
+#   );
+#
+# DESCRIPTION
+#   This method creates a branch in a Subversion repository.
+#
+# OPTIONS
+#   SRC                 - reference to a Fcm::CmUrl object.
+#   TYPE                - Specify the branch type. See help in "fcm branch" for
+#                         further information.
+#   NAME                - specify the name of the branch.
+#   NON_INTERACTIVE     - Do no interactive prompting, set SVN_NON_INTERACTIVE
+#                         to true automatically.
+#   PASSWORD            - specify the password for commit access.
+#   REV                 - specify the operative revision of the source.
+#   REV_FLAG            - A flag to specify the behaviour of the prefix to the
+#                         branch name. See help in "fcm branch" for further
+#                         information.
+#   SVN_NON_INTERACTIVE - Do no interactive prompting when running svn commit,
+#                         etc. This option is implied by NON_INTERACTIVE.
+#   TICKET              - Specify one or more related tickets for the branch.
+# ------------------------------------------------------------------------------
+
+sub create {
+  my $self = shift;
+  my %args = @_;
+
+  # Options
+  # ----------------------------------------------------------------------------
+  # Compulsory options
+  my $src  = $args{SRC};
+  my $type = $args{TYPE};
+  my $name = $args{NAME};
+
+  # Other options
+  my $rev_flag        = $args{REV_FLAG}        ? $args{REV_FLAG}    : 'NORMAL';
+  my @tickets         = exists $args{TICKET}   ? @{ $args{TICKET} } : ();
+  my $password        = exists $args{PASSWORD} ? $args{PASSWORD}    : undef;
+  my $orev            = exists $args{REV}      ? $args{REV}         : 'HEAD';
+
+  my $non_interactive     = exists $args{NON_INTERACTIVE}
+                            ? $args{NON_INTERACTIVE} : 0;
+  my $svn_non_interactive = exists $args{SVN_NON_INTERACTIVE}
+                            ? $args{SVN_NON_INTERACTIVE} : 0;
+  $svn_non_interactive    = $non_interactive ? 1 : $svn_non_interactive;
+
+  # Analyse the source URL
+  # ----------------------------------------------------------------------------
+  # Create branch from the trunk by default
+  $src->branch ('trunk') if not $src->branch;
+
+  # Remove "sub-directory" part from source URL
+  $src->subdir ('')      if $src->subdir;
+
+  # Remove "peg revision" part because it does not work with "svn copy"
+  $src->pegrev ('')      if $src->pegrev;
+
+  # Find out the URL and the last changed revision of the specified URL at the
+  # specified operative revision
+  my $url = $src->svninfo (FLAG => 'URL', REV => $orev);
+  e_report $src->url, ': cannot determine the operative URL at revision ',
+           $orev, ', abort.' if not $url;
+
+  $src->url ($url) if $url ne $src->url;
+
+  my $rev = $src->svninfo (FLAG => 'Last Changed Rev', REV => $orev);
+  e_report $src->url, ': cannot determine the last changed rev at revision',
+           $orev, ', abort.' if not $rev;
+
+  # Warn user if last changed revision is not the specified revision
+  w_report 'Warning: branch will be created from revision ', $rev,
+           ', i.e. the last changed rev.'
+    unless $orev and $orev eq $rev;
+
+  # Determine the sub-directory names of the branch
+  # ----------------------------------------------------------------------------
+  my @branch_dirs = ('branches');
+
+  # Split branch type flags into a hash table
+  my %type_flags = ();
+  $type_flags{$_} = 1 for ((split /::/, $type));
+
+  # Branch sub-directory 1, development, test or package
+  for my $flag (qw/DEV TEST PKG/) {
+    if (exists $type_flags{$flag}) {
+      push @branch_dirs, lc ($flag);
+      last;
+    }
+  }
+
+  # Branch sub-directory 2, user, share, configuration or release
+  if (exists $type_flags{USER}) {
+    my $userid = getlogin;
+    $userid    = $ENV{LOGNAME} unless $userid;
+    $userid    = $ENV{USER}    unless $userid;
+    die 'Unable to determine your user ID, abort' unless $userid;
+
+    push @branch_dirs, $userid;
+
+  } else {
+    for my $flag (qw/SHARE CONFIG REL/) {
+      if (exists $type_flags{$flag}) {
+        push @branch_dirs, uc (substr $flag, 0, 1) . lc (substr $flag, 1);
+        last;
+      }
+    }
+  }
+
+  # Branch sub-directory 3, branch name
+  # Prefix branch name with revision number/keyword if necessary
+  my $prefix = '';
+  if ($rev_flag ne 'NONE') {
+    $prefix = $rev;
+
+    # Attempt to replace revision number with a revision keyword if necessary
+    $prefix = &get_rev_keyword (
+      REV => $rev,
+      URL => $src->url_peg,
+    ) if $rev_flag eq 'NORMAL';
+
+    # $prefix is still a revision number, add "r" in front of it
+    $prefix = 'r' . $prefix if $prefix eq $rev;
+
+    # Add an underscore before the branch name
+    $prefix.= '_';
+  }
+
+  # Branch name
+  push @branch_dirs, $prefix . $name;
+
+  # Check whether the branch already exists, fail if so
+  # ----------------------------------------------------------------------------
+  # Construct the URL of the branch
+  $self->project_url ($src->project_url);
+  $self->branch  (join ('/', @branch_dirs));
+
+  # Check that branch does not already exists
+  e_report $self->url, ': branch already exists, abort.' if $self->url_exists;
+
+  # Message for the commit log
+  # ----------------------------------------------------------------------------
+  my @message = ('Created ' . $self->branch_path .  ' from ' .
+                 $src->branch_path . '@' . $rev . '.' . "\n");
+
+  # Add related Trac ticket links to commit log if set
+  if (@tickets) {
+    my $ticket_mesg = 'Relates to ticket' . (@tickets > 1 ? 's' : '');
+
+    while (my $ticket = shift @tickets) {
+      $ticket_mesg .= ' #' . $ticket;
+      $ticket_mesg .= (@tickets > 1 ? ',' : ' and') if @tickets >= 1;
+    }
+
+    push @message, $ticket_mesg . ".\n";
+  }
+
+  # Create a temporary file for the commit log message
+  my $ci_mesg = Fcm::CmCommitMessage->new;
+  $ci_mesg->auto_mesg (@message);
+  $ci_mesg->ignore_mesg ('A' . ' ' x 4 . $self->url . "\n");
+  my $logfile = $ci_mesg->edit_file (TEMP => 1, BATCH => $non_interactive);
+
+  # Check with the user to see if he/she wants to go ahead
+  # ----------------------------------------------------------------------------
+  if (not $non_interactive) {
+    my $reply = &main::get_input (
+      TITLE   => 'fcm branch',
+      MESSAGE => 'Would you like to go ahead and create this branch?',
+      TYPE    => 'yn',
+      DEFAULT => 'n',
+    );
+
+    return unless $reply eq 'y';
+  }
+
+  # Ensure existence of container sub-directories of the branch
+  # ----------------------------------------------------------------------------
+  for my $i (0 .. $#branch_dirs - 1) {
+    my $subdir     = join ('/', @branch_dirs[0 .. $i]);
+    my $subdir_url = Fcm::CmUrl->new (URL => $src->project_url . '/' . $subdir);
+
+    # Check whether each sub-directory of the branch already exists,
+    # if sub-directory does not exist, create it
+    next if $subdir_url->url_exists;
+
+    print 'Creating sub-directory: ', $subdir, "\n";
+
+    my @command = (
+      qw/svn mkdir/,
+      '-m', 'Created ' . $subdir . ' directory.',
+      ($svn_non_interactive  ? '--non-interactive'       : ()),
+      (defined $password     ? ('--password', $password) : ()),
+
+      $subdir_url->url,
+    );
+    &run_command (\@command);
+  }
+
+  # Create the branch
+  # ----------------------------------------------------------------------------
+  {
+    print 'Creating branch ', $self->url, ' ...', "\n";
+    my @command = (
+      qw/svn copy/,
+      '-r', $rev,
+      '-F', $logfile,
+      ($svn_non_interactive  ? '--non-interactive'       : ()),
+      (defined $password     ? ('--password', $password) : ()),
+
+      $src->url, $self->url,
+    );
+    &run_command (\@command);
+  }
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $cm_branch->delete (
+#     [NON_INTERACTIVE     => 1,]
+#     [PASSWORD            => $password,]
+#     [SVN_NON_INTERACTIVE => 1,]
+#   );
+#
+# DESCRIPTION
+#   This method deletes the current branch from the Subversion repository.
+#
+# OPTIONS
+#   NON_INTERACTIVE     - Do no interactive prompting, set SVN_NON_INTERACTIVE
+#                         to true automatically.
+#   PASSWORD            - specify the password for commit access.
+#   SVN_NON_INTERACTIVE - Do no interactive prompting when running svn commit,
+#                         etc. This option is implied by NON_INTERACTIVE.
+# ------------------------------------------------------------------------------
+
+sub del {
+  my $self = shift;
+  my %args = @_;
+
+  # Options
+  # ----------------------------------------------------------------------------
+  my $password            = exists $args{PASSWORD} ? $args{PASSWORD} : undef;
+  my $non_interactive     = exists $args{NON_INTERACTIVE}
+                            ? $args{NON_INTERACTIVE} : 0;
+  my $svn_non_interactive = exists $args{SVN_NON_INTERACTIVE}
+                            ? $args{SVN_NON_INTERACTIVE} : 0;
+  $svn_non_interactive    = $non_interactive ? 1 : $svn_non_interactive;
+
+  # Ensure URL is a branch
+  # ----------------------------------------------------------------------------
+  e_report $self->url_peg, ': not a branch, abort.' if not $self->is_branch;
+
+  # Message for the commit log
+  # ----------------------------------------------------------------------------
+  my @message = ('Deleted ' . $self->branch_path . '.' . "\n");
+
+  # Create a temporary file for the commit log message
+  my $ci_mesg = Fcm::CmCommitMessage->new ();
+  $ci_mesg->auto_mesg (@message);
+  $ci_mesg->ignore_mesg ('D' . ' ' x 4 . $self->url . "\n");
+  my $logfile = $ci_mesg->edit_file (TEMP => 1, BATCH => $non_interactive);
+
+  # Check with the user to see if he/she wants to go ahead
+  # ----------------------------------------------------------------------------
+  if (not $non_interactive) {
+    my $reply = &main::get_input (
+      TITLE   => 'fcm branch',
+      MESSAGE => 'Would you like to go ahead and delete this branch?',
+      TYPE    => 'yn',
+      DEFAULT => 'n',
+    );
+
+    return unless $reply eq 'y';
+  }
+
+  # Delete branch if answer is "y" for "yes"
+  # ----------------------------------------------------------------------------
+  print 'Deleting branch ', $self->url, ' ...', "\n";
+  my @command = (
+    qw/svn delete/,
+    '-F', $logfile,
+    (defined $password    ? ('--password', $password) : ()),
+    ($svn_non_interactive ? '--non-interactive'       : ()),
+
+    $self->url,
+  );
+  &run_command (\@command);
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $cm_branch->display_info (
+#     [SHOW_CHILDREN => 1],
+#     [SHOW_OTHER    => 1]
+#     [SHOW_SIBLINGS => 1]
+#   );
+#
+# DESCRIPTION
+#   This method displays information of the current branch. If SHOW_CHILDREN is
+#   set, it shows information of all current children branches of the current
+#   branch. If SHOW_SIBLINGS is set, it shows information of siblings that have
+#   been merged recently with the current branch. If SHOW_OTHER is set, it shows
+#   information of custom/reverse merges.
+# ------------------------------------------------------------------------------
+
+sub display_info {
+  my $self = shift;
+  my %args = @_;
+
+  # Arguments
+  # ----------------------------------------------------------------------------
+  my $show_children = exists $args{SHOW_CHILDREN} ? $args{SHOW_CHILDREN} : 0;
+  my $show_other    = exists $args{SHOW_OTHER   } ? $args{SHOW_OTHER}    : 0;
+  my $show_siblings = exists $args{SHOW_SIBLINGS} ? $args{SHOW_SIBLINGS} : 0;
+
+  # Useful variables
+  # ----------------------------------------------------------------------------
+  my $separator  = '-' x 80 . "\n";
+  my $separator2 = '  ' . '-' x 78 . "\n";
+
+  # Print "info" as returned by "svn info"
+  # ----------------------------------------------------------------------------
+  for my $key ('URL', 'Repository Root', 'Revision', 'Last Changed Author',
+               'Last Changed Rev', 'Last Changed Date') {
+    print $key, ': ', $self->svninfo (FLAG => $key), "\n"
+      if $self->svninfo (FLAG => $key);
+  }
+
+  if ($self->config->verbose) {
+    # Verbose mode, print log message at last changed revision
+    my %log = $self->svnlog (REV => $self->svninfo (FLAG => 'Last Changed Rev'));
+    my @log = split /\n/, $log{msg};
+    print 'Last Changed Log:', "\n\n", map ({'  ' . $_ . "\n"} @log), "\n";
+  }
+
+  if ($self->is_branch) {
+    # Print create information
+    # --------------------------------------------------------------------------
+    my %log = $self->svnlog (REV => $self->create_rev);
+
+    print $separator;
+    print 'Branch Create Author: ', $log{author}, "\n" if $log{author};
+    print 'Branch Create Rev: ', $self->create_rev, "\n";
+    print 'Branch Create Date: ', &svn_date ($log{date}), "\n";
+
+    if ($self->config->verbose) {
+      # Verbose mode, print log message at last create revision
+      my @log = split /\n/, $log{msg};
+      print 'Branch Create Log:', "\n\n", map ({'  ' . $_ . "\n"} @log), "\n";
+    }
+
+    # Print delete information if branch no longer exists
+    # --------------------------------------------------------------------------
+    print 'Branch Delete Rev: ', $self->delete_rev, "\n" if $self->delete_rev;
+
+    # Report merges into/from the parent
+    # --------------------------------------------------------------------------
+    # Print the URL@REV of the parent branch
+    print $separator, 'Branch Parent: ', $self->parent->url_peg, "\n";
+
+    # Set up a new object for the parent at the current revision
+    # --------------------------------------------------------------------------
+    my $p_url  = $self->parent->url;
+    $p_url    .= '@' . $self->pegrev if $self->pegrev;
+    my $parent = Fcm::CmBranch->new (URL => $p_url);
+
+    if (not $parent->url_exists) {
+      print 'Branch parent deleted.', "\n";
+      return;
+    }
+
+    # Report merges into/from the parent
+    # --------------------------------------------------------------------------
+    print $self->_report_merges ($parent, 'Parent');
+  }
+
+  # Report merges with siblings
+  # ----------------------------------------------------------------------------
+  if ($show_siblings) {
+    # Report number of sibling branches found
+    print $separator, 'Seaching for siblings ... ';
+    my @siblings = $self->siblings;
+    print scalar (@siblings), ' ', (@siblings> 1 ? 'siblings' : 'sibling'),
+          ' found.', "\n";
+
+    # Report branch name and merge information only if there are recent merges
+    my $out = '';
+    for my $sibling (@siblings) {
+      my $string = $self->_report_merges ($sibling, 'Sibling');
+
+      $out .= $separator2 . '  ' . $sibling->url . "\n" . $string if $string;
+    }
+
+    if (@siblings) {
+      if ($out) {
+        print 'Merges with existing siblings:', "\n", $out;
+
+      } else {
+        print 'No merges with existing siblings.', "\n";
+      }
+    }
+  }
+
+  # Report children
+  # ----------------------------------------------------------------------------
+  if ($show_children) {
+    # Report number of child branches found
+    print $separator, 'Seaching for children ... ';
+    my @children = $self->children;
+    print scalar (@children), ' ', (@children > 1 ? 'children' : 'child'),
+          ' found.', "\n";
+
+    # Report children if they exist
+    print 'Current children:', "\n" if @children;
+
+    for my $child (@children) {
+      print $separator2, '  ', $child->url, "\n";
+      print '  Child Create Rev: ', $child->create_rev, "\n";
+      print $self->_report_merges ($child, 'Child');
+    }
+  }
+
+  # Report custom/reverse merges into the branch
+  # ----------------------------------------------------------------------------
+  if ($show_other) {
+    my %log = $self->svnlog (STOP_ON_COPY => 1);
+    my @out;
+
+    # Go down the revision log, checking for merge template messages
+    REV: for my $rev (sort {$b <=> $a} keys %log) {
+      # Loop each line of the log message at each revision
+      my @msg = split /\n/, $log{$rev}{msg};
+
+      for (@msg) {
+        # Ignore unless log message matches a merge template
+        if (/^Reversed r\d+(:\d+)? of \S+$/ or
+            s/^(Custom merge) into \S+(:.+)$/$1$2/) {
+          push @out, ('r' . $rev . ': ' . $_) . "\n";
+        }
+      }
+    }
+
+    print $separator, 'Other merges:', "\n", @out if @out;
+  }
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = $self->_report_merges ($branch, $relation);
+#
+# DESCRIPTION
+#   This method returns a string for displaying merge information with a
+#   branch, the $relation of which can be a Parent, a Sibling or a Child.
+# ------------------------------------------------------------------------------
+
+sub _report_merges {
+  my ($self, $branch, $relation) = @_;
+
+  my $indent    = ($relation eq 'Parent') ? '' : '  ';
+  my $separator = ($relation eq 'Parent') ? ('-' x 80) : ('  ' . '-' x 78);
+  $separator   .= "\n";
+
+  my $return = '';
+
+  # Report last merges into/from the $branch
+  # ----------------------------------------------------------------------------
+  my %merge  = (
+    'Last Merge From ' . $relation . ':'
+    => [$self->last_merge_from ($branch, 1)],
+    'Last Merge Into ' . $relation . ':'
+    => [$branch->last_merge_from ($self, 1)],
+  );
+
+  if ($self->config->verbose) {
+    # Verbose mode, print the log of the merge
+    for my $key (keys %merge) {
+      next if not @{ $merge{$key} };
+
+      # From: target (0) is self, upper delta (1) is $branch
+      # Into: target (0) is $branch, upper delta (1) is self
+      my $t = ($key =~ /From/) ? $self : $branch;
+
+      $return .= $indent . $key . "\n";
+      $return .= $separator . $t->display_svnlog ($merge{$key}[0]->pegrev);
+    }
+
+  } else {
+    # Normal mode, print in simplified form (rREV Parent@REV)
+    for my $key (keys %merge) {
+      next if not @{ $merge{$key} };
+
+      # From: target (0) is self, upper delta (1) is $branch
+      # Into: target (0) is $branch, upper delta (1) is self
+      $return .= $indent . $key . ' r' . $merge{$key}[0]->pegrev . ' ' .
+                 $merge{$key}[1]->path_peg . ' cf. ' .
+                 $merge{$key}[2]->path_peg . "\n";
+    }
+  }
+
+  if ($relation eq 'Sibling') {
+    # For sibling, do not report further if there is no recent merge
+    my @values = values %merge;
+
+    return $return unless (@{ $values[0] } or @{ $values[1] });
+  }
+
+  # Report available merges into/from the $branch
+  # ----------------------------------------------------------------------------
+  my %avail = (
+    'Merges Avail From ' . $relation . ':'
+    => ($self->delete_rev ? [] : [$self->avail_merge_from ($branch, 1)]),
+    'Merges Avail Into ' . $relation . ':'
+    => [$branch->avail_merge_from ($self, 1)],
+  );
+
+  if ($self->config->verbose) {
+    # Verbose mode, print the log of each revision
+    for my $key (keys %avail) {
+      next unless @{ $avail{$key} };
+
+      $return .= $indent . $key . "\n";
+
+      my $s = ($key =~ /From/) ? $branch: $self;
+
+      for my $rev (@{ $avail{$key} }) {
+        $return .= $separator . $s->display_svnlog ($rev);
+      }
+    }
+
+  } else {
+    # Normal mode, print only the revisions
+    for my $key (keys %avail) {
+      next unless @{ $avail{$key} };
+
+      $return .= $indent . $key . ' ' . join (' ', @{ $avail{$key} }) . "\n";
+    }
+  }
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/CmCommitMessage.pm
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/CmCommitMessage.pm	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/CmCommitMessage.pm	(revision 5058)
@@ -0,0 +1,395 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::CmCommitMessage
+#
+# DESCRIPTION
+#   This class contains methods to read, write and edit the commit message file
+#   in a working copy.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+package Fcm::CmCommitMessage;
+
+# Standard pragma
+use warnings;
+use strict;
+
+# Standard modules
+use Carp;
+use Cwd;
+use File::Spec;
+use File::Temp qw/tempfile/;
+
+# FCM component modules
+use Fcm::Util qw/e_report run_command/;
+
+# Commit log delimiter messages
+my $ignore_line = '--This line, and those below, will be ignored--';
+my $auto_line   = '--This line will be ignored and those below will be ' .
+                  'inserted automatically--';
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $ci_mesg = Fcm::CmCommitMessage->new (
+#     CONFIG  => $config,
+#     DIR     => $dir,
+#     BASE    => $base,
+#   );
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::CmCommitMessageFile class.
+#
+# ARGUMENTS
+#   CONFIG - reference to a Fcm::Config instance
+#   DIR    - directory containing the commit message file
+#   BASE   - base name of the commit message file
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self = {
+    CONFIG => (exists $args{CONFIG} ? $args{CONFIG} : &main::cfg),
+    DIR    => (exists $args{DIR}    ? $args{DIR}    : cwd),
+    BASE   => (exists $args{BASE}   ? $args{BASE}   : '#commit_message#'),
+    USER_M => [],
+    AUTO_M => [],
+    IGNORE => [],
+  };
+
+  bless $self, $class;
+  return $self;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $config = $ci_mesg->config;
+#
+# DESCRIPTION
+#   This method returns a reference to the Fcm::Config instance.
+# ------------------------------------------------------------------------------
+
+sub config {
+  my $self = shift;
+
+  return $self->{CONFIG};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $dir = $ci_mesg->dir;
+#   $ci_mesg->dir ($dir);
+#
+# DESCRIPTION
+#   This method returns the directory container of the commit message file. If
+#   an argument is specified, the directory is reset using the value of the
+#   argument. (It does nothing is the directory does not already exist.)
+# ------------------------------------------------------------------------------
+
+sub dir {
+  my $self = shift;
+
+  if (@_) {
+    my $dir = shift;
+    $self->{DIR} = $dir if -d $dir;
+  }
+
+  return $self->{DIR};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $base = $ci_mesg->base;
+#   $ci_mesg->base ($base);
+#
+# DESCRIPTION
+#   This method returns the base name of the commit message file. If an
+#   argument is specified, the file is reset using the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub base {
+  my $self = shift;
+
+  if (@_) {
+    $self->{BASE} = shift;
+  }
+
+  return $self->{BASE};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $file = $ci_mesg->file;
+#   $ci_mesg->file ($file);
+#
+# DESCRIPTION
+#   This method returns the full name of the commit message file. If an
+#   argument is specified, the file is reset using the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub file {
+  my $self = shift;
+
+  if (@_) {
+    my $file      = shift;
+    $self->{DIR}  = dirname  ($file);
+    $self->{BASE} = basename ($file);
+  }
+
+  return File::Spec->catfile ($self->{DIR}, $self->{BASE});
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @mesg = $ci_mesg->user_mesg;
+#   $ci_mesg->user_mesg (@mesg);
+#
+# DESCRIPTION
+#   This method returns the user defined commit message. If an argument is
+#   specified, the message is reset using the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub user_mesg {
+  my $self = shift;
+
+  if (@_) {
+    @{ $self->{USER_M} } = @_;
+  }
+
+  return @{ $self->{USER_M} };
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @mesg = $ci_mesg->auto_mesg;
+#   $ci_mesg->auto_mesg (@mesg);
+#
+# DESCRIPTION
+#   This method returns the automatically inserted commit message. If an
+#   argument is specified, the message is reset using the value of the
+#   argument.
+# ------------------------------------------------------------------------------
+
+sub auto_mesg {
+  my $self = shift;
+
+  if (@_) {
+    @{ $self->{AUTO_M} } = @_;
+  }
+
+  return @{ $self->{AUTO_M} };
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @mesg = $ci_mesg->ignore_mesg;
+#   $ci_mesg->ignore_mesg (@mesg);
+#
+# DESCRIPTION
+#   This method returns the ignored part of a commit message. If an argument is
+#   specified, the message is reset using the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub ignore_mesg {
+  my $self = shift;
+
+  if (@_) {
+    @{ $self->{IGNORE} } = @_;
+  }
+
+  return @{ $self->{IGNORE} };
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   ($user, $auto) = $ci_mesg->read_file ();
+#
+# DESCRIPTION
+#   This function reads from the commit log message file. It resets the user
+#   and the automatic messages after reading the file. It returns the message
+#   back in two array references.
+# ------------------------------------------------------------------------------
+
+sub read_file {
+  my $self = shift;
+
+  my @user = ();
+  my @auto = ();
+  my $file = $self->file;
+
+  if (-r $file) {
+    open FILE, '<', $file or croak 'Cannot open ', $file, '(', $!, '), abort';
+
+    my $in_auto = 0;
+    while (<FILE>) {
+      last if index ($_, $ignore_line) == 0; # ignore after the ignore delimiter
+
+      if (index ($_, $auto_line) == 0) {
+        # Beginning of the automatically inserted message delimiter
+        $in_auto = 1;
+        next;
+      }
+
+      if ($in_auto) {
+        push @auto, $_;
+
+      } else {
+        push @user, $_;
+      }
+    }
+
+    close FILE;
+
+    $self->user_mesg (@user);
+    $self->auto_mesg (@auto);
+  }
+
+  return (\@user, \@auto);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $ci_mesg->write_file ();
+#
+# DESCRIPTION
+#   This function writes to the commit log message file based on the content of
+#   the user defined message, and the automatically inserted message.
+# ------------------------------------------------------------------------------
+
+sub write_file {
+  my $self = shift;
+  my %args = @_;
+
+  my @user = $self->user_mesg;
+  my @auto = $self->auto_mesg;
+  my $file = $self->file;
+
+  open FILE, '>', $file or die 'Cannot open ', $file, '(', $!, '), abort';
+  print FILE @user;
+  print FILE $auto_line, "\n", @auto if @auto;
+  close FILE or croak 'Cannot close ', $file, '(', $!, '), abort';
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $file = $ci_mesg->edit_file ([TEMP => 1,] [BATCH => 1,]);
+#
+# DESCRIPTION
+#   This function normally triggers an editor for editing the commit message.
+#   If TEMP is set, it edits a temporary file. Otherwise, it edits the current
+#   commit message file. It resets the user defined message on success. Returns
+#   the name of the commit log file. Do not start the editor if BATCH is set.
+# ------------------------------------------------------------------------------
+
+sub edit_file {
+  my $self  = shift;
+  my %args  = @_;
+  my $temp  = exists $args{TEMP}  ? $args{TEMP}  : 0;
+  my $batch = exists $args{BATCH} ? $args{BATCH} : 0;
+
+  my @user   = $self->user_mesg;
+  my @auto   = $self->auto_mesg;
+  my @ignore = $self->ignore_mesg;
+  my $file   = $self->file;
+
+  if ($temp) {
+    my $fh;
+    ($fh, $file) = tempfile (SUFFIX => ".fcm", UNLINK => 1);
+    close $fh;
+  }
+
+  # Add original or code driven message and status information to the file
+  my $select = select;
+  open FILE, '>', $file or croak 'Cannot open ', $file, ' (', $!, '), abort';
+  select FILE;
+
+  print @user;
+  print (@auto || @user ? '' : "\n");
+  print $auto_line, "\n", @auto, "\n" if @auto;
+  print $ignore_line, "\n\n";
+  print @ignore if @ignore;
+
+  close FILE or die 'Cannot close ', $file, ' (', $!, '), abort';
+  select $select;
+
+  if (not $batch) {
+    # Select editor
+    my $editor = 'nedit';
+    
+    if ($ENV{'SVN_EDITOR'}) {
+      $editor = $ENV{'SVN_EDITOR'};
+
+    } elsif ($ENV{'VISUAL'}) {
+      $editor = $ENV{'VISUAL'};
+
+    } elsif ($ENV{'EDITOR'}) {
+      $editor = $ENV{'EDITOR'};
+    }
+
+    # Execute command to start the editor
+    print 'Starting ', $editor, ' to edit commit message ...', "\n";
+    &run_command ([split (/\s+/, $editor), $file]);
+  }
+
+  # Read the edited file, and extract user log message from it
+  open FILE, '<', $file or croak 'Cannot open ', $file, ' (', $!, '), abort';
+  my (@log);
+  my $in_auto    = 0;
+  my $in_ignored = 0;
+
+  while (<FILE>) {
+    if (index ($_, $auto_line) == 0) {
+      $in_auto = 1;
+
+    } elsif (index ($_, $ignore_line) == 0) {
+      $in_ignored = 1;
+      last;
+
+    } else {
+      next if $in_auto;
+      push @log, $_;
+    }
+  }
+
+  close FILE;
+
+  # Ensure auto and ignored lines are not altered
+  e_report 'Error: the line "', $auto_line, '" has been altered, abort.'
+    if @auto and not $in_auto;
+
+  e_report 'Error: the line "', $ignore_line, '" has been altered, abort.'
+    if not $in_ignored;
+
+  # Check for empty commit log
+  e_report 'Error: log message unchanged or not specified, abort.'
+    if join (' ', (@log, @auto)) =~ /^\s*$/;
+
+  # Echo the commit message to standard output
+  my $separator = '-' x 80 . "\n";
+  print 'Commit message is as follows:', "\n";
+  print $separator, @log, @auto, $ignore_line, "\n", @ignore, $separator;
+
+  open FILE, '>', $file or croak 'Cannot open ', $file, ' (', $!, '), abort';
+  print FILE @log, @auto;
+  close FILE or croak 'Cannot close ', $file, ' (', $!, '), abort';
+
+  # Reset the array for the user specified log message
+  $self->user_mesg (@log);
+
+  return $file;
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/CmUrl.pm
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/CmUrl.pm	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/CmUrl.pm	(revision 5058)
@@ -0,0 +1,1138 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::CmUrl
+#
+# DESCRIPTION
+#   This class contains methods for manipulating a Subversion URL in a standard
+#   FCM project.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+package Fcm::CmUrl;
+
+# Standard pragma
+use warnings;
+use strict;
+
+# Standard modules
+use Carp;
+use File::Spec;
+use HTTP::Date;
+use XML::DOM;
+
+# FCM component modules
+use Fcm::Util qw/run_command svn_date get_browser_url/;
+
+# Revision pattern
+my $rev_pattern = '\d+|HEAD|BASE|COMMITTED|PREV|\{.+\}';
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $cm_url = Fcm::CmUrl->new (
+#     CONFIG  => $config,
+#     URL     => $url,
+#   );
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::CmUrl class.
+#
+# ARGUMENTS
+#   CONFIG - reference to a Fcm::Config instance
+#   URL    - URL of a branch
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self = {
+    CONFIG      => (exists $args{CONFIG} ? $args{CONFIG} : &main::cfg),
+    URL         => (exists $args{URL}    ? $args{URL}    : ''),
+    ANALYSED    => undef,
+    PROJECT     => undef,
+    BRANCH      => undef,
+    SUBDIR      => undef,
+    PEGREV      => undef,
+    LIST        => undef,
+    RLIST       => undef,
+    LOG         => undef,
+    LOG_RANGE   => undef,
+    INFO        => undef,
+    BRANCH_LIST => undef, # list of branches in current project
+  };
+
+  bless $self, $class;
+  return $self;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $config = $cm_url->config;
+#
+# DESCRIPTION
+#   This method returns a reference to the Fcm::Config instance.
+# ------------------------------------------------------------------------------
+
+sub config {
+  my $self = shift;
+
+  return $self->{CONFIG};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $url = $cm_url->url_peg;
+#   $cm_url->url_peg ($url);
+#
+# DESCRIPTION
+#   This method returns/sets the current URL@PEG.
+# ------------------------------------------------------------------------------
+
+sub url_peg {
+  my $self = shift;
+
+  if (@_) {
+    if (! $self->{URL} or $_[0] ne $self->{URL}) {
+      # Re-set URL
+      $self->{URL} = shift;
+
+      # Re-set essential variables
+      $self->{$_}  = undef for (qw/ANALYSED RLIST LIST INFO LOG LOG_RANGE/);
+    }
+  }
+
+  return $self->{URL};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $cm_url->is_url ();
+#
+# DESCRIPTION
+#   Returns true if current url is a valid Subversion URL.
+# ------------------------------------------------------------------------------
+
+sub is_url {
+  my $self = shift;
+
+  # This should handle URL beginning with svn://, http:// and svn+ssh://
+  return ($self->url_peg =~ m#^[\+\w]+://#);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $cm_url->url_exists ([$rev]);
+#
+# DESCRIPTION
+#   Returns true if current url exists (at operative revision $rev) in a
+#   Subversion repository.
+# ------------------------------------------------------------------------------
+
+sub url_exists {
+  my ($self, $rev) = @_;
+
+  my $exists = $self->svnlist (REV => $rev);
+
+  return defined ($exists);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = $cm_url->svninfo ([FLAG => $flag], [REV => $rev]);
+#
+# DESCRIPTION
+#   Returns the value of $flag, where $flag is a field returned by "svn info".
+#   (If $flag is not set, default to "URL".) Otherwise returns an empty string.
+#   If REV is specified, it will be used as the operative revision.
+# ------------------------------------------------------------------------------
+
+sub svninfo {
+  my $self = shift;
+  my %args = @_;
+
+  my $flag = exists $args{FLAG} ? $args{FLAG} : 'URL';
+  my $rev  = exists $args{REV}  ? $args{REV}  : undef;
+
+  $rev = ($self->pegrev ? $self->pegrev : 'HEAD') if not $rev;
+
+  return if not $self->is_url;
+
+  # Get "info" for the specified revision if necessary
+  if (not exists $self->{INFO}{$rev}) {
+    # Invoke "svn info" command
+    my @info = &run_command (
+      [qw/svn info -r/, $rev, $self->url_peg],
+      PRINT   => $self->config->verbose > 2,
+      METHOD  => 'qx',
+      DEVNULL => 1,
+      ERROR   => 'ignore',
+    );
+
+    # Store selected information
+    for (@info) {
+      chomp;
+
+      if (/^(.+?):\s*(.+)$/) {
+        $self->{INFO}{$rev}{$1} = $2;
+      }
+    }
+  }
+
+  my $return = exists $self->{INFO}{$rev}{$flag}
+               ? $self->{INFO}{$rev}{$flag} : undef;
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   %logs = $cm_url->svnlog (
+#     [REV          => $rev,]
+#     [REV          => \@revs,] # reference to a 2-element array
+#     [STOP_ON_COPY => 1,]
+#   );
+#
+# DESCRIPTION
+#   Returns the logs for the current URL. If REV is a range of revisions or not
+#   specified, return a hash where the keys are revision numbers and the values
+#   are the entries (which are hash references). If a single REV is specified,
+#   return the entry (a hash reference) at the specified REV. Each entry in the
+#   returned list is a hash reference, with the following structure:
+#
+#   $entry = {
+#     author => $author,              # the commit author
+#     date   => $date,                # the commit date (in seconds since epoch)
+#     msg    => $msg,                 # the log message
+#     paths  => {                     # list of changed paths
+#       $path1  => {                  # a changed path
+#         copyfrom-path => $frompath, # copy-from-path
+#         copyfrom-rev  => $fromrev,  # copy-from-revision
+#         action        => $action,   # action status code
+#       },
+#       ...     => { ... },           # ... more changed paths ...
+#     },
+#   }
+# ------------------------------------------------------------------------------
+
+sub svnlog {
+  my $self = shift;
+  my %args = @_;
+
+  my $stop_on_copy  = exists $args{STOP_ON_COPY} ? $args{STOP_ON_COPY} : 0;
+  my $rev_arg       = exists $args{REV}          ? $args{REV}          : 0;
+
+  my @revs;
+
+  # Get revision options
+  # ----------------------------------------------------------------------------
+  if ($rev_arg) {
+    if (ref ($rev_arg)) {
+      # Revsion option is an array, a range of revisions specified?
+      ($revs [0], $revs [1]) = @$rev_arg;
+
+    } else {
+      # A single revision specified
+      $revs [0] = $rev_arg;
+    }
+
+    # Expand 'HEAD' revision
+    for my $rev (@revs) {
+      next unless uc ($rev) eq 'HEAD';
+      $rev = $self->svninfo (FLAG => 'Revision', REV => 'HEAD');
+    }
+
+  } else {
+    # No revision option specified, get log for all revisions
+    $revs [0] = $self->svninfo (FLAG => 'Revision');
+    $revs [1] = 1;
+  }
+
+  $revs [1] = $revs [0] if not $revs [1];
+  @revs     = sort {$b <=> $a} @revs;
+
+  # Check whether a "svn log" run is necessary
+  # ----------------------------------------------------------------------------
+  my $need_update = ! ($revs [0] == $revs [1] and exists $self->{LOG}{$revs [0]});
+  my @ranges      = @revs;
+  if ($need_update and $self->{LOG_RANGE}) {
+    my %log_range = %{ $self->{LOG_RANGE} };
+
+    if ($stop_on_copy) {
+      $ranges [1] = $log_range{UPPER} if $ranges [1] >= $log_range{LOWER_SOC};
+
+    } else {
+      $ranges [1] = $log_range{UPPER} if $ranges [1] >= $log_range{LOWER};
+    }
+  }
+
+  $need_update = 0 if $ranges [0] < $ranges [1];
+
+  if ($need_update) {
+    # Invoke "svn log" command for all revisions of the current branch
+    # --------------------------------------------------------------------------
+    my @command = (
+      qw/svn log --xml -v/, ($stop_on_copy ? '--stop-on-copy' : ()),
+      '-r' . join (':', @ranges),
+      $self->url,
+    );
+
+    my $rc;
+    my @xml = &run_command (
+      \@command,
+      PRINT   => $self->config->verbose > 2,
+      METHOD  => 'qx',
+      DEVNULL => 1,
+      ERROR   => 'ignore',
+      RC      => \$rc,
+    );
+
+    # Parse the XML
+    # --------------------------------------------------------------------------
+    if (not $rc) {
+      my $parser = XML::DOM::Parser->new;
+      my $doc    = $parser->parse (join ('', @xml));
+
+      my $entry_list = $doc->getElementsByTagName ('logentry');
+
+      # Record the author, date, message and path change for each revision
+      for my $i (0 .. $entry_list->getLength - 1) {
+        # Select current entry from node list
+        my $entry = $entry_list->item ($i);
+        my %this = ();
+
+        # Revision is an attribute of the entry node
+        my $rev   = $entry->getAttributeNode ('revision')->getValue;
+
+        # Author, date and log message are children elements of the entry node
+        for my $key (qw/author date msg/) {
+          # Get data of each node, also convert date to seconds since epoch
+          my $node    = $entry->getElementsByTagName ($key)->item (0);
+          my $data    = ($node and $node->getFirstChild)
+                        ? $node->getFirstChild->getData : '';
+          $this{$key} = ($key eq 'date' ? str2time ($data) : $data);
+        }
+
+        # Path nodes are grand children elements of the entry node
+        my $paths = $entry->getElementsByTagName ('path');
+
+        for my $p (0 .. $paths->getLength - 1) {
+          # Select current path node from node list
+          my $node = $paths->item ($p);
+
+          # Get data from the path node
+          my $path = $node->getFirstChild->getData;
+          $this{paths}{$path} = {};
+
+          # Action, copyfrom-path and copyfrom-rev are attributes of path nodes
+          for my $key (qw/action copyfrom-path copyfrom-rev/) {
+            next unless $node->getAttributeNode ($key); # ensure attribute exists
+
+            $this{paths}{$path}{$key} = $node->getAttributeNode ($key)->getValue;
+          }
+        }
+
+        $self->{LOG}{$rev} = \%this;
+      }
+    }
+
+    # Update the range cache
+    # --------------------------------------------------------------------------
+    # Upper end of the range
+    $self->{LOG_RANGE}{UPPER} = $ranges [0]
+      if ! $self->{LOG_RANGE}{UPPER} or $ranges [0] > $self->{LOG_RANGE}{UPPER};
+
+    # Lower end of the range, need to take into account the stop-on-copy option
+    if ($stop_on_copy) {
+      # Lower end of the range with stop-on-copy option
+      $self->{LOG_RANGE}{LOWER_SOC} = $ranges [1]
+        if ! $self->{LOG_RANGE}{LOWER_SOC} or
+           $ranges [1] < $self->{LOG_RANGE}{LOWER_SOC};
+
+      my $low = (sort {$a <=> $b} keys %{ $self->{LOG} }) [0];
+      $self->{LOG_RANGE}{LOWER} = $low
+        if ! $self->{LOG_RANGE}{LOWER} or $low < $self->{LOG_RANGE}{LOWER};
+
+    } else {
+      # Lower end of the range without the stop-on-copy option
+      $self->{LOG_RANGE}{LOWER} = $ranges [1]
+        if ! $self->{LOG_RANGE}{LOWER} or
+           $ranges [1] < $self->{LOG_RANGE}{LOWER};
+
+      $self->{LOG_RANGE}{LOWER_SOC} = $ranges [1]
+        if ! $self->{LOG_RANGE}{LOWER_SOC} or
+           $ranges [1] < $self->{LOG_RANGE}{LOWER_SOC};
+    }
+  }
+
+  my %return = ();
+
+  if (! $rev_arg or ref ($rev_arg)) {
+    # REV is an array, return log entries if they are within range
+    for my $rev (sort {$b <=> $a} keys %{ $self->{LOG} }) {
+      next if $rev > $revs [0] or $revs [1] > $rev;
+
+      $return{$rev} = $self->{LOG}{$rev};
+
+      if ($stop_on_copy) {
+        last if exists $self->{LOG}{$rev}{paths}{$self->branch_path} and
+           $self->{LOG}{$rev}{paths}{$self->branch_path}{action} eq 'A';
+      }
+    }
+
+  } else {
+    # REV is a scalar, return log of the specified revision if it exists
+    %return = %{ $self->{LOG}{$revs [0]} } if exists $self->{LOG}{$revs [0]};
+  }
+
+  return %return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = $cm_branch->display_svnlog ($rev, [$wiki]);
+#
+# DESCRIPTION
+#   This method returns a string for displaying the log of the current branch
+#   at a $rev. If $wiki is set, returns a string for displaying in a Trac wiki
+#   table.  The value of $wiki should be the Subversion URL of a FCM project
+#   associated with the intended Trac system.
+# ------------------------------------------------------------------------------
+
+sub display_svnlog {
+  my ($self, $rev, $wiki) = @_;
+  my $return = '';
+
+  my %log = $self->svnlog (REV => $rev);
+
+  if ($wiki) {
+    # Output in Trac wiki format
+    # --------------------------------------------------------------------------
+    $return .= '|| ' . &svn_date ($log{date}) . ' || ' . $log{author} . ' || ';
+
+    my $trac_url = &get_browser_url (URL => $self->url);
+
+    # Get list of tickets from log
+    my @tickets;
+    while ($log{msg} =~ /(?:#|ticket:)(\d+)/g) {
+      push @tickets, $1;
+    }
+    @tickets = sort {$a <=> $b} @tickets;
+
+    if ($trac_url =~ m#^$wiki(?:/*|$)#) {
+      # URL is in the specified $wiki, use Trac link
+      $return .= '[' . $rev . '] ||';
+
+      for my $ticket (@tickets) {
+        $return .= ' #' . $ticket;
+      }
+
+      $return .= ' ||';
+
+    } else {
+      # URL is not in the specified $wiki, use full URL
+      my $rev_url = $trac_url;
+      $rev_url    =~ s#/browser/.*$##;
+      $rev_url   .= '/changeset/' . $rev;
+      $return    .= '[' . $rev_url . ' ' . $rev . '] ||';
+
+      my $ticket_url = $trac_url;
+      $ticket_url    =~ s#/browser/.*$#/ticket/#;
+
+      for my $ticket (@tickets) {
+        $return .= ' [' . $ticket_url . $ticket . ' ' . $ticket . ']';
+      }
+
+      $return .= ' ||';
+    }
+
+  } else {
+    # Output in plain text format
+    # --------------------------------------------------------------------------
+    my @msg  = split /\n/, $log{msg};
+    my $line = (@msg > 1 ? ' lines' : ' line');
+
+    $return .= join (
+      ' | ',
+      ('r' . $rev, $log{author}, &svn_date ($log{date}), scalar (@msg) . $line),
+    );
+    $return .= "\n\n";
+    $return .= $log{msg};
+  }
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @list = $cm_url->svnlist ([REV => $rev], [RECURSIVE => 1]);
+#
+# DESCRIPTION
+#   The method returns a list of paths as returned by "svn list". If RECURSIVE
+#   is set, "svn list" is invoked with the "-R" option.
+# ------------------------------------------------------------------------------
+
+sub svnlist {
+  my $self = shift;
+  my %args = @_;
+
+  my $recursive = exists $args{RECURSIVE} ? $args{RECURSIVE} : 0;
+  my $rev       = exists $args{REV}       ? $args{REV}       : undef;
+  my $key       = $recursive ? 'RLIST' : 'LIST';
+
+  # Find out last changed revision of the current URL
+  $rev = $self->svninfo (FLAG => 'Last Changed Rev', REV => $rev);
+  return () if not $rev;
+
+  # Get directory listing for the current URL at the last changed revision
+  if (not exists $self->{$key}{$rev}) {
+    my $rc;
+
+    my @list = map {chomp; $_} &run_command (
+      [qw/svn list -r/, $rev, ($recursive ? '-R' : ()), $self->url_peg],
+      METHOD => 'qx', ERROR => 'ignore', DEVNULL => 1, RC => \$rc,
+    );
+
+    $self->{$key}{$rev} = $rc ? undef : \@list;
+  }
+
+  return (defined ($self->{$key}{$rev}) ? @{ $self->{$key}{$rev} } : undef);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @list = $cm_url->branch_list ($rev);
+#
+# DESCRIPTION
+#   The method returns a list of branches in the current project, assuming the
+#   FCM naming convention. If $rev if specified, it returns the list of
+#   branches at $rev.
+# ------------------------------------------------------------------------------
+
+sub branch_list {
+  my ($self, $rev) = @_;
+
+  # Current URL must be a valid FCM project
+  return if not $self->project;
+
+  # Find out last changed revision of the current URL
+  $rev = $self->svninfo (FLAG => 'Revision', REV => $rev);
+  return () if not $rev;
+
+  if (not exists $self->{BRANCH_LIST}{$rev}) {
+    $self->{BRANCH_LIST}{$rev} = [];
+
+    # Get URL of the project "branches/" sub-directory
+    my $url = Fcm::CmUrl->new (URL => $self->project_url . '/branches');
+
+    # List three levels underneath "branches/"
+    # First level, i.e. dev, test, pkg, etc
+    my @list1 = map {$url->url . '/' . $_} $url->svnlist (REV => $rev);
+    @list1    = grep m#/$#, @list1;
+
+    # Second level, i.e. user name, Shared, Rel or Config
+    my @list2;
+    for (@list1) {
+      my $u    = Fcm::CmUrl->new (URL => $_);
+      my @list = $u->svnlist (REV => $rev);
+
+      push @list2, map {$u->url . $_} @list;
+    }
+
+    # Third level, branch name
+    for (@list2) {
+      my $u    = Fcm::CmUrl->new (URL => $_);
+      my @list = map {s#/*$##; $_} $u->svnlist (REV => $rev);
+
+      push @{ $self->{BRANCH_LIST}{$rev} }, map {$u->url . $_} @list;
+    }
+  }
+
+  return @{ $self->{BRANCH_LIST}{$rev} };
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_analyse_url ();
+#
+# DESCRIPTION
+#   The method analyses the current URL, breaking it up into the project
+#   (substring of URL up to the slash before "trunk", "branches" or "tags"),
+#   branch name ("trunk", "branches/<type>/<id>/<name>" or "tags/<name>") and
+#   the sub-directory below the top of the project sub-tree. It re-sets the
+#   corresponding interal variables.
+# ------------------------------------------------------------------------------
+
+sub _analyse_url {
+  my $self = shift;
+  my ($url, $project, $branch, $subdir, $pegrev);
+
+  # Check that URL is set
+  $url    = $self->url_peg;
+  return if not $url;
+  return if not $self->is_url;
+
+  # Extract from URL the peg revision
+  $pegrev = $1 if $url =~ s/@($rev_pattern)$//i;
+
+  if ($url =~ m#^(.*?)/+(trunk|branches|tags)(?:/+(.*))?/*$#) {
+    # URL is under the "trunk", a branch or a tag
+    $project                 = $1;
+    my ($branch_id, $remain) = ($2, $3);
+
+    $remain = '' if not defined $remain;
+
+    if ($branch_id eq 'trunk') {
+      # URL under the "trunk"
+      $branch = 'trunk';
+
+    } else {
+      # URL under a branch or a tag
+      $branch = $branch_id;
+
+      # Assume "3 sub-directories", FCM branch naming convention
+      for (1 .. 3) {
+        if ($remain =~ s#^([^/]+)(?:/+|$)##) {
+          $branch .=  '/' . $1;
+
+        } else {
+          $branch = undef;
+          last;
+        }
+      }
+    }
+
+    $subdir = $remain ? $remain : '' if $branch;
+
+  } else {
+    # URL is at some level above the "trunk", a branch or a tag
+    # Use "svn ls" to determine whether it is a project URL
+    my @list = $self->svnlist (REV => ($pegrev ? $pegrev : 'HEAD'));
+    my %lines = map {chomp $_; ($_, 1)} @list;
+
+    # A project URL should have the "trunk", "branches" and "tags" directories
+    ($project = $url) =~ s#/*$##
+      if $lines{'trunk/'} and $lines{'branches/'} and $lines{'tags/'};
+  }
+
+  $self->{PROJECT}  = $project;
+  $self->{BRANCH}   = $branch;
+  $self->{SUBDIR}   = $subdir;
+  $self->{PEGREV}   = $pegrev;
+  $self->{ANALYSED} = 1;
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $url = $cm_url->root ();
+#
+# DESCRIPTION
+#   The method returns the repository root of the current URL.
+# ------------------------------------------------------------------------------
+
+sub root {
+  my $self = shift;
+
+  return $self->svninfo (FLAG => 'Repository Root');
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $url = $cm_url->project_url_peg ();
+#   $cm_url->project_url_peg ($url);
+#
+# DESCRIPTION
+#   The method returns the URL@PEG of the "project" part of the current URL. If
+#   an argument is specified, the URL of the "project" part and the peg
+#   revision of the current URL are re-set.
+# ------------------------------------------------------------------------------
+
+sub project_url_peg {
+  my $self = shift;
+
+  if (@_) {
+    my $url = shift;
+
+    # Re-construct URL is necessary
+    if (! $self->project_url_peg or $url ne $self->project_url_peg) {
+      my $pegrev = ($url =~ s/@($rev_pattern)$//i) ? $1 : '';
+
+      $url .= '/' . $self->branch if $self->branch;
+      $url .= '/' . $self->subdir if $self->subdir;
+      $url .= '@' . $pegrev       if $pegrev;
+
+      $self->url_peg ($url);
+    }
+  }
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return $self->{PROJECT} . ($self->pegrev ? '@' . $self->pegrev : '');
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $url = $cm_url->project_url ();
+#   $cm_url->project_url ($url);
+#
+# DESCRIPTION
+#   The method returns the URL of the "project" part of the current URL. If an
+#   argument is specified, the URL of the "project" part of the current URL is
+#   re-set.
+# ------------------------------------------------------------------------------
+
+sub project_url {
+  my $self = shift;
+
+  if (@_) {
+    my $url = shift;
+    $url =~ s/@($rev_pattern)$//i;
+
+    # Re-construct URL is necessary
+    if (! $self->project_url or $url ne $self->project_url) {
+      $url .= '/' . $self->branch if $self->branch;
+      $url .= '/' . $self->subdir if $self->subdir;
+
+      $self->url ($url);
+    }
+  }
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return $self->{PROJECT};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $path = $cm_url->project_path ();
+#   $cm_url->project_path ($path);
+#
+# DESCRIPTION
+#   The method returns the path of the "project" part of the current URL. If an
+#   argument is specified, the path of the "project" part of the current URL is
+#   re-set.
+# ------------------------------------------------------------------------------
+
+sub project_path {
+  my $self = shift;
+
+  # Repository root
+  my $root = $self->root;
+  $root    = substr (
+    $self->project_url,
+    0,
+    length ($self->project_url) - length ($self->project) - 1
+  ) if not $root;
+
+  if (@_) {
+    my $path = shift;
+
+    # Re-construct URL is necessary
+    if (! $self->project_path or $path ne $self->project_path) {
+      $path .= '/' . $self->branch if $self->branch;
+      $path .= '/' . $self->subdir if $self->subdir;
+
+      $self->path ($path);
+    }
+  }
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return substr ($self->{PROJECT}, length ($root));
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $name = $cm_url->project ();
+#   $cm_url->project ($name);
+#
+# DESCRIPTION
+#   The method returns the basename of the "project" part of the current URL.
+#   If an argument is specified, the basename of the "project" part of the
+#   current URL is re-set.
+# ------------------------------------------------------------------------------
+
+sub project {
+  my $self = shift;
+
+  if (@_) {
+    my $name = shift;
+
+    # Re-construct URL is necessary
+    if (! $self->project or $name ne $self->project) {
+      my $url = '';
+      if ($self->project) {
+        $url =  $self->project;
+        $url =~ s#/[^/]+$##;
+
+      } else {
+        $url =  $self->root;
+      }
+
+      $url .=  '/' . $name;
+      $url .=  '/' . $self->branch if $self->branch;
+      $url .=  '/' . $self->subdir if $self->subdir;
+      $url .=  '@' . $self->pegrev if $self->pegrev;
+
+      $self->url_peg ($url);
+    }
+  }
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  my $name = $self->{PROJECT};
+  $name =~ s#^.*/([^/]+)$#$1# if $name;
+
+  return $name;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $url = $cm_url->branch_url_peg ();
+#   $cm_url->branch_url_peg ($url);
+#
+# DESCRIPTION
+#   The method returns the URL@PEG of the "branch" part of the current URL. If
+#   an argument is specified, the URL@PEG of the "branch" part of the current
+#   URL is re-set.
+# ------------------------------------------------------------------------------
+
+sub branch_url_peg {
+  my $self = shift;
+
+  if (@_) {
+    my $url = shift;
+
+    # Re-construct URL is necessary
+    if (! $self->branch_url_peg or $url ne $self->branch_url_peg) {
+      my $pegrev = ($url =~ s/@($rev_pattern)$//i) ? $1 : '';
+
+      $url .= '/' . $self->subdir if $self->subdir;
+      $url .= '@' . $pegrev       if $pegrev;
+
+      $self->url_peg ($url);
+    }
+  }
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return $self->project_url . '/' . $self->branch .
+         ($self->pegrev ? '@' . $self->pegrev : '');
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $url = $cm_url->branch_url ();
+#   $cm_url->branch_url ($url);
+#
+# DESCRIPTION
+#   The method returns the URL of the "branch" part of the current URL. If an
+#   argument is specified, the URL of the "branch" part of the current URL is
+#   re-set.
+# ------------------------------------------------------------------------------
+
+sub branch_url {
+  my $self = shift;
+
+  if (@_) {
+    my $url = shift;
+    $url =~ s/@($rev_pattern)$//i;
+
+    # Re-construct URL is necessary
+    if (! $self->branch_url or $url ne $self->branch_url) {
+      $url .= '/' . $self->subdir if $self->subdir;
+
+      $self->url ($url);
+    }
+  }
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return $self->project_url . '/' . $self->branch;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $path = $cm_url->branch_path ();
+#   $cm_url->branch_path ($path);
+#
+# DESCRIPTION
+#   The method returns the path of the "branch" part of the current URL. If an
+#   argument is specified, the path of the "branch" part of the current URL is
+#   re-set.
+# ------------------------------------------------------------------------------
+
+sub branch_path {
+  my $self = shift;
+
+  if (@_) {
+    my $path = shift;
+
+    # Re-construct URL is necessary
+    if (! $self->branch_path or $path ne $self->branch_path) {
+      $path .= '/' . $self->subdir if $self->subdir;
+
+      $self->path ($path);
+    }
+  }
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return $self->project_path . '/' . $self->branch;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $branch = $cm_url->branch ();
+#   $cm_url->branch ($branch);
+#
+# DESCRIPTION
+#   The method returns the "branch" part of the current URL. If an argument is
+#   specified, the "branch" part of the current URL is re-set.
+# ------------------------------------------------------------------------------
+
+sub branch {
+  my $self = shift;
+
+  if (@_) {
+    my $branch = shift;
+
+    # Re-construct URL is necessary
+    if (! $self->branch or $branch ne $self->branch) {
+      my $url = $self->project_url;
+      $url   .= '/' . $branch;
+      $url   .= '/' . $self->subdir if $self->subdir;
+
+      $self->url ($url);
+    }
+  }
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return $self->{BRANCH};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $cm_url->is_trunk ();
+#
+# DESCRIPTION
+#   The method returns true if the the current URL is (a sub-tree of) the trunk.
+# ------------------------------------------------------------------------------
+
+sub is_trunk {
+  my $self = shift;
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return ($self->branch and $self->branch eq 'trunk');
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $cm_url->is_branch ();
+#
+# DESCRIPTION
+#   The method returns true if the the current URL is (a sub-tree of) a branch.
+# ------------------------------------------------------------------------------
+
+sub is_branch {
+  my $self = shift;
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return ($self->branch and $self->branch =~ m#^branches/#);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $cm_url->is_tag ();
+#
+# DESCRIPTION
+#   The method returns true if the the current URL is (a sub-tree of) a tag.
+# ------------------------------------------------------------------------------
+
+sub is_tag {
+  my $self = shift;
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return ($self->branch and $self->branch =~ m#^tags/#);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $subdir = $cm_url->subdir ();
+#   $cm_url->subdir ($subdir);
+#
+# DESCRIPTION
+#   The method returns the "subdir" part of the current URL. If an argument is
+#   specified, the "subdir" part of the current URL is re-set.
+# ------------------------------------------------------------------------------
+
+sub subdir {
+  my $self = shift;
+
+  if (@_) {
+    my $subdir = shift;
+
+    # Re-construct URL is necessary
+    if (! $self->subdir or $subdir ne $self->subdir) {
+      my $url = $self->project_url;
+      $url   .= '/' . $self->branch if $self->branch;
+      $url   .= '/' . $subdir if $subdir;
+
+      $self->url ($url);
+    }
+  }
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return $self->{SUBDIR};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $url = $cm_url->url ();
+#   $cm_url->url ($url);
+#
+# DESCRIPTION
+#   The method returns the URL without the "peg revision" part. If an argument
+#   is specified, the URL is re-set without modifying the "peg revision" part.
+# ------------------------------------------------------------------------------
+
+sub url {
+  my $self = shift;
+
+  if (@_) {
+    my $url = shift;
+    $url    =~ s/@($rev_pattern)$//i;
+
+    # Re-construct URL if necessary
+    if (! $self->url or $url ne $self->url) {
+      $self->url_peg ($url . ($self->pegrev ? '@' . $self->pegrev : ''));
+    }
+  }
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  (my $url = $self->url_peg) =~ s/@($rev_pattern)$//i;
+
+  return $url;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $path = $cm_url->path ();
+#   $cm_url->path ($path);
+#
+# DESCRIPTION
+#   The method returns the "path" part of the URL (i.e. URL without the
+#   "root" part). If an argument is specified, the "path" part of the URL is
+#   re-set.
+# ------------------------------------------------------------------------------
+
+sub path {
+  my $self = shift;
+
+  # Repository root
+  my $root = $self->root;
+  $root    = substr (
+    $self->project_url,
+    0,
+    length ($self->project_url) - length ($self->project) - 1
+  ) if not $root;
+
+  if (@_) {
+    my $path = shift;
+    $path    =~ s/@($rev_pattern)$//i;
+
+    # Re-construct URL is necessary
+    if (! $self->path or $path ne $self->path) {
+      my $url = ($root . (substr ($path, 0, 1) eq '/' ? '' : '/') . $path);
+      $self->url ($url);
+    }
+  }
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return substr ($self->url, length ($root));
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $path = $cm_url->path_peg ();
+#   $cm_url->path_peg ($path);
+#
+# DESCRIPTION
+#   The method returns the PATH@PEG part of the URL (i.e. URL without the
+#   "root" part). If an argument is specified, the PATH@PEG part of the URL is
+#   re-set.
+# ------------------------------------------------------------------------------
+
+sub path_peg {
+  my $self = shift;
+
+  # Repository root
+  my $root = $self->root;
+  $root    = substr (
+    $self->project_url,
+    0,
+    length ($self->project_url) - length ($self->project) - 1
+  ) if not $root;
+
+  if (@_) {
+    my $path = shift;
+
+    # Re-construct URL is necessary
+    if (! $self->path_peg or $path ne $self->path_peg) {
+      my $url = ($root . (substr ($path, 0, 1) eq '/' ? '' : '/') . $path);
+      $self->url_peg ($url);
+    }
+  }
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return substr ($self->url_peg, length ($root));
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rev = $cm_url->pegrev ();
+#   $cm_url->pegrev ($rev);
+#
+# DESCRIPTION
+#   The method returns the "peg revision" part of the current URL. If an
+#   argument is specified, the "peg revision" part of the current URL is
+#   re-set.
+# ------------------------------------------------------------------------------
+
+sub pegrev {
+  my $self = shift;
+
+  if (@_) {
+    my $pegrev = shift;
+
+    # Re-construct URL is necessary
+    if (! $self->pegrev or $pegrev ne $self->pegrev) {
+      $self->url_peg ($self->url . ($pegrev ? '@' . $pegrev : ''));
+    }
+  }
+
+  $self->_analyse_url () if not $self->{ANALYSED};
+
+  return $self->{PEGREV};
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/Config.pm
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/Config.pm	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/Config.pm	(revision 5058)
@@ -0,0 +1,735 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::Config
+#
+# DESCRIPTION
+#   This is a class for reading and processing central and user configuration
+#   settings for FCM.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+package Fcm::Config;
+
+# Standard pragma
+use warnings;
+use strict;
+
+# Standard modules
+use File::Basename;
+use File::Spec::Functions;
+use FindBin;
+use POSIX qw/setlocale LC_ALL/;
+
+# FCM component modules
+use Fcm::CfgFile;
+
+# Other declarations:
+sub _get_hash_value;
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $config = Fcm::Config->new (VERBOSE => $verbose);
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::Config class.
+#
+# ARGUMENTS
+#   VERBOSE - Set the verbose level of diagnostic output
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  # Ensure that all subsequent Subversion output is in UK English
+  if (setlocale (LC_ALL, 'en_GB')) {
+    $ENV{LANG} = 'en_GB';
+  }
+
+  # Location of the central/user configuration file
+  my $cntl_config = catfile (dirname ($FindBin::Bin), 'etc', 'fcm.cfg');
+  $cntl_config    = catfile $FindBin::Bin, 'fcm.cfg' unless -r $cntl_config;
+  my $user_config = exists $ENV{HOME} ? catfile $ENV{HOME}, '.fcm' : '';
+
+  # Verbose mode
+  my $verbose     = exists $ENV{FCM_VERBOSE} ? $ENV{FCM_VERBOSE} : 1;
+
+  my $self = {
+    CNTL_CONFIG => -r $cntl_config ? $cntl_config : '',
+    USER_CONFIG => -r $user_config ? $user_config : '',
+    VERBOSE     => exists $args{VERBOSE} ? $args{VERBOSE} : $verbose,
+    VARIABLE    => {},
+
+    # Primary settings
+    SETTING => {
+      # Release identifier/version
+      RELEASE  => '1.2',
+
+      # Location of file with the last changed revision of the FCM trunk
+      REV_FILE => catfile (dirname ($FindBin::Bin), 'etc', 'fcm_rev'),
+
+      # Default names of known FCM configuration files
+      CFG_NAME => {
+        BLD        => 'bld.cfg',      # bld cfg
+        EXT        => 'ext.cfg',      # ext cfg
+        SRCPACKAGE => '@PACKAGE.cfg', # source package cfg
+      },
+
+      # Latest version of known FCM configuration files
+      CFG_VERSION => {
+        BLD        => '1.0', # bld cfg
+        EXT        => '1.0', # ext cfg
+        SRCPACKAGE => '1.0', # source package cfg
+      },
+
+      # Labels for all types of FCM configuration files
+      CFG_LABEL => {
+        CFGFILE => {
+          TYPE    => 'CFG::TYPE',       # config file type
+          VERSION => 'CFG::VERSION',    # version of config file syntax
+        },
+
+        # Labels for central/user internal config setting
+        SETTING    => 'SET',
+
+        # Labels for ext and bld cfg
+        USE        => 'USE',            # use (inherit from) another ext/bld
+        SRCDIR     => 'SRC',            # prefix, source directory
+
+        # Labels for bld and pck cfg
+        TARGET     => 'TARGET',         # BLD: targets of current build
+                                        # PCK: target name of source file
+
+        # Labels for bld cfg
+        NAME        => 'NAME',          # build name
+        DIR         => 'DIR',           # prefix, build directory
+        PP          => 'PP',            # prefix, pre-process?
+        LIB         => 'LIB',           # declare name of a library
+        SEARCH_SRC  => 'SEARCH_SRC',    # search src/ sub-directory?
+        EXE_NAME    => 'EXE_NAME',      # rename a main program target
+        TOOL        => 'TOOL',          # prefix, build tool
+        INHERIT     => 'INHERIT',       # prefix, inheritance flag
+        EXCL_DEP    => 'EXCL_DEP',      # exclude these automatic dependencies
+        INFILE_EXT  => 'INFILE_EXT',    # input file name extension and type
+        OUTFILE_EXT => 'OUTFILE_EXT',   # output file type and name extension
+        EXE_DEP     => 'EXE_DEP',       # extra executable dependencies
+        BLOCKDATA   => 'BLOCKDATA',     # BLOCKDATA dependencies
+
+        # Labels for ext cfg
+        DEST       => {                 # local extract destinations
+          ROOTDIR  => 'DEST::ROOTDIR',  # top directory for this extract
+          SRCDIR   => 'DEST::SRCDIR',   # extracted source directory
+          CFGDIR   => 'DEST::CFGDIR',   # generated configuration directory
+          CACHEDIR => 'DEST::CACHEDIR', # cache directory for fast extract
+          BLD_CFG  => 'DEST::BLD_CFG',  # generated bld cfg file
+          EXT_CFG  => 'DEST::EXT_CFG',  # generated ext cfg file
+        },
+        RDEST      => {                 # remote extract destionations
+          MACHINE  => 'RDEST::MACHINE', # name of remote machine
+          LOGNAME  => 'RDEST::LOGNAME', # user logname on remote machine
+          ROOTDIR  => 'RDEST::ROOTDIR', # top directory for this extract
+          SRCDIR   => 'RDEST::SRCDIR',  # extracted source directory
+          CFGDIR   => 'RDEST::CFGDIR',  # generated configuration directory
+          BLD_CFG  => 'RDEST::BLD_CFG', # generated bld cfg file
+          EXT_CFG  => 'RDEST::EXT_CFG', # generated ext cfg file
+        },
+        INC        => 'INC',            # "include" settings in another cfg file
+        BDECLARE   => 'BLD',            # declare entries for build system
+        OVERRIDE   => 'OVERRIDE',       # set conflict override option
+        REPOS      => 'REPOS',          # set repos loc for a project branch
+        VERSION    => 'VERSION',        # set version for a project branch
+        EXPSRCDIR  => 'EXPSRC',         # prefix, expandable source directory
+        MIRROR     => 'MIRROR',         # mirror tool
+
+        # Labels for pck cfg
+        TYPE       => 'TYPE',           # type of source file/build task
+        SCAN       => 'SCAN',           # scan source file for dependency
+        INTNAME    => 'INTNAME',        # internal name of source file
+        DEP        => 'DEP',            # source file/build task dependencies
+      },
+
+      # Keywords in known FCM configuration files
+      CFG_KEYWORD => 'USE,INC,TARGET,EXCL_DEP',
+
+      # Types of "inc" statements expandable CFG files
+      CFG_EXP_INC => 'BLD,EXT,FCM',
+
+      # Standard sub-directories for extract/build
+      DIR => {
+        BIN    => 'bin',    # executable
+        BLD    => 'bld',    # build
+        CACHE  => '.cache', # cache
+        CFG    => 'cfg',    # configuration
+        DONE   => 'done',   # "done"
+        ETC    => 'etc',    # miscellaneous items
+        FLAGS  => 'flags',  # "flags"
+        INC    => 'inc',    # include
+        LIB    => 'lib',    # library
+        OBJ    => 'obj',    # object
+        PPSRC  => 'ppsrc',  # pre-processed source
+        SRC    => 'src',    # source
+        TMP    => 'tmp',    # temporary directory
+      },
+
+      # Build commands and options (i.e. tools)
+      TOOL => {
+        SHELL        => '/usr/bin/ksh',    # Default shell
+
+        CPP          => 'cpp',             # C pre-processor
+        CPPFLAGS     => '-C',              # CPP flags
+        CPP_INCLUDE  => '-I',              # CPP flag, specify "include" path
+        CPP_DEFINE   => '-D',              # CPP flag, define macro
+        CPPKEYS      => '',                # CPP keys (definition macro)
+
+        CC           => 'cc',              # C compiler
+        CFLAGS       => '',                # CC flags
+        CC_COMPILE   => '-c',              # CC flag, compile only
+        CC_OUTPUT    => '-o',              # CC flag, specify output file name
+        CC_INCLUDE   => '-I',              # CC flag, specify "include" path
+        CC_DEFINE    => '-D',              # CC flag, define macro
+
+        FPP          => 'cpp',             # Fortran pre-processor
+        FPPFLAGS     => '-P -traditional', # FPP flags
+        FPP_INCLUDE  => '-I',              # FPP flag, specify "include" path
+        FPP_DEFINE   => '-D',              # FPP flag, define macro
+        FPPKEYS      => '',                # FPP keys (definition macro)
+
+        FC           => 'f90',             # Fortran compiler
+        FFLAGS       => '',                # FC flags
+        FC_COMPILE   => '-c',              # FC flag, compile only
+        FC_OUTPUT    => '-o',              # FC flag, specify output file name
+        FC_INCLUDE   => '-I',              # FC flag, specify "include" path
+        FC_DEFINE    => '-D',              # FC flag, define macro
+
+        LD           => 'ld',              # linker
+        LDFLAGS      => '',                # LD flags
+        LD_OUTPUT    => '-o',              # LD flag, specify output file name
+        LD_LIBSEARCH => '-L',              # LD flag, specify "library" path
+        LD_LIBLINK   => '-l',              # LD flag, specify link library
+
+        AR           => 'ar',              # library archiver
+        ARFLAGS      => 'rs',              # AR flags
+
+        MAKE         => 'make',            # make command
+        MAKEFLAGS    => '',                # make flags
+        MAKE_SILENT  => '-s',              # make flag, silent diagnostic
+        MAKE_JOB     => '-j',              # make flag, number of jobs
+
+        INTERFACE    => 'file',            # name interface after file/program
+        GENINTERFACE => 'ECMWF',           # Fortran 9x interface generator
+
+        MIRROR       => 'rsync',           # extract mirroring tool
+        REMOTE_SHELL => 'remsh',           # command to invoke the remote shell
+        GRAPHIC_DIFF => 'xxdiff',          # graphical diff tool
+      },
+
+      # List of tools that are local to FCM, (will not be exported to a Makefile)
+      LOCALTOOL => 'CPP,CPPFLAGS,CPP_INCLUDE,CPP_DEFINE,FPP,FPPFLAGS,' .
+                   'FPP_INCLUDE,FPP_DEFINE,GRAPHIC_DIFF,MAKE,MAKEFLAGS,' .
+                   'MAKE_SILENT,MAKE_JOB,INTERFACE,GENINTERFACE,MIRROR,' .
+                   'REMOTE_SHELL',
+
+      # Supported tools for compilable source
+      SRC_TOOL => {
+        FORTRAN => {
+          COMPILER => 'FC',
+          FLAGS    => 'FFLAGS',
+          OUTPUT   => 'FC_OUTPUT',
+          INCLUDE  => 'FC_INCLUDE',
+        },
+
+        FPP     => {
+          COMPILER => 'FC',
+          FLAGS    => 'FFLAGS',
+          PPKEYS   => 'FPPKEYS',
+          OUTPUT   => 'FC_OUTPUT',
+          INCLUDE  => 'FC_INCLUDE',
+          DEFINE   => 'FC_DEFINE',
+        },
+
+        C       => {
+          COMPILER => 'CC',
+          FLAGS    => 'CFLAGS',
+          PPKEYS   => 'CPPKEYS',
+          OUTPUT   => 'CC_OUTPUT',
+          INCLUDE  => 'CC_INCLUDE',
+          DEFINE   => 'CC_DEFINE',
+        },
+      },
+
+      # Cache file names/extensions
+      CACHE => {
+        EXTCONFIG   => '.config',       # ext cache, commit version info
+        PCKFILE     => '.pck_file',     # bld cache, source package list
+        PCKPPDEPEND => '.pck_ppdepend', # bld cache, source package PP dependency
+        PCKDEPEND   => '.pck_depend',   # bld cache, source package dependency
+        BLDTOOL     => '.bld_tool',     # bld cache, build tool list
+        PPOPTION    => '.bld_pp',       # bld cache, PP option
+        EXE_DEP     => '.exe_dep',      # bld cache, executable extra dependency
+      },
+
+      # Input file name extension and type
+      # (may overlap with output and vpath, see below)
+      INFILE_EXT => {
+        # General extensions
+        'f'    => 'FORTRAN::SOURCE',
+        'for'  => 'FORTRAN::SOURCE',
+        'ftn'  => 'FORTRAN::SOURCE',
+        'f77'  => 'FORTRAN::SOURCE',
+        'f90'  => 'FORTRAN::FORTRAN9X::SOURCE',
+        'f95'  => 'FORTRAN::FORTRAN9X::SOURCE',
+        'F'    => 'FPP::SOURCE',
+        'FOR'  => 'FPP::SOURCE',
+        'FTN'  => 'FPP::SOURCE',
+        'F77'  => 'FPP::SOURCE',
+        'F90'  => 'FPP::FPP9X::SOURCE',
+        'F95'  => 'FPP::FPP9X::SOURCE',
+        'c'    => 'C::SOURCE',
+        'cpp'  => 'C::C++::SOURCE',
+        'h'    => 'CPP::INCLUDE',
+        'o'    => 'BINARY::OBJ',
+        'obj'  => 'BINARY::OBJ',
+        'exe'  => 'BINARY::EXE',
+        'a'    => 'BINARY::LIB',
+        'sh'   => 'SHELL::SCRIPT',
+        'ksh'  => 'SHELL::SCRIPT',
+        'bash' => 'SHELL::SCRIPT',
+        'csh'  => 'SHELL::SCRIPT',
+        'pl'   => 'PERL::SCRIPT',
+        'pm'   => 'PERL::SCRIPT',
+        'py'   => 'PYTHON::SCRIPT',
+        'tcl'  => 'TCL::SCRIPT',
+        'pro'  => 'PVWAVE::SCRIPT',
+
+        # Local extensions
+        'cfg'       => 'CFGFILE',
+        'h90'       => 'CPP::INCLUDE',
+        'inc'       => 'FORTRAN::FORTRAN9X::INCLUDE',
+        'interface' => 'FORTRAN::FORTRAN9X::INCLUDE::INTERFACE',
+      },
+
+      # Input file name pattern and type
+      INFILE_PAT => {
+        '\w+Scr_\w+'              => 'SHELL::SCRIPT',
+        '\w+Comp_\w+'             => 'SHELL::SCRIPT::GENTASK',
+        '\w+(?:IF|Interface)_\w+' => 'SHELL::SCRIPT::GENIF',
+        '\w+Suite_\w+'            => 'SHELL::SCRIPT::GENSUITE',
+        '\w+List_\w+'             => 'SHELL::SCRIPT::GENLIST',
+        '\w+Sql_\w+'              => 'SCRIPT::SQL',
+      },
+
+      # Input text file pattern and type
+      INFILE_TXT => {
+        '(?:[ck]|ba)?sh'  => 'SHELL::SCRIPT',
+        'perl'            => 'PERL::SCRIPT',
+        'python'          => 'PYTHON::SCRIPT',
+        'tcl(?:sh)?|wish' => 'TCL::SCRIPT',
+      },
+
+      # Ignore input files matching the following names (comma-separated list)
+      INFILE_IGNORE => 'fcm_env.ksh',
+
+      # Output file type and extension
+      # (may overlap with input (above) and vpath (below))
+      OUTFILE_EXT => {
+        CFG       => '.cfg',       # FCM configuration file
+        DONE      => '.done',      # "done" files for compiled source
+        ETC       => '.etc',       # "etc" dummy file
+        EXE       => '.exe',       # binary executables
+        FLAGS     => '.flags',     # "flags" files, compiler flags config
+        IDONE     => '.idone',     # "done" files for included source
+        INTERFACE => '.interface', # interface for F90 subroutines/functions
+        LIB       => '.a',         # archive object library
+        MK        => '.mk',        # dependency files, Makefile fragments
+        MOD       => '.mod',       # compiled Fortran module information files
+        OBJ       => '.o',         # compiled object files
+        PDONE     => '.pdone',     # "done" files for pre-processed files
+        TAR       => '.tar',       # TAR archive
+      },
+
+      # VPATH, each value must be a comma separate list
+      # EMPTY      translates to %
+      # IN:<FLAG>  translates to any key in {INFILE_EXT} if the value contains
+      #            the word in <FLAG>
+      # OUT:<FLAG> translates to {OUTFILE_EXT}{<FLAG>}
+      VPATH   => {
+        BIN   => 'EMPTY,OUT:EXE,IN:SCRIPT',
+        BLD   => 'OUT:MK',
+        DONE  => 'OUT:DONE,OUT:IDONE,OUT:ETC',
+        FLAGS => 'OUT:FLAGS',
+        INC   => 'IN:INCLUDE',
+        LIB   => 'OUT:LIB',
+        OBJ   => 'OUT:OBJ',
+      },
+
+      # Dependency scan types for pre-processing
+      PP_DEP_TYPE => {
+        FPP => 'H',
+        CPP => 'H',
+        C   => 'H',
+      },
+
+      # Dependency scan types
+      DEP_TYPE => {
+        FORTRAN => 'USE::INTERFACE::INC::OBJ',
+        FPP     => 'USE::INTERFACE::INC::H::OBJ',
+        CPP     => 'H::OBJ',
+        C       => 'H::OBJ',
+        SCRIPT  => 'EXE',
+      },
+
+      # Dependency pattern for each type
+      DEP_PATTERN => {
+        H         => q/^#\s*include\s*['"](\S+)['"]/,
+        USE       => q/^\s*use\s+(\w+)/,
+        INTERFACE => q/^#?\s*include\s+['"](\S+##OUTFILE_EXT::INTERFACE##)['"]/,
+        INC       => q/^\s*include\s+['"](\S+)['"]/,
+        OBJ       => q#^\s*(?:/\*|!)\s*depends\s*on\s*:\s*(\S+)#,
+        EXE       => q/^\s*(?:#|;)\s*(?:calls|list|if|interface)\s*:\s*(\S+)/,
+      },
+
+      # Types that always need to be built
+      ALWAYS_BUILD_TYPE => 'PVWAVE,GENLIST,SQL',
+
+      # Types that cannot have duplicated targets
+      NO_DUPLICATED_TARGET_TYPE => '',
+
+      # Excluded dependency
+      EXCL_DEP => {
+        # Fortran intrinsic modules
+        'USE::ISO_C_BINDING'            => {'' => 1},
+        'USE::IEEE_EXCEPTIONS'          => {'' => 1},
+        'USE::IEEE_ARITHMETIC'          => {'' => 1},
+        'USE::IEEE_FEATURES'            => {'' => 1},
+
+        # Fortran intrinsic subroutines
+        'OBJ::CPU_TIME'                 => {'' => 1},
+        'OBJ::GET_COMMAND'              => {'' => 1},
+        'OBJ::GET_COMMAND_ARGUMENT'     => {'' => 1},
+        'OBJ::GET_ENVIRONMENT_VARIABLE' => {'' => 1},
+        'OBJ::MOVE_ALLOC'               => {'' => 1},
+        'OBJ::MVBITS'                   => {'' => 1},
+        'OBJ::RANDOM_NUMBER'            => {'' => 1},
+        'OBJ::RANDOM_SEED'              => {'' => 1},
+        'OBJ::SYSTEM_CLOCK'             => {'' => 1},
+
+        # Dummy statements
+        'OBJ::NONE'                     => {'' => 1},
+        'EXE::NONE'                     => {'' => 1},
+      },
+
+      # Extra executable dependencies
+      EXE_DEP => {},
+
+      # Fortran BLOCKDATA dependencies
+      BLOCKDATA => {},
+
+      # Rename main program targets
+      EXE_NAME => {},
+
+      # Build sub-directories that can be archived by "tar"
+      TAR_DIRS => 'BLD,CACHE,DONE,FLAGS,INC,PPSRC,OBJ',
+
+      # Misc
+      MISC => {
+        CPDUMMY       => '$(FCM_DONEDIR)/FCM_CP.dummy',
+                                         # build system "copy" dummy target
+        DIR_SEPARATOR => '/',            # repository directory separator
+        EXPURL_PREFIX => 'fcm:',         # expandable URL keyword prefix
+        LOCK_BLD      => 'fcm.bld.lock', # build lock file
+        LOCK_EXT      => 'fcm.ext.lock', # extract lock file
+        MAKEFILE      => 'Makefile',     # name of Makefile
+        RUN_ENV_SH    => 'fcm_env.ksh',  # bld runtime environment shell script
+        WEB_BROWSER   => 'firefox',      # web browser
+      },
+
+      # URL, revision, and Trac URL keywords
+      URL      => {},
+      REVISION => {},
+      TRAC     => {},
+    },
+  };
+
+  # Backward compatibility: the REPOS setting is equivalent to the URL setting
+  $self->{SETTING}{REPOS} = $self->{SETTING}{URL};
+
+  bless $self, $class;
+  return $self;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $file = $config->central_config ();
+#   $config->central_config ($file);
+#
+# DESCRIPTION
+#   This method returns the path name of the central configuration file. If an
+#   argument $file is specified, the path name of the central configuration
+#   file is set to its value.
+# ------------------------------------------------------------------------------
+
+sub central_config {
+  my $self = shift;
+
+  if (@_) {
+    $self->{CNTL_CONFIG} = $_[0];
+  }
+
+  return $self->{CNTL_CONFIG};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $file = $config->user_config ();
+#   $config->user_config ($file);
+#
+# DESCRIPTION
+#   This method returns the path name of the user configuration file. If an
+#   argument $file is specified, the path name of the user configuration file
+#   is set to its value.
+# ------------------------------------------------------------------------------
+
+sub user_config {
+  my $self = shift;
+
+  if (@_) {
+    $self->{USER_CONFIG} = $_[0];
+  }
+
+  return $self->{USER_CONFIG};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $mode = $config->verbose ();
+#   $config->verbose ($mode);
+#
+# DESCRIPTION
+#   This method returns the diagnostic verbose level. If an argument $mode is
+#   specified, the diagnostic verbose level is set to its value.
+# ------------------------------------------------------------------------------
+
+sub verbose {
+  my $self = shift;
+
+  if (@_) {
+    $self->{VERBOSE} = $_[0];
+  }
+
+  return $self->{VERBOSE};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $setting = $config->setting (arg, [...]);
+#
+# DESCRIPTION
+#   This method returns an item under the SETTING hash table. The depth within
+#   the hash table is given by the list of arguments, which should match with
+#   the keys in the multi-dimension SETTING hash table.
+# ------------------------------------------------------------------------------
+
+sub setting {
+  my $self = shift;
+
+  if (@_) {
+    my $label   = shift;
+    my $setting = $self->{SETTING};
+    return _get_hash_value ($setting->{$label}, @_) if exists $setting->{$label};
+  }
+
+  return undef;
+
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $config->assign_setting (
+#     LABELS => \@labels, # setting labels
+#     VALUE  => $value,   # setting value
+#   );
+#
+# DESCRIPTION
+#   This method assigns a VALUE to a SETTING specified by the names in LABEL.
+# ------------------------------------------------------------------------------
+
+sub assign_setting {
+  my $self = shift;
+  my %args = @_;
+
+  my @labels = exists $args{LABELS} ? @{ $args{LABELS} } : ();
+  my $value  = exists $args{VALUE}  ? $args{VALUE}       : undef;
+
+  my $setting = $self->{SETTING};
+  while (defined (my $label = shift @labels)) {
+    if (exists $setting->{$label}) {
+      if (ref $setting->{$label}) {
+        $setting = $setting->{$label};
+
+      } else {
+        $setting->{$label} = $value;
+        last;
+      }
+
+    } else {
+      if (@labels) {
+        $setting->{$label} = {};
+        $setting           = $setting->{$label};
+
+      } else {
+        $setting->{$label} = $value;
+      }
+    }
+  }
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $variable = $config->variable ([arg]);
+#
+# DESCRIPTION
+#   If arg is set, this method returns the value of a variable named arg. If
+#   arg is not set, this method returns the VARIABLE hash.
+# ------------------------------------------------------------------------------
+
+sub variable {
+  my $self     = shift;
+
+  my $variable = $self->{VARIABLE};
+
+  if (@_) {
+    my $label   = shift;
+    return exists $variable->{$label} ? $variable->{$label} : undef;
+
+  } else {
+    return %{ $variable };
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $config->assign_variable (
+#     LABEL => $label, # variable label
+#     VALUE => $value, # variable value
+#   );
+#
+# DESCRIPTION
+#   This method assigns a VALUE to a VARIABLE named by LABEL.
+# ------------------------------------------------------------------------------
+
+sub assign_variable {
+  my $self = shift;
+  my %args = @_;
+
+  my $label = exists $args{LABEL} ? $args{LABEL} : undef;
+  my $value = exists $args{VALUE} ? $args{VALUE} : undef;
+
+  if ($label) {
+    $self->{VARIABLE}{$label} = $value;
+  }
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $config->get_config ();
+#
+# DESCRIPTION
+#   This method reads the configuration settings from the central and the user
+#   configuration files.
+# ------------------------------------------------------------------------------
+
+sub get_config {
+  my $self = shift;
+
+  $self->_read_config_file ($self->{CNTL_CONFIG}) if $self->{CNTL_CONFIG};  
+  $self->_read_config_file ($self->{USER_CONFIG}) if $self->{USER_CONFIG};
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $config->_read_config_file ();
+#
+# DESCRIPTION
+#   This internal method reads a configuration file and assign values to the
+#   attributes of the current instance.
+# ------------------------------------------------------------------------------
+
+sub _read_config_file {
+  my $self        = shift;
+  my $config_file = $_[0];
+
+  return undef unless -r $config_file;
+
+  my $cfgfile = Fcm::CfgFile->new (SRC => $config_file, TYPE => 'FCM');
+  $cfgfile->read_cfg ();
+  my @lines = $cfgfile->lines ();
+
+  LINE: for my $line (@lines) {
+    my $label = $line->{LABEL};
+    my $value = $line->{VALUE};
+
+    next unless $label;
+
+    # "Environment variables" start with $
+    if ($label =~ s/^\$([A-Za-z_]\w*)$/$1/) {
+      $ENV{$label} = $value;
+      next LINE;
+    }
+
+    # "Settings variables" start with "set::"
+    my @tags = map {uc $_} split (/::/, $label);
+    if ($tags[0] eq uc $self->{SETTING}{CFG_LABEL}{SETTING}) {
+      shift @tags;
+      $self->assign_setting (LABELS => \@tags, VALUE => $value);
+      next LINE;
+    }
+
+    # Not a standard setting variable, put in internal variable list
+    $label =~ s/^\%//;
+    $self->assign_variable (LABEL => $label, VALUE => $value);
+  }
+
+  1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $ref = _get_hash_value (arg1, arg2, ...);
+#
+# DESCRIPTION
+#   This internal method recursively gets a value from a multi-dimensional
+#   hash.
+# ------------------------------------------------------------------------------
+
+sub _get_hash_value {
+  my $value = shift;
+
+  while (defined (my $arg = shift)) {
+    if (exists $value->{$arg}) {
+      $value = $value->{$arg};
+
+    } else {
+      return undef;
+    }
+  }
+
+  return $value;
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/Extract.pm
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/Extract.pm	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/Extract.pm	(revision 5058)
@@ -0,0 +1,1768 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::Extract
+#
+# DESCRIPTION
+#   This class contains methods for carrying out the various tasks that are
+#   required to extract code from the FCM Subversion repository for feeding
+#   into the prototype build system. At the end of the extract, it writes a
+#   build configuration file for feeding into the build system.  If the code
+#   is to be built on a remote machine, it is mirrored to the remote machine
+#   using a "rdist" or "rsync" interface.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+package Fcm::Extract;
+
+# Standard pragma
+use warnings;
+use strict;
+
+# Standard modules
+use Carp;
+use File::Spec;
+use File::Spec::Functions;
+use File::Basename;
+use File::Path;
+use File::Compare;
+
+# FCM component modules
+use Fcm::CfgFile;
+use Fcm::ReposBranch;
+use Fcm::SrcDirLayer;
+use Fcm::Util;
+use Fcm::Timer;
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $ext = Fcm::Extract->new (
+#     CONFIG    => $config,
+#     CFG_SRC   => $cfg_src,
+#     EXTRACTED => $extracted,
+#   );
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::Extract class.
+#
+# ARGUMENTS
+#   CONFIG     - reference to a Fcm::Config instance
+#   CFG_SRC    - source path to the extract configuration file
+#   EXTRACTED  - is it a pre-extracted object?
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $cfg       = exists $args{CFG_SRC}   ? $args{CFG_SRC}   : undef;
+  my $extracted = exists $args{EXTRACTED} ? $args{EXTRACTED} : undef;
+  my $config    = exists $args{CONFIG}    ? $args{CONFIG}    : &main::cfg;
+
+  my $self = {
+    CONFIG     => $config,            # configuration settings
+    CFG        => Fcm::CfgFile->new ( # ext cfg for this extract
+      SRC      => $cfg,               # source path of the config file
+      TYPE     => 'ext',              # config file type
+      CONFIG   => $config,            # configuration settings
+    ),
+    DEST       => {                   # destination info for this extract
+      ROOTDIR  => undef,              # destination root directory
+      CACHEDIR => undef,              # extract cache directory
+      CFGDIR   => undef,              # destination configuration directory
+      SRCDIR   => undef,              # destination source directory
+      BLD_CFG  => undef,              # bld cfg for the build system
+      EXT_CFG  => undef,              # ext cfg for subsequent extract
+    },
+    RDEST      => {                   # remote destination information
+      MACHINE  => undef,              # destination machine
+      LOGNAME  => undef,              # remote login name
+      ROOTDIR  => undef,              # destination root directory
+      CFGDIR   => undef,              # destination configuration directory
+      SRCDIR   => undef,              # destination source directory
+      BLD_CFG  => undef,              # bld cfg for the build system
+      EXT_CFG  => undef,              # ext cfg for subsequent extract
+    },
+    BDECLARE   => [],                 # list of declared bld cfg entries
+    OVERRIDE   => 0,                  # override conflicting patches?
+    EXTRACTED  => $extracted,         # is the current object pre-extracted?
+    USE        => [],                 # list of re-used extracts
+    BRANCHES   => [],                 # list of repository branch info
+    SRCDIRS    => {},                 # list of source directory extract info
+    LOCK       => undef,              # lock file
+  };
+  bless $self, $class;
+  return $self;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->DESTROY;
+#
+# DESCRIPTION
+#   This method is called automatically when a Fcm::Extract object is
+#   destroyed.
+# ------------------------------------------------------------------------------
+
+sub DESTROY {
+  my $self = shift;
+
+  # Remove the lock if it is set
+  unlink $self->{LOCK} if $self->{LOCK} and -e $self->{LOCK};
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $config = $ext->config;
+#
+# DESCRIPTION
+#   This method returns a reference to the Fcm::Config instance.
+# ------------------------------------------------------------------------------
+
+sub config {
+  my $self = shift;
+
+  return $self->{CONFIG};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $cfgfile = $ext->cfg;
+#   $ext->cfg ($cfgfile);
+#
+# DESCRIPTION
+#   This method returns a reference to a Fcm::CfgFile instance for the extract
+#   configuration file.
+# ------------------------------------------------------------------------------
+
+sub cfg {
+  my $self = shift;
+
+  return $self->{CFG};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $dest = $ext->dest ([$name]);
+#
+# DESCRIPTION
+#   This method returns a hash containing the extract destination information
+#   (local) if no argument is specified. If $name is specified, it returns the
+#   named hash element if it exists.
+# ------------------------------------------------------------------------------
+
+sub dest {
+  my $self = shift;
+
+  if (@_) {
+    my $name = shift;
+    $name    = uc $name;
+
+    if (exists $self->{DEST}{$name}) {
+      return $self->{DEST}{$name};
+    }
+  }
+
+  return %{ $self->{DEST} };
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rdest = $ext->rdest ([$name]);
+#
+# DESCRIPTION
+#   This method returns a hash containing the extract destination information
+#   (remote) if no argument is specified. If $name is specified, it returns the
+#   named hash element if it exists.
+# ------------------------------------------------------------------------------
+
+sub rdest {
+  my $self = shift;
+
+  if (@_) {
+    my $name = shift;
+    $name    = uc $name;
+
+    if (exists $self->{RDEST}{$name}) {
+      return $self->{RDEST}{$name};
+    }
+  }
+
+  return %{ $self->{RDEST} };
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @bdeclare = $ext->bdeclare ();
+#
+# DESCRIPTION
+#   This method returns a list containing the build configuration file entries.
+# ------------------------------------------------------------------------------
+
+sub bdeclare {
+  my $self = shift;
+
+  return @{ $self->{BDECLARE} };
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @branches = $ext->branches ([$index]);
+#
+# DESCRIPTION
+#   This method returns a list of references to Fcm::ReposBranch instances. If
+#   $index is specified, it returns the numbered item in the list.
+# ------------------------------------------------------------------------------
+
+sub branches {
+  my $self = shift;
+
+  if (@_) {
+    my $index = $_[0];
+    return exists $self->{BRANCHES}[$index] ? $self->{BRANCHES}[$index] : undef;
+  }
+
+  return @{ $self->{BRANCHES} };
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   %srcdirs = $ext->srcdirs ([$name]);
+#
+# DESCRIPTION
+#   This method returns a hash of source directories to be processed by this
+#   extract. If $name is specified, a named element of the hash is returned
+#   instead.
+# ------------------------------------------------------------------------------
+
+sub srcdirs {
+  my $self = shift;
+
+  if (@_) {
+    my $name = shift;
+    $name    = uc $name;
+
+    return exists $self->{SRCDIRS}{$name} ? $self->{SRCDIRS}{$name} : undef;
+  }
+
+  return %{ $self->{SRCDIRS} };
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $ext->extract ([FULL => 1], [IGNORE_LOCK => 1]);
+#
+# DESCRIPTION
+#   This is the main class method. It performs an extract using the current
+#   configuration. If FULL is set to true, it runs in full mode. Otherwise, it
+#   runs in incremental mode. If IGNORE_LOCK is set to true, it ignores any lock
+#   files that may exist in the extract destination root directory.
+# ------------------------------------------------------------------------------
+
+sub extract {
+  my $self = shift;
+  my %args = @_;
+
+  my $full        = exists $args{FULL}        ? $args{FULL}        : 0;
+  my $ignore_lock = exists $args{IGNORE_LOCK} ? $args{IGNORE_LOCK} : 0;
+
+  my $verbose = $self->config->verbose;
+
+  my $date = localtime;
+  print 'Extract started on ', $date, '.', "\n" if $verbose;
+  my $otime = time;
+
+  my $rc;
+  $rc = $self->decipher_cfg;
+
+  print '->Extract: start', "\n" if $verbose;
+  my $stime = time;
+
+  $rc = $self->check_dest               if $rc;
+  $rc = $self->check_lock               if $rc and not $ignore_lock;
+  $rc = $self->_set_lock                if $rc;
+  $rc = $self->expand_cfg               if $rc;
+  $rc = $self->_create_dest_dir ($full) if $rc;
+  $rc = $self->create_dir_stack         if $rc;
+  $rc = $self->_extract_src             if $rc;
+
+  $rc = $self->_sort_bdeclare if $rc;
+  $rc = $self->_write_ext_cfg if $rc;
+  $rc = $self->_write_bld_cfg if $rc;
+
+  my $ftime = time;
+  my $s_str = $ftime - $stime > 1 ? 'seconds' : 'second';
+  print '->Extract: ', $ftime - $stime, ' ', $s_str, "\n";
+
+  if ($rc and $self->{RDEST}{MACHINE}) {
+    print '->Mirror : start', "\n" if $verbose;
+    $stime = time;
+    $rc = $self->_mirror_extract;
+    $ftime = time;
+    $s_str = $ftime - $stime > 1 ? 'seconds' : 'second';
+    print '->Mirror : ', $ftime - $stime, ' ', $s_str, "\n";
+  }
+
+  if ($verbose) {
+    $s_str = $ftime - $otime > 1 ? 'seconds' : 'second';
+    print '->TOTAL  : ', $ftime - $otime, ' ', $s_str, "\n";
+  }
+
+  $date = localtime;
+  if ($rc) {
+    print 'Extract command finished on ', $date, '.', "\n" if $verbose;
+
+  } else {
+    e_report 'Extract command failed on ', $date, '.';
+  }
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $ext->decipher_cfg ();
+#
+# DESCRIPTION
+#   This method deciphers the extract configuration file.
+# ------------------------------------------------------------------------------
+
+sub decipher_cfg {
+  my $self = shift;
+
+  return unless $self->cfg->src;
+
+  # Read config file
+  my $read = $self->cfg->read_cfg;
+
+  # Check config file type
+  if ($read) {
+    if ($self->cfg->type ne 'ext') {
+      w_report 'Error: ', $self->cfg->src, ': not an extract config file';
+      return;
+    }
+
+  } else {
+    return;
+  }
+
+  my %cfg_labels = %{ $self->config->setting ('CFG_LABEL') };
+
+  # Extract information from each line of the config file
+  my @lines    = $self->cfg->lines;
+  LINE: for my $line (@lines) {
+    my $label = $line->{LABEL};
+    my $value = $line->{VALUE};
+
+    next LINE unless $label;
+
+    # Configuration file type/version, ignore
+    for my $my_label (keys %{ $cfg_labels{CFGFILE} }) {
+      next LINE if uc ($label) eq uc ($cfg_labels{CFGFILE}{$my_label});
+    }
+
+    # Include another file, processed already, ignore this line
+    next LINE if uc ($label) eq $cfg_labels{INC};
+
+    # User variable, ignore
+    next LINE if index (uc ($label), '%') == 0;
+
+    # Local destination directories, config file, etc
+    for my $my_label (keys %{ $cfg_labels{DEST} }) {
+      if (uc ($label) eq uc ($cfg_labels{DEST}{$my_label})) {
+        $self->{DEST}{$my_label} = &expand_tilde ($value);
+        next LINE;
+      }
+    }
+
+    # Remote machine, logname, destination directories, config file, etc
+    for my $my_label (keys %{ $cfg_labels{RDEST} }) {
+      if (uc ($label) eq uc ($cfg_labels{RDEST}{$my_label})) {
+        $self->{RDEST}{$my_label} = $value;
+        next LINE;
+      }
+    }
+
+    # "USE" statements
+    if (uc ($label) eq uc ($cfg_labels{USE})) {
+      my $exists = grep {$_->cfg->src eq $value} @{ $self->{USE} };
+
+      # Initialise new Fcm::Extract object if not already exists
+      unless ($exists) {
+        my $extract = Fcm::Extract->new (
+          CONFIG    => $self->config,
+          CFG_SRC   => expand_tilde ($value),
+          EXTRACTED => 1,
+        );
+
+        $extract->decipher_cfg;
+        $extract->check_dest;
+        $extract->expand_cfg ();
+        push @{ $self->{USE} }, $extract;
+      }
+      next LINE;
+    }
+
+    # "Override" setting
+    if (uc ($label) eq uc ($cfg_labels{OVERRIDE})) {
+      $self->{OVERRIDE} = $value;
+      next LINE;
+    }
+
+    # "Mirror" command
+    if (uc ($label) eq uc ($cfg_labels{MIRROR})) {
+      $self->config->assign_setting (
+        LABELS => [qw/TOOL MIRROR/],
+        VALUE  => $value,
+      );
+      next LINE;
+    }
+
+    # Declared bld cfg entries
+    {
+      my $prefix = $cfg_labels{BDECLARE} . '::';
+
+      if (index (uc ($label), $prefix) == 0) {
+        my $name = substr $label, length ($prefix);
+
+        if ($name) {
+          push @{ $self->{BDECLARE} }, {LABEL => $name, VALUE => $value,};
+          next LINE;
+        }
+      }
+    }
+
+    # Repository, version and source directories
+    for my $my_label (qw/REPOS VERSION SRCDIR EXPSRCDIR/) {
+      my $prefix  = $cfg_labels{$my_label} . '::';
+
+      if (index (uc ($label), $prefix) == 0) {
+        my $name    = substr $label, length ($prefix);
+
+        # Detemine package and tag
+        my @names   = split /::/, $name;
+        my $tag     = pop @names;
+        my $pckroot = $names[0];
+        my $pck     = join '::', @names;
+
+        # Check that $tag and $pckroot are defined
+        last if not $tag;
+        last if not $pckroot;
+
+        # Check whether branch already exists
+        my @branches = grep {
+          $_->package eq $pckroot and $_->tag eq $tag
+        } @{ $self->{BRANCHES} };
+
+        my $branch   = undef;
+
+        if (@branches) { # If so, set $branch to point to existing branch
+          $branch = shift @branches;
+
+        } else {         # If not, create new branch
+          $branch = Fcm::ReposBranch->new (
+            CONFIG  => $self->config,
+            PACKAGE => $pckroot,
+            TAG     => $tag,
+          );
+
+          push @{ $self->{BRANCHES} }, $branch;
+        }
+
+        # Check package name for source directory declarations
+        if ($my_label eq 'SRCDIR' or $my_label eq 'EXPSRCDIR') {
+          if ($pck eq $pckroot and $value !~ m#^/#) {
+            # Sub-package name not set and source directory quoted as a relative
+            # path, determine package name from path name
+            my @subpck = File::Spec->splitdir ($value);
+            $pck       = join '::', ($pckroot, @subpck);
+          }
+        }
+
+        # Assign the value accordingly
+        if ($my_label eq 'REPOS') {          # Repository location
+          $branch->repos ($value);
+
+        } elsif ($my_label eq 'VERSION') {   # Version used
+          $branch->version ($value);
+
+        } elsif ($my_label eq 'SRCDIR') {    # Source directory used
+          $branch->dir ($pck, $value);
+
+        } elsif ($my_label eq 'EXPSRCDIR') { # Expandable source directory
+          $branch->expdir ($pck, $value);
+        }
+
+        next LINE;
+      }
+    }
+
+    # Label not recognised
+    w_report 'ERROR: ', $line->{SRC}, ': LINE ', $line->{NUMBER},
+             ': label "', $label, '" not recognised';
+    return;
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $ext->check_dest ();
+#
+# DESCRIPTION
+#   This method checks that the extract destionations are set correctly.
+# ------------------------------------------------------------------------------
+
+sub check_dest {
+  my $self = shift;
+
+  my %subdir  = %{ $self->config->setting ('DIR') };
+  my %cfgname = %{ $self->config->setting ('CFG_NAME') };
+
+  # Default destination settings
+  my $dest = $self->{DEST};
+  if ($dest->{ROOTDIR}) {
+    unless ($dest->{SRCDIR}) {   # Location of extracted source
+      $dest->{SRCDIR} = catfile $dest->{ROOTDIR}, $subdir{SRC};
+    }
+    unless ($dest->{CFGDIR}) {   # Location of configuration files
+      $dest->{CFGDIR} = catfile $dest->{ROOTDIR}, $subdir{CFG};
+    }
+    unless ($dest->{CACHEDIR}) { # Location of cache
+      $dest->{CACHEDIR} = catfile $dest->{ROOTDIR}, $subdir{CACHE};
+    }
+    unless ($dest->{BLD_CFG}) {  # Location of (output) bld cfg
+      $dest->{BLD_CFG} = catfile $dest->{CFGDIR}, $cfgname{BLD};
+    }
+    unless ($dest->{EXT_CFG}) {  # Location of (output) ext cfg
+      $dest->{EXT_CFG} = catfile $dest->{CFGDIR}, $cfgname{EXT};
+    }
+  } else {
+    w_report 'Error: ', $self->cfg->src,
+             ': destination root directory not set.';
+    return;
+  }
+
+  # Default remote destination settings
+  if ($self->{RDEST}{MACHINE}) {
+
+    # Use local logname as remote logname if it is not set
+    $self->{RDEST}{LOGNAME} = getlogin      unless $self->{RDEST}{LOGNAME};
+    $self->{RDEST}{LOGNAME} = $ENV{LOGNAME} unless $self->{RDEST}{LOGNAME};
+    $self->{RDEST}{LOGNAME} = $ENV{USER}    unless $self->{RDEST}{LOGNAME};
+
+    unless ($self->{RDEST}{LOGNAME}) {
+      w_report 'Error: ', $self->cfg->src,
+               ': cannot determine your remote logname.';
+      return;
+    }
+
+    # Make sure remote destination root directory is set
+    unless ($self->{RDEST}{ROOTDIR}) {
+      w_report 'Error: ', $self->cfg->src,
+               ': remote destination root directory not set.';
+      return;
+    }
+
+    # Make sure remote destination source directory is set
+    $self->{RDEST}{SRCDIR} = catfile $self->{RDEST}{ROOTDIR}, $subdir{SRC}
+      unless $self->{RDEST}{SRCDIR};
+
+    # Make sure remote destination configuration directory is set
+    $self->{RDEST}{CFGDIR} = catfile $self->{RDEST}{ROOTDIR}, $subdir{CFG}
+      unless $self->{RDEST}{CFGDIR};
+
+    # Make sure remote bld cfg is set
+    $self->{RDEST}{BLD_CFG} = catfile $self->{RDEST}{CFGDIR}, $cfgname{BLD}
+      unless $self->{RDEST}{BLD_CFG};
+
+    # Make sure remote ext cfg is set
+    $self->{RDEST}{EXT_CFG} = catfile $self->{RDEST}{CFGDIR}, $cfgname{EXT}
+      unless $self->{RDEST}{EXT_CFG};
+
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $ext->check_lock ();
+#
+# DESCRIPTION
+#   This method checks whether a lock is set in the current extract.
+# ------------------------------------------------------------------------------
+
+sub check_lock {
+  my $self = shift;
+
+  my $rootdir  = $self->{DEST}{ROOTDIR};
+  my $lock_ext = catfile ($rootdir, $self->config->setting (qw/MISC LOCK_EXT/));
+  my $lock_bld = catfile ($rootdir, $self->config->setting (qw/MISC LOCK_BLD/));
+
+  # Always throw error if extract lock exists
+  if (-e $lock_ext) {
+    w_report 'ERROR: extract lock file exists: ', $lock_ext, ',';
+    w_report '       an extract may be running at ', $rootdir, ', abort.';
+    return;
+  }
+
+  # Throw error if current object is not a "used" pre-extracted object and
+  # a build lock exists
+  if ((not $self->{EXTRACTED}) and -e $lock_bld) {
+    w_report 'ERROR: build lock file exists: ', $lock_bld, ',';
+    w_report '       a build may be running at ', $rootdir, ', abort.';
+    return;
+  }
+
+  # Check locks in inherited extract
+  for my $use (@{ $self->{USE} }) {
+    return unless $use->check_lock;
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_set_lock ();
+#
+# DESCRIPTION
+#   This method sets a lock is set in the current extract.
+# ------------------------------------------------------------------------------
+
+sub _set_lock {
+  my $self = shift;
+
+  $self->{LOCK} = catfile (
+    $self->{DEST}{ROOTDIR}, $self->config->setting (qw/MISC LOCK_EXT/),
+  );
+
+  &touch_file ($self->{LOCK});
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $ext->expand_cfg ();
+#
+# DESCRIPTION
+#   This method expands the settings of the extract configuration.
+# ------------------------------------------------------------------------------
+
+sub expand_cfg {
+  my $self = shift;
+
+  # Establish a set of source directories from the "base repository"
+  my %base_branches = ();
+
+  # Inherit "base" set of source directories from re-used extracts
+  my @uses = @{ $self->{USE} };
+
+  for my $use (@uses) {
+    my @branches = $use->branches;
+
+    for my $branch (@branches) {
+      my $package              = $branch->package;
+      $base_branches{$package} = $branch unless exists $base_branches{$package};
+    }
+  }
+
+  for my $branch (@{ $self->{BRANCHES} }) {
+    # Expand URL keywords if necessary
+    if ($branch->repos) {
+      my $repos = expand_url_keyword (
+        URL => $branch->repos,
+        CFG => $self->config,
+      );
+      $branch->repos ($repos) if $repos ne $branch->repos;
+    }
+
+    # Check that repository type and version are set
+    if ($branch->repos and &is_url ($branch->repos)) {
+      $branch->type    ('svn')  unless $branch->type;
+      $branch->version ('head') unless $branch->version;
+
+    } else {
+      $branch->type    ('user') unless $branch->type;
+      $branch->version ('user') unless $branch->version;
+    }
+
+    $branch->expand_version_tag; # Work out revision number a version tag
+    $branch->expand_path;        # Expand relative path to full path
+    $branch->expand_all;         # Search sub-directories
+
+    my $package = $branch->package;
+
+    if (exists $base_branches{$package}) {
+      # A base branch for this package exists
+
+      # If current branch has no source directory, use the set provided by the
+      # base branch
+      my %dirs = $branch->dirs;
+      $branch->add_base_dirs ($base_branches{$package}) unless keys %dirs;
+
+    } else {
+      # This package does not yet have a base branch, set this branch as base
+      $base_branches{$package} = $branch;
+    }
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_create_dest_dir ($full);
+#
+# DESCRIPTION
+#   This internal method (re-)creates all the destination directories if
+#   necessary. If $full is set to true, it removes existing directories/files
+#   in the destination directories.
+# ------------------------------------------------------------------------------
+
+sub _create_dest_dir {
+  my ($self, $full) = @_;
+
+  my $verbose = $self->config->verbose;
+
+  # Remove previous extract if "FULL" flag is set
+  if ($full) {
+    # Remove extracted source
+    if (-d $self->{DEST}{SRCDIR} and -w $self->{DEST}{SRCDIR}) {
+      print 'Remove directory: ', $self->{DEST}{SRCDIR}, "\n" if $verbose;
+      my $removed = rmtree $self->{DEST}{SRCDIR};
+      w_report 'WARNING: ', $self->{DEST}{SRCDIR}, ': cannot remove.'
+        if not $removed;
+    }
+
+    # Remove cache
+    my @files;
+    if (-d $self->{DEST}{CACHEDIR} and opendir DIR, $self->{DEST}{CACHEDIR}) {
+      @files = grep {$_ ne '.' and $_ ne '..'} readdir 'DIR';
+      closedir DIR;
+    }
+
+    for my $file (@files) {
+      my $path = File::Spec->catfile ($self->{DEST}{CACHEDIR}, $file);
+
+      next unless $file eq $self->config->setting (qw/CACHE EXTCONFIG/) or
+                  -d $path;
+
+      print 'Remove: ', $path, "\n" if $verbose;
+      my $removed = rmtree $path;
+      w_report 'WARNING: ', $path, ': cannot remove.' if not $removed;
+    }
+  }
+
+  # Create extract destinations if necessary
+  for my $my_label (qw/ROOTDIR CACHEDIR CFGDIR SRCDIR/) {
+    my $dirname = $self->{DEST}{$my_label};
+
+    # Create directory if it does not already exist
+    if (not -d $dirname) {
+      print 'Make directory: ', $dirname, "\n" if $verbose > 1;
+      mkpath $dirname;
+    }
+
+    unless (-d $dirname and -w $dirname) {
+      w_report 'ERROR: ', $dirname, ': cannot write to destination.';
+      return;
+    }
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $ext->create_dir_stack (
+#     USE => $use, # Is this a pre-extracted configuration?
+#   );
+#
+# DESCRIPTION
+#   This method creates a hash of source directories to be processed. If the
+#   flag USE is set to true, the source directories are assumed processed and
+#   extracted.
+# ------------------------------------------------------------------------------
+
+sub create_dir_stack {
+  my $self = shift;
+  my %args = @_;
+  my $extracted = exists $args{USE} ? $args{USE} : undef;
+
+  # Inherit from USE ext cfg
+  if (@{ $self->{USE} } > 0) {
+    for my $use (@{ $self->{USE} }) {
+      $use->create_dir_stack (USE => 1);
+      my %use_srcdirs = $use->srcdirs;
+
+      while (my ($key, $value) = each %use_srcdirs) {
+        $self->{SRCDIRS}{$key} = $value;
+
+        # Re-set destination to current destination
+        my @path = split (/::/, $key);
+        $self->{SRCDIRS}{$key}{DEST} = catfile ($self->{DEST}{SRCDIR}, @path);
+      }
+    }
+  }
+
+  # Build stack from current ext cfg
+  for my $branch (@{ $self->{BRANCHES} }) {
+    my %branch_dirs = $branch->dirs;
+
+    for my $dir (keys %branch_dirs) {
+      # Check whether source directory is already in the list
+      if (not exists $self->{SRCDIRS}{$dir}) { # if not, create it
+        $self->{SRCDIRS}{$dir} = {
+          DEST  => catfile ($self->{DEST}{SRCDIR}, split (/::/, $dir)),
+          STACK => [],
+          FILES => {},
+        };
+      }
+
+      my $stack = $self->{SRCDIRS}{$dir}{STACK}; # copy reference
+
+      # Create a new layer in the input stack
+      my $layer = Fcm::SrcDirLayer->new (
+        CONFIG    => $self->config,
+        NAME      => $dir,
+        PACKAGE   => $branch->package,
+        TAG       => $branch->tag,
+        LOCATION  => $branch->dir ($dir),
+        REPOSROOT => $branch->repos,
+        VERSION   => $branch->version,
+        TYPE      => $branch->type,
+        EXTRACTED => $extracted ? $self->{SRCDIRS}{$dir}{DEST} : undef,
+      );
+
+      # Check whether layer is already in the stack
+      my $exist = grep {
+        $_->location eq $layer->location and $_->version  eq $layer->version;
+      } @{ $stack };
+
+      if (not $exist) {
+        # If not already exist, put layer into stack
+
+        # Note: user stack always comes last
+        if (! $layer->user and exists $stack->[-1] and $stack->[-1]->user) {
+          my $lastlayer = pop @{ $stack };
+          push @{ $stack }, $layer;
+          $layer = $lastlayer;
+        }
+
+        push @{ $stack }, $layer;
+
+      } elsif ($layer->user) {
+
+        # User layer already exists, overwrite it
+        $stack->[-1] = $layer;
+
+      }
+    }
+  }
+
+  # Read content of "commit cache" file if it exists
+  my $cachedir = $self->{DEST}{CACHEDIR};
+  my $cfgbase  = $self->config->setting (qw/CACHE EXTCONFIG/);
+  my $cfgfile  = catfile $cachedir, $cfgbase;
+  my %config_lines = ();
+  if (-r $cfgfile) {
+    my $cfg = Fcm::CfgFile->new (CONFIG => $self->config, SRC => $cfgfile,);
+    $cfg->read_cfg;
+    my @lines = $cfg->lines;
+
+    for my $line (@lines) {
+      $config_lines{$line->{LABEL}} = $line->{VALUE};
+    }
+  }
+
+  my %new_config_lines;
+
+  # Compare each layer to base layer, discard unnecessary layers
+  for my $srcdir (keys %{ $self->{SRCDIRS} }) {
+    my @stack = ();
+
+    while (my $layer = shift @{ $self->{SRCDIRS}{$srcdir}{STACK} }) {
+      if ($layer->user) {
+        # User directory, check that the declared location exists
+        if (not -d $layer->location) {
+          w_report 'Error: declared source directory ', $layer->location,
+                   ' does not exists ';
+          return;
+        }
+
+        # Always override repository code
+        push @stack, $layer;
+
+      } else {
+        unless ($layer->extracted and $layer->commit) {
+
+          my $key = join '::', ($srcdir, $layer->location, $layer->version);
+
+          # See if commit version information is cached
+          if (keys %config_lines) {
+            if (exists $config_lines{$key}) {
+              $layer->commit ($config_lines{$key});
+            }
+          }
+
+          # Check source directory for commit version, if necessary
+          $layer->get_commit unless $layer->commit;
+          if (not $layer->commit) {
+            w_report 'Error: cannot determine the last changed revision of ',
+                     $layer->location;
+            return;
+          }
+
+          # Set cache directory for layer
+          my $tag_ver = $layer->tag . '__' . $layer->commit;
+          $layer->cachedir (catfile $cachedir, split (/::/, $srcdir), $tag_ver);
+
+          # New line in cache config file
+          $new_config_lines{$key} = $layer->commit;
+        }
+
+        # Push this layer in the stack:
+        # 1. it has a different version compared to the top layer
+        # 2. it is the top layer (base line code)
+        if (@stack > 0) {
+          push @stack, $layer if $layer->commit != $stack[0]->commit;
+
+        } else {
+          push @stack, $layer;
+        }
+
+      }
+    }
+
+    $self->{SRCDIRS}{$srcdir}{STACK} = \@stack;
+
+  }
+
+  # Write "commit cache" file
+  if (not $extracted) {
+    mkpath $cachedir if not -d $cachedir;
+    my $cfg = Fcm::CfgFile->new (CONFIG => $self->config,);
+
+    while ((my $label, my $value) = each %new_config_lines) {
+      $cfg->add_line (LABEL => $label, VALUE => $value,);
+    }
+
+    $cfg->print_cfg ($cfgfile);
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_extract_src ();
+#
+# DESCRIPTION
+#   This internal method performs the extract of the source directories and
+#   files if necessary.
+# ------------------------------------------------------------------------------
+
+sub _extract_src {
+  my $self = shift;
+
+  my $verbose = $self->config->verbose;
+  my %v_count = (
+    CREATED_DIRS    => 0,
+    IGNORED_SUBDIRS => 0,
+    UPDATED_FILES   => 0,
+    REMOVED_FILES   => 0,
+  );
+
+  my $cachedir = $self->{DEST}{CACHEDIR};
+
+  # Go through the "stack" of each source directory
+  # Extract the source directories/files if required
+
+  for my $srcdir (values %{ $self->{SRCDIRS} }) {
+
+    # Check if destionation exists and is not a directory
+    if (-f $srcdir->{DEST}) {
+      w_report $srcdir->{DEST},
+               ': destination exists and is not a directory, abort.';
+      return;
+    }
+
+    my %base_files   = (); # list of files in the base layer
+    my %used_files   = (); # keys = file basenames, values = layer reference
+    $srcdir->{FILES} = \%used_files;
+    my @destpath     = (); # search path for source directory destinations
+ 
+    for my $layer (@{ $srcdir->{STACK} }) {
+      # Update the cache for each layer of the stack if necessary
+      $layer->update_cache unless $layer->extracted or -d $layer->localdir;
+
+      # Search path for extract destinations of this source directory
+      unshift @destpath, $layer->extracted
+        if $layer->extracted and not grep {$_ eq $layer->extracted} @destpath;
+ 
+      # Get list of files in the cache or local directory
+      for my $file (($layer->get_files)) {
+        if (exists $base_files{$file}) {
+          # File exists in the base, compare current version with base version,
+          # discard if not changed
+          my $base_file = catfile $base_files{$file}->localdir, $file;
+          my $used_file = catfile $used_files{$file}->localdir, $file;
+          my $this_file = catfile $layer->localdir, $file;
+
+          if (compare ($base_file, $this_file)) { # Differs
+            if ($base_files{$file} eq $used_files{$file}) {
+              # Base and used are the same layer, use current layer
+              $used_files{$file} = $layer;
+
+            } elsif (compare ($used_file, $this_file) == 0) {
+              # Changes in used and this are the same, no update required
+
+              # Print a message at verbose mode 2 or above
+              if ($verbose > 1) {
+                print &_print_override_mesg (
+                  FILE   => $file,
+                  LAYER0 => $base_files{$file},
+                  LAYER1 => $used_files{$file},
+                  LAYER2 => $layer,
+                );
+                print '  Same modifications, use the source in URL 1.', "\n";
+              }
+
+            } elsif ($self->{OVERRIDE}) {
+              # Base and used are different, and used is not the same as this
+              # Override mode, use current layer
+
+              # Print override mode message
+              if ($verbose) {
+                print &_print_override_mesg (
+                  FILE   => $file,
+                  LAYER0 => $base_files{$file},
+                  LAYER1 => $used_files{$file},
+                  LAYER2 => $layer,
+                );
+                print '  ', $file, ' in URL 2 overrides that in URL 1.', "\n";
+              }
+
+              $used_files{$file} = $layer;
+
+            } else {
+              # Base and used are different, and used is not the same as this
+              # Non-override mode, fail the extract
+              w_report &_print_override_mesg (
+                FILE   => $file,
+                LAYER0 => $base_files{$file},
+                LAYER1 => $used_files{$file},
+                LAYER2 => $layer,
+              );
+              w_report '  Override mode is false, file in URL 1 cannot ',
+                       'override file in URL 2, abort.';
+              return;
+            }
+          }
+ 
+        } else {
+          # The first time the file is found
+          $base_files{$file} = $layer;
+          $used_files{$file} = $layer;
+        }
+      }
+    }
+
+    # Add current destination to the beginning of the destination search path
+    unshift @destpath, $srcdir->{DEST} if -d $srcdir->{DEST};
+
+    for my $file (keys %used_files) {
+      # Ignore sub-directories
+      if (-d catfile $used_files{$file}->localdir, $file) {
+        # Print diagnostic
+        if ($verbose > 1) {
+          print 'Ignore subdirectory: ', $file, "\n";
+          print '                Src: ', $used_files{$file}->location;
+          print '@', $used_files{$file}->version unless $used_files{$file}->user;
+          print "\n";
+        }
+        $v_count{IGNORED_SUBDIRS}++;
+        next;
+      }
+
+      # Determine whether file has changed, compared with the destination
+      my $diff = 1;
+      for my $dir (@destpath) {
+        my $old = catfile ($dir, $file);
+
+        if (-f $old) {
+          my $new = catfile ($used_files{$file}->localdir, $file);
+          $diff   = compare $old, $new;
+          last;
+        }
+      }
+
+      if ($diff) { # copy if differs
+        # Create extract destination, if required
+        if (not -d $srcdir->{DEST}) {
+          print 'Create directory: ', $srcdir->{DEST}, "\n" if $verbose > 1;
+          my $mkdirs = mkpath $srcdir->{DEST};
+
+          if (! -d $srcdir->{DEST} or ! -w $srcdir->{DEST}) {
+            w_report $srcdir->{DEST}, ': not a writable directory, abort.';
+            return;
+          }
+
+          $v_count{CREATED_DIRS} += $mkdirs;
+        }
+
+        # Set up the copy command
+        my @cmd = (
+          'cp',
+          catfile ($used_files{$file}->localdir, $file),
+          $srcdir->{DEST},
+        );
+
+        my $dest_file = catfile ($srcdir->{DEST}, $file);
+
+        # Print diagnostic
+        if ($verbose > 1) {
+          print 'Update: ', $dest_file, "\n";
+          print '   Src: ', $used_files{$file}->location;
+          print '@', $used_files{$file}->version unless $used_files{$file}->user;
+          print "\n";
+        }
+
+        # Remove old file if it exists
+        unlink $dest_file if -f $dest_file;
+
+        # Execute the copy command
+        &run_command (\@cmd, TIME => $self->config->verbose > 2);
+
+        $v_count{UPDATED_FILES}++;
+      }
+
+    }
+
+    # Check that the destination directory does not contain any removed files
+    opendir DIR, $srcdir->{DEST};
+    my @dest_files = readdir DIR;
+    closedir DIR;
+
+    while (my $file = shift @dest_files) {
+      next if $file =~ /^\.\.?/;                   # ignore hidden files
+      next if -d catfile ($srcdir->{DEST}, $file); # ignore sub-directories
+
+      # Check if the file exists in any of the versions
+      my $exists = 0;
+      for my $layer (@{ $srcdir->{STACK} }) {
+        if (-f catfile ($layer->localdir, $file)) {
+          $exists = 1;
+          last;
+        }
+      }
+
+      # File exists in destination but not in any versions...
+      if (not $exists) {
+        my @cmd = (
+          qw/rm -f/,
+          catfile ($srcdir->{DEST}, $file),
+        );
+
+        # Print diagnostic
+        print 'Remove: ', catfile ($srcdir->{DEST}, $file), "\n"
+          if $verbose > 1;
+
+        # Execute the command
+        &run_command (\@cmd, TIME => $self->config->verbose > 2);
+
+        $v_count{REMOVED_FILES}++;
+      }
+    }
+  }
+
+  if ($verbose) {
+    my %v_label = (
+      CREATED_DIRS    => 'Number of directories created    : ',
+      IGNORED_SUBDIRS => 'Number of ignored sub-directories: ',
+      UPDATED_FILES   => 'Number of updated files          : ',
+      REMOVED_FILES   => 'Number of removed files          : ',
+    );
+    for my $key (qw/CREATED_DIRS IGNORED_SUBDIRS UPDATED_FILES REMOVED_FILES/) {
+      print $v_label{$key}, $v_count{$key}, "\n" if $v_count{$key};
+    }
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = _print_override_mesg (
+#     FILE => $file,
+#     LAYER0 => $layer0,
+#     LAYER1 => $layer1,
+#     LAYER2 => $layer2,
+#   );
+#
+# DESCRIPTION
+#   This internal method returns a string containing an override mode message.
+#
+# ARGUMENTS
+#   FILE   - name of the source file
+#   LAYER0 - base location
+#   LAYER1 - source location overridden by LOC2
+#   LAYER2 - source location overriding LOC1
+# ------------------------------------------------------------------------------
+
+sub _print_override_mesg {
+  my %args = @_;
+
+  my $string = $args{FILE};
+  $string .= ': modified in both URL 1 and URL 2, relative to BASE:';
+  $string .= "\n";
+  $string .= '  BASE : ' . $args{LAYER0}->location;
+  $string .= '@' . $args{LAYER0}->version unless $args{LAYER0}->user;
+  $string .= "\n";
+  $string .= '  URL 1: ' . $args{LAYER1}->location;
+  $string .= '@' . $args{LAYER1}->version unless $args{LAYER1}->user;
+  $string .= "\n";
+  $string .= '  URL 2: ' . $args{LAYER2}->location;
+  $string .= '@' . $args{LAYER2}->version unless $args{LAYER2}->user;
+  $string .= "\n";
+
+  return $string;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_sort_bdeclare ();
+#
+# DESCRIPTION
+#   This internal method sorts the declared build configuration entries,
+#   filtering out repeated entries.
+# ------------------------------------------------------------------------------
+
+sub _sort_bdeclare {
+  my $self = shift;
+
+  # Get list of build configuration labels that can be declared multiple times
+  my %cfg_labels   = %{ $self->config->setting ('CFG_LABEL') };
+  my @cfg_keywords = split /,/, $self->config->setting ('CFG_KEYWORD');
+  @cfg_keywords    = map {$cfg_labels{$_}} @cfg_keywords;
+
+  # Filter out repeated declarations
+  my @bdeclares = ();
+  for my $bdeclare (reverse @{ $self->{BDECLARE} }) {
+    my $label = $bdeclare->{LABEL};
+
+    # Do not filter any declarations that can be declared multiple times
+    my $unshift_ok = grep {
+      uc ($label) eq $_ or index (uc ($label), $_ . '::') == 0;
+    } @cfg_keywords;
+    # @bdeclare contains nothing, last entry
+    $unshift_ok    = 1 unless $unshift_ok or @bdeclares;
+    # Check if a later entry already exists
+    $unshift_ok    = 1
+      unless $unshift_ok or grep {$_->{LABEL} eq $label} @bdeclares;
+
+    # Reconstruct array from bottom up
+    unshift @bdeclares, $bdeclare if $unshift_ok;
+  }
+
+  $self->{BDECLARE} = \@bdeclares;
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_write_ext_cfg ();
+#
+# DESCRIPTION
+#   This internal method writes the expanded extract configuration file.
+# ------------------------------------------------------------------------------
+
+sub _write_ext_cfg {
+  my $self = shift;
+
+  my %cfg_labels = %{ $self->config->setting ('CFG_LABEL') };
+  my %subdir     = %{ $self->config->setting ('DIR') };
+  my %cfgname    = %{ $self->config->setting ('CFG_NAME') };
+
+  # Create new config file object and assign lines to it
+  my $cfg = Fcm::CfgFile->new (CONFIG => $self->config, TYPE => 'ext',);
+
+  # Set up config file header
+  $cfg->add_header ();
+
+  # Re-use pre-extracted expanded ext cfg
+  if (@{ $self->{USE} }) {
+    $cfg->add_comment_block ('Other ext cfg');
+
+    for my $reuse (@{ $self->{USE} }) {
+      my $rootdir = $reuse->dest ('ROOTDIR');
+      my $ext_cfg = $reuse->cfg->src;
+
+      # Default location of build config file
+      my $def_ext_cfg = catfile $rootdir, $subdir{CFG}, $cfgname{EXT};
+
+      $cfg->add_line (
+        LABEL => $cfg_labels{USE},
+        VALUE => $ext_cfg eq $def_ext_cfg ? $rootdir : $ext_cfg,
+      );
+    }
+
+    # Blank line
+    $cfg->add_line;
+  }
+
+  # Destination directories, config file, etc
+  my $dest = $self->{DEST};
+
+  $cfg->add_comment_block ('Destination');
+
+  $cfg->add_line (
+    LABEL => $cfg_labels{DEST}{ROOTDIR},
+    VALUE => $dest->{ROOTDIR},
+  );
+
+  for my $label (qw/CFG SRC CACHE/) {
+    my $dir = $label . 'DIR';
+
+    if ($dest->{$dir} ne catfile $dest->{ROOTDIR}, $subdir{$label}) {
+      $cfg->add_line (
+        LABEL => $cfg_labels{DEST}{$dir},
+        VALUE => $dest->{$dir},
+      );
+    }
+  }
+
+  for my $name (qw/BLD EXT/) {
+    my $label = $name . '_CFG';
+
+    if ($dest->{$label} ne catfile $dest->{CFGDIR}, $cfgname{$name}) {
+      $cfg->add_line (
+        LABEL => $cfg_labels{DEST}{$label},
+        VALUE => $dest->{$label},
+      );
+    }
+  }
+
+  # Blank line
+  $cfg->add_line;
+
+  # Remote destination directories, config file, etc
+  if ($self->{RDEST}{MACHINE}) {
+    my $rdest = $self->{RDEST};
+
+    $cfg->add_comment_block ('Remote destination');
+
+    for my $label (qw/MACHINE LOGNAME ROOTDIR/) {
+      $cfg->add_line (
+        LABEL => $cfg_labels{RDEST}{$label},
+        VALUE => $rdest->{$label},
+      );
+    }
+
+    for my $label (qw/CFG SRC/) {
+      my $dir = $label . 'DIR';
+      if ($rdest->{$dir} ne catfile $rdest->{ROOTDIR}, $subdir{$label}) {
+        $cfg->add_line (
+          LABEL => $cfg_labels{RDEST}{$dir},
+          VALUE => $rdest->{$dir},
+        );
+      }
+    }
+
+    for my $name (qw/BLD EXT/) {
+      my $label = $name . '_CFG';
+
+      if ($rdest->{$label} ne catfile $rdest->{CFGDIR}, $cfgname{$name}) {
+        $cfg->add_line (
+          LABEL => $cfg_labels{RDEST}{$label},
+          VALUE => $rdest->{$label},
+        );
+      }
+    }
+
+    $cfg->add_line (
+      LABEL => $cfg_labels{MIRROR},
+      VALUE => $self->config->setting (qw/TOOL MIRROR/),
+    );
+
+    # Blank line
+    $cfg->add_line;
+  }
+
+  if ($self->{OVERRIDE}) {
+    $cfg->add_line (
+      LABEL => $cfg_labels{OVERRIDE},
+      VALUE => $self->{OVERRIDE} ? 1 : 0,
+    );
+    $cfg->add_line;
+  }
+
+  # Source directories
+  $cfg->add_comment_block ('Source directories');
+
+  # Set up lines in the ext cfg
+  my @lines = ();
+  for my $my_label (keys %{ $self->{SRCDIRS} }) {
+    for my $layer (@{ $self->{SRCDIRS}{$my_label}{STACK} }) {
+      next if $layer->extracted;
+
+      my $tag = $layer->package . '::' . $layer->tag;
+
+      # Repository
+      my $exists = grep {
+        $_->{LABEL} eq $cfg_labels{REPOS} . '::' . $tag;
+      } @lines;
+      push @lines, {
+        LABEL   => $cfg_labels{REPOS} . '::' . $tag,
+        VALUE   => $layer->reposroot,
+      } if not $exists;
+
+      # Version
+      $exists = grep {
+        $_->{LABEL} eq $cfg_labels{VERSION} . '::' . $tag;
+      } @lines;
+      push @lines, {
+        LABEL   => $cfg_labels{VERSION} . '::' . $tag,
+        VALUE   => $layer->version,
+      } unless $layer->user or $exists;
+
+      # Source directory
+      my ($pcks, $path);
+
+      if ($layer->reposroot) {
+        # Repository root declaration exists, print relative path
+        if ($layer->location eq $layer->reposroot) {
+          $path  = '';
+
+        } else {
+          $path  = substr ($layer->location, length ($layer->reposroot) + 1);
+        }
+        my @pcks = split /::/, $my_label;
+        shift @pcks;
+
+        if (join ('::', @pcks) eq join ('::', File::Spec->splitdir ($path))) {
+          # Print top package name if relative path matches sub-package name
+          $pcks = $layer->package;
+
+        } else {
+          # Print full sub-package name otherwise
+          $pcks = $my_label;
+        }
+
+      } else {
+        # No repository root declaration
+        # Print full path and full sub-package name
+        $path = $layer->location;
+        $pcks = $my_label;
+      }
+
+      my $length = $layer->reposroot ? length ($layer->reposroot) + 1 : 0;
+      push @lines, {
+        LABEL   => join ('::', ($cfg_labels{SRCDIR}, $pcks, $layer->tag)),
+        VALUE   => $path,
+      };
+    }
+  }
+
+  # Sort lines for specifying repository, version and source directories
+  @lines = sort {
+    my $rep_label = $cfg_labels{REPOS};
+    my $ver_label = $cfg_labels{VERSION};
+
+    if ($a->{LABEL} =~ /^$rep_label/) {
+
+      # Repository labels
+      if ($b->{LABEL} =~ /^$rep_label/) {
+        $a->{LABEL} cmp $b->{LABEL} or $a->{VALUE} cmp $b->{VALUE};
+      } else {
+        -1;
+      }
+
+    } elsif ($a->{LABEL} =~ /^$ver_label/) {
+
+      # Version labels
+      if ($b->{LABEL} =~ /^$rep_label/) {
+        1;
+      } elsif ($b->{LABEL} =~ /^$ver_label/) {
+        $a->{LABEL} cmp $b->{LABEL} or $a->{VALUE} cmp $b->{VALUE};
+      } else {
+        -1;
+      }
+    } else {
+
+      # Source directories labels
+      if ($b->{LABEL} =~ /^(?:$rep_label|$ver_label)/) {
+        1;
+      } else {
+        $a->{LABEL} cmp $b->{LABEL} or $a->{VALUE} cmp $b->{VALUE};
+      }
+
+    }
+  } @lines;
+
+  # Add lines for specifying repository, version and source directories
+  while (my $line = shift @lines) {
+    $cfg->add_line (
+      LABEL => $line->{LABEL},
+      VALUE => $line->{VALUE},
+    );
+  }
+
+  # Add declared bld cfg entries
+  if (@{ $self->{BDECLARE} }) {
+    # Blank line
+    $cfg->add_line;
+
+    $cfg->add_comment_block ('Declared bld cfg entries');
+    for my $bdeclare (@{ $self->{BDECLARE} }) {
+      $cfg->add_line (
+        LABEL => $cfg_labels{BDECLARE} . '::' . $bdeclare->{LABEL},
+        VALUE => $bdeclare->{VALUE},
+      );
+    }
+  }
+
+  # Print lines to config file
+  $cfg->print_cfg ($self->{DEST}{EXT_CFG});
+
+  return 1;
+
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_write_bld_cfg ();
+#
+# DESCRIPTION
+#   This internal method writes the build configuration file.
+# ------------------------------------------------------------------------------
+
+sub _write_bld_cfg {
+  my $self = shift;
+
+  my %cfg_labels = %{ $self->config->setting ('CFG_LABEL') };
+  my %subdir     = %{ $self->config->setting ('DIR') };
+  my %cfgname    = %{ $self->config->setting ('CFG_NAME') };
+
+  # Create new config file object and assign lines to it
+  my $cfg = Fcm::CfgFile->new (CONFIG => $self->config, TYPE => 'bld');
+
+  # Set up config file header
+  $cfg->add_header ();
+
+  # Pre-compile source
+  if (@{ $self->{USE} }) {
+    $cfg->add_comment_block ('Pre-compile source');
+
+    for my $reuse (@{ $self->{USE} }) {
+      my $rootdir;
+      my $bld_cfg;
+
+      if ($self->{RDEST}{MACHINE}) {
+        $rootdir = $reuse->rdest ('ROOTDIR');
+        $bld_cfg = $reuse->rdest ('BLD_CFG');
+      } else {
+        $rootdir = $reuse->dest ('ROOTDIR');
+        $bld_cfg = $reuse->dest ('BLD_CFG');
+      }
+
+      # Default location of build config file
+      my $def_bld_cfg = catfile $rootdir, $subdir{CFG}, $cfgname{BLD};
+
+      $cfg->add_line (
+        LABEL => $cfg_labels{USE},
+        VALUE => $bld_cfg eq $def_bld_cfg ? $rootdir : $bld_cfg,
+      );
+    }
+
+    # Blank line
+    $cfg->add_line;
+  }
+
+  # Add declared bld cfg entries
+  if (@{ $self->{BDECLARE} }) {
+    $cfg->add_comment_block ('Declared build options...');
+
+    my @bdeclares = sort {$a->{LABEL} cmp $b->{LABEL}} @{ $self->{BDECLARE} };
+    for my $bdeclare (@bdeclares) {
+      $cfg->add_line (
+        LABEL => $bdeclare->{LABEL},
+        VALUE => $bdeclare->{VALUE},
+      );
+    }
+
+    # Blank line
+    $cfg->add_line;
+  }
+
+  # Add source directories to config file
+  $cfg->add_comment_block ('Project directory tree');
+
+  my $dest = $self->{RDEST}{MACHINE} ? $self->{RDEST} : $self->{DEST};
+  $cfg->add_line (
+    LABEL => $cfg_labels{DIR} . '::ROOT',
+    VALUE => $dest->{ROOTDIR},
+  );
+  for my $label (qw/SRC CFG/) {
+    my $dir = $label . 'DIR';
+    if ($dest->{$dir} ne catfile $dest->{ROOTDIR}, $subdir{$label}) {
+      $cfg->add_line (
+        LABEL => $cfg_labels{DIR} . '::' . $label,
+        VALUE => $dest->{$dir},
+      );
+    }
+  }
+
+  # Blank line
+  $cfg->add_line;
+
+  # Add source directories to config file
+  $cfg->add_comment_block ('Source directories');
+
+  $cfg->add_line (LABEL => $cfg_labels{SEARCH_SRC}, VALUE => '0',);
+  $cfg->add_line;
+
+  for my $srcdir (sort keys %{ $self->{SRCDIRS} }) {
+
+    if (-d $self->{SRCDIRS}{$srcdir}{DEST}) {
+      # Check whether pre-extracted source exists
+      my $pre_extracted = grep {
+        $_->extracted;
+      } @{ $self->{SRCDIRS}{$srcdir}{STACK} };
+
+      # Source directory
+      my $dest = undef;
+      if ($self->{RDEST}{MACHINE}) {
+        my $base = substr $self->{SRCDIRS}{$srcdir}{DEST},
+                          length ($self->{DEST}{SRCDIR}) + 1;
+        $dest    = catfile $self->{RDEST}{SRCDIR}, $base;
+      } else {
+        $dest = $self->{SRCDIRS}{$srcdir}{DEST}
+      }
+
+      # Source directory label
+      my $label = join '::', ($cfg_labels{SRCDIR}, $srcdir);
+
+      $cfg->add_line (LABEL => $label, VALUE => $dest,)
+    }
+
+  }
+
+  # Print lines to config file
+  $cfg->print_cfg ($self->{DEST}{BLD_CFG});
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_mirror_extract ();
+#
+# DESCRIPTION
+#   This internal method mirrors the current extract to a remote machine.
+# ------------------------------------------------------------------------------
+
+sub _mirror_extract {
+  my $self = shift;
+
+  # Needs mirroring only if remote machine is set
+  return unless $self->{RDEST}{MACHINE};
+
+  my $verbose = $self->config->verbose;
+
+  my $mirror = $self->config->setting (qw/TOOL MIRROR/);
+
+  if ($mirror eq 'rdist') {
+    # Use "rdist" to mirror extract
+
+    # Variable for "remote_logname@remote_machine"
+    my $rhost = $self->{RDEST}{LOGNAME} . '@' . $self->{RDEST}{MACHINE};
+
+    # Print distfile content to temporary file
+    my @distfile = ();
+    for my $my_label (qw/BLD_CFG EXT_CFG SRCDIR/) {
+      push @distfile, '( ' . $self->{DEST}{$my_label} . ' ) -> ' . $rhost . "\n";
+      push @distfile, '  install ' . $self->{RDEST}{$my_label} . ';' . "\n";
+    }
+
+    # Set up mirroring command (use "rdist" at the moment)
+    my $command = 'rdist -R';
+    $command   .= ' -q' unless $verbose > 1;
+    $command   .= ' -f - 1>/dev/null';
+
+    # Diagnostic
+    my $croak = 'Cannot execute "' . $command . '"';
+    if ($verbose > 2) {
+      print timestamp_command ($command, 'Start');
+      print '  ', $_ for (@distfile);
+    }
+
+    # Execute the mirroring command
+    open COMMAND, '|-', $command or croak $croak, ' (', $!, '), abort';
+    for my $line (@distfile) {
+      print COMMAND $line;
+    }
+    close COMMAND or croak $croak, ' (', $?, '), abort';
+
+    # Diagnostic
+    print timestamp_command ($command, 'End  ') if $verbose > 2;
+
+  } elsif ($mirror eq 'rsync') {
+    # Use "rsync" to mirror extract
+
+    my $rsh = $self->config->setting (qw/TOOL REMOTE_SHELL/);
+
+    # Variable for "remote_logname@remote_machine"
+    my $rhost = $self->{RDEST}{LOGNAME} . '@' . $self->{RDEST}{MACHINE};
+
+    for my $my_label (qw/BLD_CFG EXT_CFG SRCDIR/) {
+      my $rdir = dirname $self->{RDEST}{$my_label}; # remote container directory
+
+      {
+        # Create remote container directory with remote shell command
+        my @command = (
+          $rsh, $self->{RDEST}{MACHINE}, '-n', '-l', $self->{RDEST}{LOGNAME},
+          qw/mkdir -p/, $rdir,
+        );
+
+        # Execute command
+        &run_command (\@command, TIME => $verbose > 2);
+      }
+
+      {
+        # Build the rsync command
+        my @command = qw/rsync -a --exclude='.*' --delete-excluded/;
+        push @command, '-v' if $verbose > 2;
+        push @command, $self->{DEST}{$my_label};
+        push @command, $rhost . ':' . $rdir;
+
+        # Execute command
+        &run_command (\@command, TIME => $verbose > 2);
+      }
+    }
+
+  } else {
+    w_report $mirror, ': unknown mirroring tool, abort.';
+    return;
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/ReposBranch.pm
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/ReposBranch.pm	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/ReposBranch.pm	(revision 5058)
@@ -0,0 +1,516 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::ReposBranch
+#
+# DESCRIPTION
+#   This class contains methods for gathering information for a repository
+#   branch. It currently supports Subversion repository and local user
+#   directory.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+package Fcm::ReposBranch;
+
+# Standard pragma
+use warnings;
+use strict;
+
+# Standard modules
+use Carp;
+use File::Spec;
+use File::Spec::Functions;
+use File::Basename;
+use File::Find;
+
+# FCM component modules
+use Fcm::Util;
+use Fcm::Timer;
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $branch = Fcm::ReposBranch->new (
+#     CONFIG  => $config,
+#     PACKAGE => $package,
+#     TAG     => $tag,
+#     REPOS   => $repos,
+#     VERSION => $version,
+#     TYPE    => $type,
+#   );
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::ReposBranch class.
+#
+# ARGUMENTS
+#   CONFIG  - reference to a Fcm::Config instance
+#   PACKAGE - package name of which this repository belongs
+#   TAG     - "tag" name of this branch of the repository
+#   REPOS   - repository branch root URL/path
+#   VERSION - this version of the branch is used by the extract
+#   TYPE    - repository type
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self = {
+    CONFIG  => (exists $args{CONFIG}  ? $args{CONFIG}  : &main::cfg),
+    PACKAGE => (exists $args{PACKAGE} ? $args{PACKAGE} : undef),
+    TAG     => (exists $args{TAG}     ? $args{TAG}     : undef),
+    REPOS   => (exists $args{REPOS}   ? $args{REPOS}   : undef),
+    VERSION => (exists $args{VERSION} ? $args{VERSION} : undef),
+    TYPE    => (exists $args{TYPE}    ? $args{TYPE}    : undef),
+
+    # Use this list of directories in this branch
+    DIRS    => {},
+
+    # Expand this list of directories in this branch
+    EXPDIRS => {},
+  };
+
+  bless $self, $class;
+  return $self;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $config = $branch->config;
+#
+# DESCRIPTION
+#   This method returns a reference to the Fcm::Config instance.
+# ------------------------------------------------------------------------------
+
+sub config {
+  my $self = shift;
+
+  return $self->{CONFIG};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $package = $branch->package;
+#   $branch->package ($package);
+#
+# DESCRIPTION
+#   This method returns the package name of this repository branch. If an
+#   argument is specified, the name is set to the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub package {
+  my $self = shift;
+
+  if (@_) {
+    $self->{PACKAGE} = shift;
+  }
+
+  return $self->{PACKAGE};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $tag = $branch->tag;
+#   $branch->tag ($tag);
+#
+# DESCRIPTION
+#   This method returns the tag name of this repository branch. If an
+#   argument is specified, the name is set to the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub tag {
+  my $self = shift;
+
+  if (@_) {
+    $self->{TAG} = shift;
+  }
+
+  return $self->{TAG};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $repos = $branch->repos;
+#   $branch->repos ($repos);
+#
+# DESCRIPTION
+#   This method returns the URL/path name of this repository branch. If an
+#   argument is specified, the URL/path is set to the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub repos {
+  my $self = shift;
+
+  if (@_) {
+    $self->{REPOS} = shift;
+  }
+
+  return $self->{REPOS};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $version= $branch->version;
+#   $branch->version ($version);
+#
+# DESCRIPTION
+#   This method returns the revision number of this repository branch. If an
+#   argument is specified, the revision is set to the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub version {
+  my $self = shift;
+
+  if (@_) {
+    $self->{VERSION} = shift;
+  }
+
+  return $self->{VERSION};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $type = $branch->type;
+#   $branch->type ($type);
+#
+# DESCRIPTION
+#   This method returns the repository type ("svn" or "user"). If an
+#   argument is specified, the type is set to the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub type {
+  my $self = shift;
+
+  if (@_) {
+    $self->{TYPE} = shift;
+  }
+
+  return $self->{TYPE};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $dir = $branch->dir ($name);
+#   $branch->dir ($name, $dir);
+#
+# DESCRIPTION
+#   This method returns the path to the source directory with sub-package name
+#   $name. If $dir is specified, the path is set to its value.
+# ------------------------------------------------------------------------------
+
+sub dir {
+  my $self = shift;
+  my $name = shift;
+
+  if (@_) {
+    $self->{DIRS}{$name} = $_[0];
+  }
+
+  if (exists $self->{DIRS}{$name}) {
+    return $self->{DIRS}{$name};
+
+  } else {
+    return undef;
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   %dirs = $branch->dirs;
+#
+# DESCRIPTION
+#   This method returns a hash containing the source directories of this
+#   repository. The keys of the hash are the sub-package names, and the values
+#   of the hash are the URL/path to the source directories.
+# ------------------------------------------------------------------------------
+
+sub dirs {
+  my $self = shift;
+
+  return %{ $self->{DIRS} };
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $dir = $branch->expdir ($name);
+#   $branch->expdir ($name, $dir);
+#
+# DESCRIPTION
+#   This method returns the path to the expandable source directory with
+#   sub-package name $name. If $dir is specified, the path is set to its
+#   value.
+# ------------------------------------------------------------------------------
+
+sub expdir {
+  my $self = shift;
+  my $name = shift;
+
+  if (@_) {
+    $self->{EXPDIRS}{$name} = $_[0];
+  }
+
+  if (exists $self->{EXPDIRS}{$name}) {
+    return $self->{EXPDIRS}{$name};
+
+  } else {
+    return undef;
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   %dirs = $branch->expdirs;
+#
+# DESCRIPTION
+#   This method returns a hash containing the expandable source directories of
+#   this repository. The keys of the hash are the sub-package names, and the
+#   values of the hash are the URL/path to the expandable source directories.
+# ------------------------------------------------------------------------------
+
+sub expdirs {
+  my $self = shift;
+
+  return %{ $self->{EXPDIRS} };
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $branch->expand_version_tag;
+#
+# DESCRIPTION
+#   This method expands the VERSION of the current repository branch to a
+#   revision number.
+# ------------------------------------------------------------------------------
+
+sub expand_version_tag {
+  my $self = shift;
+
+  if ($self->type eq 'svn') {
+    # Expand revision keyword
+    my $rev = expand_rev_keyword (
+      REV  => $self->version,
+      URL  => $self->repos,
+      HEAD => 1,
+    );
+
+    # Find out whether the specified revision is current or not
+    if (uc ($self->version) ne 'HEAD' and $self->config->verbose > 1) {
+      # Get last commit revision
+      my $lc_rev = expand_rev_keyword (
+        REV => 'HEAD',
+        URL => $self->repos,
+      );
+
+      # Print information if used rev is less than the last commit rev
+      print 'Info: using rev ', $rev, ' of ', $self->repos,
+            ', last commit rev is ', $lc_rev, ".\n"
+        if $rev < $lc_rev;
+    }
+
+    $self->version ($rev) if $rev ne $self->version;
+
+  } elsif ($self->type eq 'user') {
+    return;
+
+  } else {
+    e_report $self->repos, ': repository type "', $self->type,
+             '" not supported.';
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $branch->expand_path;
+#
+# DESCRIPTION
+#   This method expands the relative path names of sub-directories to full
+#   path names. It returns 1 on success.
+# ------------------------------------------------------------------------------
+
+sub expand_path {
+  my $self = shift;
+
+  my $separator = $self->{CONFIG}->setting (qw/MISC DIR_SEPARATOR/);
+
+  if ($self->type eq 'svn') {
+    # SVN repository
+    # Do nothing unless there is a declared repository for this branch
+    return unless $self->{REPOS};
+
+    # Remove trailing /
+    $self->{REPOS} =~ s/$separator+$//;
+
+    # Consider all declared (expandable) sub-directories
+    for my $name (qw/DIRS EXPDIRS/) {
+      for my $dir (keys %{ $self->{$name} }) {
+        # Do nothing if declared sub-directory is quoted as a full URL
+        next if &is_url ($self->{$name}{$dir});
+
+        # Expand sub-directory to full URL
+        $self->{$name}{$dir} = $self->{REPOS} . (
+          $self->{$name}{$dir} ? ($separator . $self->{$name}{$dir}) : ''
+        );
+      }
+    }
+    # Note: "catfile" cannot be used in the above statement because it has
+    #       the tendency of removing a slash from double slashes.
+
+  } elsif ($self->type eq 'user') {
+    # Local user directories
+
+    # Expand leading ~ for all declared (expandable) sub-directories
+    for my $name (qw/DIRS EXPDIRS/) {
+      for my $dir (keys %{ $self->{$name} }) {
+        $self->{$name}{$dir} = expand_tilde $self->{$name}{$dir};
+      }
+    }
+
+    # A top directory for the source is declared
+    if ($self->{REPOS}) {
+      # Expand leading ~ for the top directory
+      $self->{REPOS} = expand_tilde $self->{REPOS};
+
+      # Get the root directory of the file system
+      my $rootdir = File::Spec->rootdir ();
+
+      # Expand top directory to absolute path, if necessary
+      $self->{REPOS} = File::Spec->rel2abs ($self->{REPOS})
+        if $self->{REPOS} !~ m/^$rootdir/;
+
+      # Remove trailing /
+      $self->{REPOS} =~ s/$separator+$//;
+
+      # Consider all declared (expandable) sub-directories
+      for my $name (qw/DIRS EXPDIRS/) {
+        for my $dir (keys %{ $self->{$name} }) {
+          # Do nothing if declared sub-directory is quoted as a full path
+          next if $self->{$name}{$dir} =~ m#^$rootdir#;
+
+          # Expand sub-directory to full path
+          $self->{$name}{$dir} = $self->{$name}{$dir}
+                               ? catfile ($self->{REPOS}, $self->{$name}{$dir})
+                               : $self->{REPOS};
+        }
+      }
+    }
+
+  } else {
+    e_report $self->repos, ': repository type "', $self->type,
+             '" not supported.';
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $branch->expand_all;
+#
+# DESCRIPTION
+#   This method searches the expandable source directories recursively for
+#   source directories containing regular files. These sub-directories are
+#   then added to the source directory list. The method returns total number
+#   of sub-directories to be extracted from the current repository.
+# ------------------------------------------------------------------------------
+
+sub expand_all {
+  my $self = shift;
+
+  my %dirs = ();
+
+  # Directory separator for SVN repository
+  my $separator = $self->{CONFIG}->setting (qw/MISC DIR_SEPARATOR/);
+
+  if ($self->type eq 'user') {
+    for my $rootname (keys %{ $self->{EXPDIRS} }) {
+      my %subdirs = find_srcdir $self->{EXPDIRS}{$rootname}, $rootname, '::';
+
+      for my $key (keys %subdirs) {
+        $dirs{$key} = $subdirs{$key};
+      }
+    }
+
+  } elsif  ($self->type eq 'svn') {
+    for my $rootname (keys %{ $self->{EXPDIRS} }) {
+      # Execute the "svn ls -R" command
+      my @lines   = &run_command (
+        [qw/svn ls -R/, $self->{EXPDIRS}{$rootname} . '@' . $self->{VERSION}],
+        METHOD => 'qx', TIME => $self->config->verbose > 2,
+      );
+
+      # Get list of sub-directories containing source files
+      for my $line (@lines) {
+        chomp $line;
+        next if $line =~ /$separator$/;
+
+        my $dir = dirname $line;
+
+        my @pck = split /::/, $rootname;
+        push @pck, (File::Spec->splitdir ($dir)) unless $dir eq '.';
+        my $pck = join '::', @pck;
+
+        my $val = $self->{EXPDIRS}{$rootname};
+        $val   .= $separator . $dir unless $dir eq '.';
+
+        $dirs{$pck} = $val;
+      }
+    }
+
+  } else {
+    e_report $self->repos, ': repository type "', $self->type,
+             '" not supported.';
+  }
+
+  for my $key (keys %dirs) {
+    $self->{DIRS}{$key} = $dirs{$key};
+  }
+
+  return scalar keys %dirs;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $branch->add_base_dirs ($base);
+#
+# DESCRIPTION
+#   Add a list of source directories to the current branch based on the set
+#   provided by $base, which must be a reference to a Fcm::ReposBranch
+#   instance. It returns the total number of used sub-directories in the
+#   current repositories.
+# ------------------------------------------------------------------------------
+
+sub add_base_dirs {
+  my $self = shift;
+  my $base = shift;
+
+  my %base_dirs = $base->dirs;
+
+  for my $key (keys %base_dirs) {
+    # Remove repository root from base directories
+    if ($base_dirs{$key} eq $base->repos) {
+      $base_dirs{$key} = '';
+
+    } else {
+      $base_dirs{$key} = substr $base_dirs{$key}, length ($base->repos) + 1;
+    }
+
+    # Append base directories to current repository root
+    $self->dir ($key, $base_dirs{$key}); 
+  }
+
+  # Expand relative path names of sub-directories
+  $self->expand_path;
+
+  return scalar keys %{ $self->{DIRS} };
+  
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/SrcDirLayer.pm
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/SrcDirLayer.pm	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/SrcDirLayer.pm	(revision 5058)
@@ -0,0 +1,481 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::SrcDirLayer
+#
+# DESCRIPTION
+#   This class contains methods to manipulate the extract of a source
+#   directory from a branch of a (Subversion) repository.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+package Fcm::SrcDirLayer;
+
+# Standard pragma
+use warnings;
+use strict;
+
+# Standard modules
+use Carp;
+use File::Spec;
+use File::Spec::Functions;
+use File::Basename;
+use File::Path;
+use File::Compare;
+
+# FCM component modules
+use Fcm::Util;
+use Fcm::Timer;
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $layer = Fcm::SrcDirLayer->new (
+#     CONFIG    => $config,
+#     NAME      => $dir,
+#     PACKAGE   => $package,
+#     TAG       => $tag,
+#     LOCATION  => $loc,
+#     REPOSROOT => $repos,
+#     VERSION   => $ver,
+#     TYPE      => $type,
+#     COMMIT    => $com,
+#     EXTRACTED => $ext,
+#     CACHEDIR  => $cac,
+#   );
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::SrcDirLayer class.
+#
+# ARGUMENTS
+#   CONFIG    - reference to a Fcm::Config instance
+#   NAME      - sub-package name of the source directory
+#   PACKAGE   - top level package name of which the current repository belongs
+#   TAG       - package/revision tag of the current repository branch
+#   LOCATION  - location of the source directory in the branch
+#   REPOSROOT - repository root URL
+#   VERSION   - revision of the repository branch
+#   TYPE      - type of the repository branch ("svn" or "user")
+#   COMMIT    - revision at which the source directory was changed
+#   EXTRACTED - is this branch already extracted?
+#   CACHEDIR  - cache directory for this directory branch
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self = {
+    CONFIG    => (exists $args{CONFIG}    ? $args{CONFIG}    : &main::cfg),
+    NAME      => (exists $args{NAME}      ? $args{NAME}      : undef),
+    PACKAGE   => (exists $args{PACKAGE}   ? $args{PACKAGE}   : undef),
+    TAG       => (exists $args{TAG}       ? $args{TAG}       : undef),
+    LOCATION  => (exists $args{LOCATION}  ? $args{LOCATION}  : undef),
+    REPOSROOT => (exists $args{REPOSROOT} ? $args{REPOSROOT} : undef),
+    VERSION   => (exists $args{VERSION}   ? $args{VERSION}   : undef),
+    TYPE      => (exists $args{TYPE}      ? $args{TYPE}      : undef),
+    COMMIT    => (exists $args{COMMIT}    ? $args{COMMIT}    : undef),
+    EXTRACTED => (exists $args{EXTRACTED} ? $args{EXTRACTED} : undef),
+    CACHEDIR  => (exists $args{CACHEDIR}  ? $args{CACHEDIR}  : undef),
+
+    # List of source files in this directory branch
+    FILES     => [],
+  };
+
+  bless $self, $class;
+  return $self;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $config = $layer->config;
+#
+# DESCRIPTION
+#   This method returns a reference to the Fcm::Config instance.
+# ------------------------------------------------------------------------------
+
+sub config {
+  my $self = shift;
+
+  return $self->{CONFIG};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $name = $layer->name;
+#   $layer->name ($name);
+#
+# DESCRIPTION
+#   This method returns the sub-package name of the current source directory.
+#   If an argument is specified, the sub-package name is set to the value of
+#   the argument.
+# ------------------------------------------------------------------------------
+
+sub name {
+  my $self = shift;
+
+  if (@_) {
+    $self->{NAME} = shift;
+  }
+
+  return $self->{NAME};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $package = $layer->package;
+#   $layer->package ($package);
+#
+# DESCRIPTION
+#   This method returns the top level package name in which the current source
+#   directory belongs. If an argument is specified, the package name is set to
+#   the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub package {
+  my $self = shift;
+
+  if (@_) {
+    $self->{PACKAGE} = shift;
+  }
+
+  return $self->{PACKAGE};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $tag = $layer->tag;
+#   $layer->tag ($tag);
+#
+# DESCRIPTION
+#   This method returns the branch/revision tag of the current repository
+#   branch. If an argument is specified, the tag is set to the value of the
+#   argument.
+# ------------------------------------------------------------------------------
+
+sub tag {
+  my $self = shift;
+
+  if (@_) {
+    $self->{TAG} = shift;
+  }
+
+  return $self->{TAG};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $location = $layer->location;
+#   $layer->location ($location);
+#
+# DESCRIPTION
+#   This method returns the URL/location of the source directory in the
+#   branch. If an argument is specified, the location is set to the value of
+#   the argument.
+# ------------------------------------------------------------------------------
+
+sub location {
+  my $self = shift;
+
+  if (@_) {
+    $self->{LOCATION} = shift;
+  }
+
+  return $self->{LOCATION};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $reposroot = $layer->reposroot;
+#   $layer->reposroot ($reposroot);
+#
+# DESCRIPTION
+#   This method returns the URL/location of the repository root of this
+#   branch. If an argument is specified, the location is set to the value of
+#   the argument.
+# ------------------------------------------------------------------------------
+
+sub reposroot {
+  my $self = shift;
+
+  if (@_) {
+    $self->{REPOSROOT} = shift;
+  }
+
+  return $self->{REPOSROOT};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $version = $layer->version;
+#   $layer->version ($version);
+#
+# DESCRIPTION
+#   This method returns the revision number of this branch. If an argument is
+#   specified, the revision number is set to the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub version {
+  my $self = shift;
+
+  if (@_) {
+    $self->{VERSION} = shift;
+  }
+
+  return $self->{VERSION};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $type = $layer->type;
+#   $layer->type ($type);
+#
+# DESCRIPTION
+#   This method returns the repository type ("svn" or "user"). If an argument is
+#   specified, the type is set to the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub type {
+  my $self = shift;
+
+  if (@_) {
+    $self->{TYPE} = shift;
+  }
+
+  return $self->{TYPE};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $version = $layer->commit;
+#   $layer->commit ($version);
+#
+# DESCRIPTION
+#   This method returns the last modified revision of the source directory in
+#   the branch. If an argument is specified, this revision is set to the value
+#   of the argument.
+# ------------------------------------------------------------------------------
+
+sub commit {
+  my $self = shift;
+
+  if (@_) {
+    $self->{COMMIT} = shift;
+  }
+
+  return $self->{COMMIT};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $extracted = $layer->extracted;
+#   $layer->extracted ($extracted);
+#
+# DESCRIPTION
+#   This method returns the "extracted flag" of the source directory branch
+#   If an argument is specified, the flag is set to the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub extracted {
+  my $self = shift;
+
+  if (@_) {
+    $self->{EXTRACTED} = shift;
+  }
+
+  return $self->{EXTRACTED};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $dir = $layer->cachedir;
+#   $layer->cachedir ($dir);
+#
+# DESCRIPTION
+#   This method returns the cache directory of the source directory branch
+#   If an argument is specified, the cache directory is set to the value of
+#   the argument.
+# ------------------------------------------------------------------------------
+
+sub cachedir {
+  my $self = shift;
+
+  if (@_) {
+    my $dir = shift;
+    $self->{CACHEDIR} = $dir;
+  }
+
+  return $self->{CACHEDIR};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $dir = $layer->localdir;
+#
+# DESCRIPTION
+#   This method returns the user or cache directory for the current revision
+#   of the repository branch.
+# ------------------------------------------------------------------------------
+
+sub localdir {
+  my $self = shift;
+
+  return $self->user ? $self->{LOCATION} : $self->{CACHEDIR};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @files = $layer->files;
+#
+# DESCRIPTION
+#   This method returns a list of regular files in this directory branch.
+#   This method should only be called after a successful operation of the
+#   get_files method that will be described below.
+# ------------------------------------------------------------------------------
+
+sub files {
+  my $self = shift;
+
+  return @{ $self->{FILES} };
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $user = $layer->user;
+#
+# DESCRIPTION
+#   This method returns the string "user" if the current source directory
+#   branch is a local directory. Otherwise, it returns "undef".
+# ------------------------------------------------------------------------------
+
+sub user {
+  my $self = shift;
+
+  return $self->{TYPE} eq 'user' ? 'user' : undef;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $version = $layer->get_commit;
+#
+# DESCRIPTION
+#   If the current repository type is "svn", this method attempts to obtain
+#   the revision in which the branch is last committed. On a successful
+#   operation, it returns this revision number. Otherwise, it returns
+#   "undef".
+# ------------------------------------------------------------------------------
+
+sub get_commit {
+  my $self = shift;
+
+  if ($self->type eq 'svn') {
+    # Execute the "svn info" command
+    my @lines   = &run_command (
+      [qw/svn info/, $self->{LOCATION} . '@' . $self->{VERSION}],
+      METHOD => 'qx', TIME => $self->config->verbose > 2,
+    );
+
+    my $rev;
+    for (@lines) {
+      if (/^Last\s+Changed\s+Rev\s*:\s*(\d+)/i) {
+        $rev = $1;
+        last;
+      }
+    }
+
+    # Commit version of this source directory
+    $self->{COMMIT} = $rev;
+
+    return $self->{COMMIT};
+
+  } elsif ($self->type eq 'user') {
+    return;
+
+  } else {
+    e_report 'Repository type "', $self->type, '" not supported.';
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $layer->update_cache;
+#
+# DESCRIPTION
+#   If the current repository type is "svn", this method attempts to extract
+#   the current version source directory from the current branch from the
+#   repository, sending the output to the cache directory. It returns true on
+#   a successful operation, or false if the repository is not of type "svn".
+# ------------------------------------------------------------------------------
+
+sub update_cache {
+  my $self = shift;
+
+  return unless $self->{CACHEDIR};
+
+  # Create cache extract destination, if necessary
+  my $dirname = dirname $self->{CACHEDIR};
+  mkpath $dirname if not -d $dirname;
+
+  e_report $dirname, ': cannot write to cache, abort.'
+    unless -d $dirname and -w $dirname;
+  
+  if ($self->type eq 'svn') {
+    # Set up the extract command, "svn export --force -q -N"
+    my @command = (
+      qw/svn export --force -q -N/,
+      $self->{LOCATION} . '@' . $self->{VERSION},
+      $self->{CACHEDIR},
+    );
+
+    &run_command (\@command, TIME => $self->config->verbose > 2);
+
+  } elsif ($self->type eq 'user') {
+    return;
+
+  } else {
+    e_report 'Repository type "', $self->type, '" not supported.';
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @files = $layer->get_files;
+#
+# DESCRIPTION
+#   This method returns a list of files in the cache or local user directory
+#   of the current version of the source directory in the current branch.
+# ------------------------------------------------------------------------------
+
+sub get_files {
+  my $self = shift;
+
+  # Get a list of files in the cache (or local user) directory
+  my @files = ();
+
+  opendir (DIR, $self->localdir)
+    or die $self->localdir, ': cannot read directory';
+
+  while (my $file = readdir DIR) {
+    next if $file =~ /^\.\.?/;                   # ignore . and .. and hidden
+    next if $file =~ /~$/;                       # ignore emacs swap files
+    next if -d catfile ($self->localdir, $file); # ignore sub-directories
+    push @files, $file;
+  }
+  closedir DIR;
+
+  # Return the (base name) of the list of files
+  $self->{FILES} = \@files;
+  return @files;
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/SrcFile.pm
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/SrcFile.pm	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/SrcFile.pm	(revision 5058)
@@ -0,0 +1,1538 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::SrcFile
+#
+# DESCRIPTION
+#   This class contains methods to manipulate the build process of a source
+#   file of supported type.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+package Fcm::SrcFile;
+
+# Standard pragma
+
+use strict;
+use warnings;
+
+# Standard modules
+use Cwd;
+use Carp;
+use File::Basename;
+use File::Spec;
+use File::Spec::Functions;
+
+# FCM component modules
+use Fcm::Util;
+use Fcm::Timer;
+
+# Other modules
+use Ecmwf::Fortran90_stuff ();
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $srcfile = Fcm::SrcFile->new (
+#     CONFIG     => $config,
+#     SRCPACKAGE => $srcpackage,
+#     SRC        => $src,
+#     PPSRC      => $ppsrc,
+#     TYPE       => $type,
+#     SCAN       => $scan,
+#     EXEBASE    => $exebase,
+#     PCKCFG     => $pckcfg,
+#   );
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::SrcFile class.
+#
+# ARGUMENTS
+#   CONFIG     - reference to a Fcm::Config instance
+#   SRCPACKAGE - reference to the container Fcm::SrcPackage instance
+#   SRC        - source path of this file
+#   PPSRC      - pre-processed source path of this file
+#   TYPE       - type flag of this source file
+#   SCAN       - scan source file for dependency?
+#   EXEBASE    - name of executable
+#   PCKCFG     - this source file is modified by a package cfg?
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self = {
+    CONFIG     => exists $args{CONFIG}     ? $args{CONFIG}     : &main::cfg,
+    SRCPACKAGE => exists $args{SRCPACKAGE} ? $args{SRCPACKAGE} : undef,
+    SRC        => exists $args{SRC}        ? $args{SRC}        : undef,
+    PPSRC      => exists $args{PPSRC}      ? $args{PPSRC}      : undef,
+    TYPE       => exists $args{TYPE}       ? $args{TYPE}       : undef,
+    SCAN       => exists $args{SCAN}       ? $args{SCAN}       : 1,
+    EXEBASE    => exists $args{EXEBASE}    ? $args{EXEBASE}    : undef,
+    PCKCFG     => exists $args{PCKCFG}     ? $args{PCKCFG}     : undef,
+
+    PROGNAME   => undef,
+    LANG       => undef,
+    DEP        => {}, 
+    RULES      => {},
+  };
+  bless $self, $class;
+
+  return $self;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $config = $srcfile->config;
+#
+# DESCRIPTION
+#   This method returns a reference to the Fcm::Config instance.
+# ------------------------------------------------------------------------------
+
+sub config {
+  my $self = shift;
+
+  return $self->{CONFIG};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $srcpackage = $srcfile->srcpackage;
+#   $srcfile->srcpackage ($srcpackage);
+#
+# DESCRIPTION
+#   This method returns the reference to the container Fcm::SrcPackage of this
+#   source file. If an argument is specified, the reference is set to the
+#   value of the argument.
+# ------------------------------------------------------------------------------
+
+sub srcpackage {
+  my $self = shift;
+
+  if (@_) {
+    $self->{SRCPACKAGE} = shift;
+  }
+
+  return $self->{SRCPACKAGE};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $src = $srcfile->src;
+#   $srcfile->src ($src);
+#
+# DESCRIPTION
+#   This method returns the reference to the location of this source file. If
+#   an argument is specified, the location is set to the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub src {
+  my $self = shift;
+
+  if (@_) {
+    $self->{SRC} = shift;
+  }
+
+  return $self->{SRC};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $ppsrc = $srcfile->ppsrc;
+#   $srcfile->ppsrc ($ppsrc);
+#
+# DESCRIPTION
+#   This method returns the reference to the location of the pre-processed
+#   file of this source file. If an argument is specified, the location is set
+#   to the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub ppsrc {
+  my $self = shift;
+
+  if (@_) {
+    $self->{PPSRC} = shift;
+  }
+
+  return $self->{PPSRC};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $time = $srcfile->mtime;
+#
+# DESCRIPTION
+#   This method returns the last modified time of the source file. If a
+#   pre-processed version of the source file exists, it returns the last
+#   modified time of the pre-processed source file instead.
+# ------------------------------------------------------------------------------
+
+sub mtime {
+  my $self = shift;
+
+  return $self->{PPSRC} ? (stat $self->{PPSRC})[9] : (stat $self->{SRC})[9];
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $base = $srcfile->base;
+#
+# DESCRIPTION
+#   This method returns the base name of the source file.
+# ------------------------------------------------------------------------------
+
+sub base {
+  my $self = shift;
+
+  return basename ($self->{SRC});
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $ppbase = $srcfile->ppbase;
+#
+# DESCRIPTION
+#   This method returns the base name of the pre-processed source file.
+# ------------------------------------------------------------------------------
+
+sub ppbase {
+  my $self = shift;
+
+  return basename ($self->{PPSRC});
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $interfacebase = $srcfile->interfacebase;
+#
+# DESCRIPTION
+#   This method returns the base name of the F9X interface file.
+# ------------------------------------------------------------------------------
+
+sub interfacebase {
+  my $self   = shift;
+  my $return = undef;
+
+  if ($self->is_type_or (qw/FORTRAN FPP/) and
+      uc ($self->select_tool ('GENINTERFACE')) ne 'NONE' and
+      not $self->is_type_or (qw/PROGRAM MODULE/)) {
+
+    my $flag = lc ($self->select_tool ('INTERFACE'));
+    my $ext  = $self->config->setting (qw/OUTFILE_EXT INTERFACE/);
+
+    $return = ($flag eq 'program' ? $self->intname : $self->root) . $ext;
+  }
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $root = $srcfile->root;
+#
+# DESCRIPTION
+#   This method returns the root name (i.e. base name without file extension)
+#   of the source file.
+# ------------------------------------------------------------------------------
+
+sub root {
+  my $self = shift;
+
+  (my $root = $self->base) =~ s/\.\w+$//;
+
+  return $root;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $ext = $srcfile->ext;
+#
+# DESCRIPTION
+#   This method returns the file extension of the source file.
+# ------------------------------------------------------------------------------
+
+sub ext {
+  my $self = shift;
+
+  return substr $self->base, length ($self->root);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $ppext = $srcfile->ppext;
+#
+# DESCRIPTION
+#   This method returns the file extension of the pre-processed source file.
+# ------------------------------------------------------------------------------
+
+sub ppext {
+  my $self = shift;
+
+  return substr $self->ppbase, length ($self->root);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $dir = $srcfile->dir;
+#
+# DESCRIPTION
+#   This method returns the dir name of the source file.
+# ------------------------------------------------------------------------------
+
+sub dir {
+  my $self = shift;
+
+  return dirname ($self->{SRC});
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $ppdir = $srcfile->ppdir;
+#
+# DESCRIPTION
+#   This method returns the dir name of the pre-processed source file.
+# ------------------------------------------------------------------------------
+
+sub ppdir {
+  my $self = shift;
+
+  return dirname ($self->{PPSRC});
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $progname = $srcfile->progname();
+#   $srcfile->progname ($progname);
+#
+# DESCRIPTION
+#   This method returns the name of the first program unit in a Fortran source
+#   file. If an argument is specified, the name is set to the value of the
+#   argument.
+# ------------------------------------------------------------------------------
+
+sub progname {
+  my $self = shift;
+
+  if (@_) {
+    $self->{PROGNAME} = $_[0];
+  }
+
+  return $self->{PROGNAME};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $intname = $srcfile->intname ();
+#
+# DESCRIPTION
+#   This method returns the internal name of the source file.
+# ------------------------------------------------------------------------------
+
+sub intname {
+  my $self = shift;
+
+  return $self->progname ? $self->progname : lc ($self->root);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $objbase = $srcfile->objbase ();
+#
+# DESCRIPTION
+#   If the source file contains a compilable procedure, this method returns
+#   the name of the object file.
+# ------------------------------------------------------------------------------
+
+sub objbase {
+  my $self   = shift;
+  my $return = undef;
+
+  if ($self->is_type ('SOURCE')) {
+    my $ext = $self->config->setting (qw/OUTFILE_EXT OBJ/);
+
+    if ($self->is_type_or (qw/FORTRAN FPP/)) {
+      $return = $self->progname . $ext if $self->progname;
+
+    } else {
+      $return = $self->intname . $ext;
+    }
+  }
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $donebase = $srcfile->donebase ();
+#
+# DESCRIPTION
+#   This method returns the DONE file for a source file containing a compilable
+#   procedure, or the IDONE file for an include file.
+# ------------------------------------------------------------------------------
+
+sub donebase {
+  my $self   = shift;
+  my $return = undef;
+
+  if ($self->is_type ('SOURCE')) {
+    if ($self->objbase and not $self->is_type ('PROGRAM')) {
+      $return = $self->intname . $self->config->setting (qw/OUTFILE_EXT DONE/);
+    }
+
+  } elsif ($self->is_type ('INCLUDE')) {
+    $return = $self->base . $self->config->setting (qw/OUTFILE_EXT IDONE/);
+  }
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $exebase = $srcfile->exebase ();
+#   $srcfile->exebase ($exebase);
+#
+# DESCRIPTION
+#   If the source file contains a compilable main program, this method returns
+#   the executable name. If an argument is specified, the executable name is
+#   set to the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub exebase {
+  my $self = shift;
+
+  if (@_) {
+    $self->{EXEBASE} = $_[0];
+  }
+
+  my $return;
+
+  if ($self->objbase and $self->is_type ('PROGRAM')) {
+    if ($self->config->setting ('EXE_NAME', $self->root)) {
+      $return = $self->config->setting ('EXE_NAME', $self->root);
+
+    } elsif ($self->{EXEBASE}) {
+      $return = $self->{EXEBASE};
+
+    } else {
+      $return = $self->root . $self->config->setting (qw/OUTFILE_EXT EXE/);
+    }
+  }
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $base = $srcfile->flagsbase ([$flag]);
+#
+# DESCRIPTION
+#   If the source file contains a compilable program unit, it returns the base
+#   name of the compiler flags-file. If $flag is set, it returns the base name
+#   of the flags file as specified by $flag. The value of $flag can be:
+#     FLAGS   - compiler flags flags-file (default)
+#     PPKEYS  - pre-processor keys (i.e. macro definitions) flags-file
+#     LD      - linker flags-file
+#     LDFLAGS - linker flags flags-file
+# ------------------------------------------------------------------------------
+
+sub flagsbase {
+  my ($self, $flag) = @_;
+  $flag             = 'FLAGS' if not $flag;
+  my $return        = undef;
+
+  if ($self->is_type ('SOURCE')) {
+    if ($flag eq 'FLAGS' or $flag eq 'PPKEYS') {
+      my %src_tool = %{ $self->config->setting ('SRC_TOOL') };
+
+      if ($self->lang and exists $src_tool{$self->lang}{$flag}) {
+        $return = join ('__', (
+          $src_tool{$self->lang}{$flag}, $self->srcpackage->name, $self->root,
+        )) . $self->config->setting (qw/OUTFILE_EXT FLAGS/);
+      }
+
+    } elsif ($self->is_type ('PROGRAM')) {
+      $return = join ('__', ($flag, $self->srcpackage->name, $self->root)) .
+                $self->config->setting (qw/OUTFILE_EXT FLAGS/);
+    }
+  }
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   %dep   = $srcfile->dep;
+#   @files = $srcfile->dep ($type);
+#   $srcfile->dep (\%dep);
+#
+# DESCRIPTION
+#   This method returns the dependencies of this source file. If no argument
+#   is set, the method returns the dependency hash of this source file. The
+#   keys of the hash are the names of the files this source files depends on
+#   and the values of the hash are the dependency types of the corresponding
+#   files. If an argument is specified and the argument is a normal string,
+#   the method returns the keys of the dependency hash, which have their
+#   corresponding values equal to $type. If an argument is specified and the
+#   argument is a reference to a hash, the reference to the dependency hash of
+#   the current source file is re-set to point to the reference of this new
+#   hash.
+# ------------------------------------------------------------------------------
+
+sub dep {
+  my $self = shift;
+
+  if (@_) {
+    if (ref $_[0] eq 'HASH') {
+      $self->{DEP} = $_[0];
+
+    } else {
+      my $type = $_[0];
+      return grep {
+        $self->{DEP}{$_} eq $type;
+      } keys %{ $self->{DEP} };
+    }
+  }
+
+  return %{ $self->{DEP} };
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $srcfile->add_dep ($target, $type);
+#
+# DESCRIPTION
+#   This method adds (or modifies) a dependency to the dependency hash of the
+#   source file. The argument $type is the type of the dependency and the
+#   argument $target is the dependency target.
+# ------------------------------------------------------------------------------
+
+sub add_dep {
+  my $self = shift;
+  my ($target, $type) = @_;
+
+  $self->{DEP}{$target} = $type;
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @pklist = $self->get_package_list ();
+#
+# DESCRIPTION
+#   This method returns a list of package names associated with this source
+#   file. The list begins with the top level container package to the
+#   sub-package name of the current source file.
+# ------------------------------------------------------------------------------
+
+sub get_package_list {
+  my $self = shift;
+
+  my @pknames = ();
+
+  my @packages = split /__/, $self->srcpackage->name;
+  push @packages, $self->root;
+
+  for my $i (0 .. $#packages) {
+    push @pknames, join ('__', (@packages[0 .. $i]));
+  }
+
+  return @pknames;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $pckcfg = $srcfile->pckcfg ();
+#   $srcfile->pckcfg ($pckcfg);
+#
+# DESCRIPTION
+#   This method returns the name of the flag to indicate whether this source
+#   file is modified by a package level configuration file. If an argument is
+#   specified, the flag is set to the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub pckcfg {
+  my $self = shift;
+
+  if (@_) {
+    $self->{PCKCFG} = $_[0];
+  }
+
+  return $self->{PCKCFG};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $srcfile->scan ();
+#   $srcfile->scan ($flag);
+#
+# DESCRIPTION
+#   This method returns the "scan" flag that determines whether the source
+#   file needs to be scanned for dependency. If an argument is specified, the
+#   flag is set to the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub scan {
+  my $self = shift;
+
+  if (@_) {
+    $self->{SCAN} = $_[0];
+  }
+
+  return $self->{SCAN};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $type = $srcfile->type;
+#   $srcfile->type ($type);
+#
+# DESCRIPTION
+#   This method returns the type flag of the source file. If an argument is
+#   specified, the flag is set to the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub type {
+  my $self = shift;
+
+  if (@_) {
+    $self->{TYPE} = shift;
+  }
+
+  return $self->{TYPE};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $srcfile->is_type ($type1[, $type2, ...]);
+#
+# DESCRIPTION
+#   This method returns true if current file is a known type matching all the
+#   arguments.
+# ------------------------------------------------------------------------------
+
+sub is_type {
+  my $self    = shift;
+  my @intypes = @_;
+  my $rc      = 0;
+
+  if ($self->{TYPE}) {
+    my @types = split /::/, $self->{TYPE};
+
+    for my $intype (@intypes) {
+      $rc = grep {uc $_ eq uc $intype} @types;
+      last unless $rc;
+    }
+
+  }
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $srcfile->is_type_or ($type1[, $type2, ...]);
+#
+# DESCRIPTION
+#   This method returns true if current file is a known type matching any of
+#   the arguments.
+# ------------------------------------------------------------------------------
+
+sub is_type_or {
+  my $self    = shift;
+  my @intypes = @_;
+  my $rc      = 0;
+
+  if ($self->{TYPE}) {
+    my @types = split /::/, $self->{TYPE};
+
+    for my $intype (@intypes) {
+      $rc = grep {uc $_ eq uc $intype} @types;
+      last if $rc;
+    }
+
+  }
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $lang = $srcfile->lang ();
+#
+# DESCRIPTION
+#   This method returns the language name of the source file if it contains
+#   compilable source of a supported language.
+# ------------------------------------------------------------------------------
+
+sub lang {
+  my $self = shift;
+
+  if ((not $self->{LANG}) and $self->is_type ('SOURCE')) {
+    my %src_tool = %{ $self->config->setting ('SRC_TOOL') };
+
+    for my $key (keys %src_tool) {
+      if ($self->is_type ($key)) {
+        $self->{LANG} = $key;
+        last;
+      }
+    }
+  }
+
+  return $self->{LANG};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $srcfile->determine_type;
+#
+# DESCRIPTION
+#   This method determines whether the source file is a type known to the
+#   build system. If so, it sets the "type" flag.
+# ------------------------------------------------------------------------------
+
+sub determine_type {
+  my $self = shift;
+
+  if (not $self->{TYPE}) {
+    # Do not set a type if the file name matches the "ignore" list
+    my @ignore = split /,/, $self->config->setting ('INFILE_IGNORE');
+
+    for (@ignore) {
+      return if $self->base eq $_;
+    }
+  }
+
+  if (not $self->{TYPE}) {
+    # Determine file type by comparing its extension with supported ones
+    my %known_ext = %{ $self->config->setting ('INFILE_EXT') };
+    my $ext       = $self->ext ? substr ($self->ext, 1) : 0;
+    $self->{TYPE} = $known_ext{$ext} if $ext and exists $known_ext{$ext};
+  }
+
+  if (not $self->{TYPE}) {
+    # Determine file type by comparing its name with known patterns
+    my %known_pat = %{ $self->config->setting ('INFILE_PAT') };
+    for my $pat (keys %known_pat) {
+      if ($self->base =~ /$pat/) {
+        $self->{TYPE} = $known_pat{$pat};
+        last;
+      }
+    }
+  }
+
+  if (-s $self->{SRC} and -T $self->{SRC} and not $self->{TYPE}) {
+    # Determine file type by inspecting its first line (text file only)
+    if (open SRC, '<', $self->{SRC}) {
+      my $line = <SRC>;
+      close SRC;
+
+      my %known_txt = %{ $self->config->setting ('INFILE_TXT') };
+      for my $txt (keys %known_txt) {
+        if ($line =~ /^#!.*$txt/) {
+          $self->{TYPE} = $known_txt{$txt};
+          last;
+        }
+      }
+    }
+  }
+
+  if ($self->is_type_or (qw/FORTRAN FPP/)) {
+    # Determine whether source file is a main Fortran program or module
+    if (open SRC, '<', $self->{SRC}) {
+      while (my $line = <SRC>) {
+        if ($line =~ /^\s*(PROGRAM|MODULE)\b/i) {
+          $self->{TYPE} = $self->{TYPE} . '::' . uc ($1);
+          last;
+
+        } elsif ($line =~ /^\s*BLOCK\s*DATA\b/i) {
+          $self->{TYPE} = $self->{TYPE} . '::' . 'BLOCKDATA';
+          last;
+        }
+      }
+      close SRC;
+    }
+
+  } elsif ($self->is_type (qw/C/)) {
+    # Determine whether source file is a main C program
+    if (open SRC, '<', $self->{SRC}) {
+      while (my $line = <SRC>) {
+        next unless $line =~ /int\s*main\s*\(/i;
+        $self->{TYPE} = $self->{TYPE} . '::PROGRAM';
+        last;
+      }
+      close SRC;
+    }
+  }
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @pp_src = @{ $srcfile->pre_process () };
+#
+# DESCRIPTION
+#   This method invokes the pre-processor on the source file. It returns a
+#   reference to an array containing the lines of the pre-processed source if
+#   the pre-processor command succeeded.
+# ------------------------------------------------------------------------------
+
+sub pre_process {
+  my $self = shift;
+
+  # Support only Fortran and C source files
+  return unless $self->is_type_or (qw/FPP C/);
+
+  # List of include directories
+  my @inc = @{ $self->config->setting (qw/PATH INC/) };
+
+  # Build the pre-processor command according to file type
+  my $name    = $self->is_type ('FPP') ? 'FPP' : 'CPP';
+  my %tool    = %{ $self->config->setting ('TOOL') };
+
+  # The pre-processor command and its options
+  my @command = ($tool{$name});
+  my @ppflags = split /\s+/, $self->select_tool ($name . 'FLAGS');
+
+  # List of defined macros, add "-D" in front of each macro
+  my @ppkeys  = split /\s+/, $self->select_tool ($name . 'KEYS');
+  @ppkeys     = map {($tool{$name . '_DEFINE' }. $_)} @ppkeys;
+
+  # Add "-I" in front of each include directories
+  @inc        = map {($tool{$name . '_INCLUDE'}. $_)} @inc;
+
+  push @command, (@ppflags, @ppkeys, @inc, $self->base);
+
+  my $verbose = $self->config->verbose;
+  my $cwd     = cwd;
+
+  # Change to container directory of source file
+  print 'cd ', $self->dir, "\n" if $verbose > 1;
+  chdir $self->dir;
+
+  # Execute the command, getting the output lines
+  my @outlines = &run_command (
+    \@command, METHOD => 'qx', PRINT => $verbose > 1, TIME => $verbose > 2,
+  );
+
+  # Change back to original directory
+  print 'cd ', $cwd, "\n" if $self->config->verbose > 1;
+  chdir $cwd;
+
+  return \@outlines;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @interface_block = @{ $srcfile->gen_interface () };
+#
+# DESCRIPTION
+#   This method invokes the Fortran 9x interface block generator to generate
+#   an interface block for the current source file. It returns a reference to
+#   an array containing the lines of the interface block.
+# ------------------------------------------------------------------------------
+
+sub gen_interface {
+  my $self = shift;
+
+  my $generator = $self->select_tool ('GENINTERFACE');
+
+  my $src      = $self->{PPSRC} ? $self->{PPSRC} : $self->{SRC};
+  my @outlines = ();
+
+  if ($generator eq 'f90aib') {
+    # Use F90AIB
+
+    # Open pipeline to interface file generator and read its output
+    my $devnull = File::Spec->devnull;
+    my $command = $generator;
+    $command   .= " <'" . $src . "'" . " 2>'" . $devnull . "'";
+    my $croak   = $command . ' failed';
+
+    print timestamp_command ($command, 'Start') if $self->config->verbose > 2;
+    open COMMAND, '-|', $command or croak $croak, ' (', $!, '), abort';
+    @outlines = readline 'COMMAND';
+    close COMMAND or croak $croak, ' (', $?, '), abort';
+    print timestamp_command ($command, 'End  ') if $self->config->verbose > 2;
+
+  } elsif ($generator eq 'ECMWF') {
+    # Use ECMWF interface generator
+  
+    # Read source file into an array
+    open FILE, '<', $src or croak 'Cannot open "', $src, '" (', $!, '), abort';
+    my @src_lines = <FILE>;
+    close FILE;
+  
+    # Process standalone subroutines and functions only
+    if (not grep /^\s*(?:program|module)\b/i, @src_lines) {
+      print timestamp_command ('Analyse: ' . $self->src, 'Start')
+        if $self->config->verbose > 2;
+
+      my @statements = ();
+      my %prog_info  = ();
+  
+      # Set name of source file
+      &Ecmwf::Fortran90_stuff::fname ($src);
+  
+      # Parse lines in source
+      &Ecmwf::Fortran90_stuff::setup_parse ();
+
+      # Expand continuation lines in source
+      &Ecmwf::Fortran90_stuff::expcont (\@src_lines, \@statements);
+  
+      # Analyse statements in source
+      $Ecmwf::Fortran90_stuff::study_called = 0;
+      &Ecmwf::Fortran90_stuff::study (\@statements, \%prog_info);
+  
+      # Source code is not a module
+      if (not $prog_info{is_module}) {
+        my @interface_block = ();
+        my @line_hash       = ();
+  
+        # Create an interface block for the program unit
+        &Ecmwf::Fortran90_stuff::create_interface_block (
+          \@statements,
+          \@interface_block,
+        );
+
+        # Put continuation lines back
+        &Ecmwf::Fortran90_stuff::cont_lines (
+          \@interface_block,
+          \@outlines,
+          \@line_hash,
+        );
+      }
+
+      print timestamp_command ('Analyse: ' . $self->src, 'End')
+        if $self->config->verbose > 2;
+    }
+
+  } elsif (uc ($generator) eq 'NONE') {
+    print $self->root, ': interface generation is switched off', "\n"
+      if $self->config->verbose > 2;
+
+  } else {
+    e_report 'Error: Unknown Fortran 9x interface generator: ', $generator, '.';
+  }
+
+  return \@outlines;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $tool = $self->select_tool ($name);
+#
+# DESCRIPTION
+#   This method selects the correct "tool" for the current source file by
+#   following the name of its container package. The argument $name must be
+#   the generic name of the "tool" to be selected. The method returns the
+#   value of the selected tool.
+# ------------------------------------------------------------------------------
+
+sub select_tool {
+  my $self  = shift;
+  my $name  = shift;
+
+  return undef unless $name;
+
+  my @pknames = $self->get_package_list ();
+
+  my %tool    = %{ $self->config->setting ('TOOL') };
+
+  for my $pkname (reverse @pknames) {
+    my $cur_name = join '__', ($name, $pkname);
+    return $tool{$cur_name} if exists $tool{$cur_name};
+  }
+
+  return exists $tool{$name} ? $tool{$name} : '';
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $srcfile->scan_dependency ();
+#   $rc = $srcfile->scan_dependency (HEADER_ONLY => 1);
+#
+# DESCRIPTION
+#   This method scans the source file for dependencies. If no argument is
+#   specified, the method scans the pre-processed source file if it exists.
+#   Otherwise, the original source file is scanned. If HEADER_ONLY is
+#   specified, only pre-processing header dependencies are scanned from the
+#   source file. (The HEADER_ONLY flag should only be specified if "ppsrc" is
+#   not already specified.) This method returns the number of 1 on success.
+# ------------------------------------------------------------------------------
+
+sub scan_dependency {
+  my $self = shift;
+  my %args = @_;
+
+  my $header_only = exists $args{HEADER_ONLY} ? $args{HEADER_ONLY} : 0;
+
+  return 0 unless $self->{SCAN};
+  return 0 unless $self->{TYPE};
+
+  my $src = $self->{PPSRC} ? $self->{PPSRC} : $self->{SRC};
+  return 0 unless $src;
+
+  # Determine what dependencies are supported by this known type
+  my %types = $header_only
+              ? %{ $self->config->setting ('PP_DEP_TYPE') }
+              : %{ $self->config->setting ('DEP_TYPE') };
+
+  # List of excluded dependencies
+  my %excl_dep = %{ $self->config->setting ('EXCL_DEP') };
+
+  # Package list
+  my @pknames = $self->get_package_list ();
+
+  my @depends = ();
+  for my $key (keys %types) {
+    # Check if current file is a type of file requiring dependency scan
+    next unless $self->is_type ($key);
+    
+    # Get list of dependency type for this file
+    DEPEND: for my $depend ((split /::/, $types{$key})) {
+      # Ignore a dependency type if the dependency is in the exclude list
+      if (exists $excl_dep{$depend}) {
+        # Global exclude
+        next DEPEND if exists $excl_dep{$depend}{''};
+
+        # Sub-package exclude
+        for my $pkname (@pknames) {
+          next DEPEND if exists $excl_dep{$depend}{$pkname};
+        }
+      }
+
+      # Add to dependency list for current file
+      push @depends, $depend;
+    }
+  }
+
+  # Scan dependencies, if necessary ...
+  if (@depends) {
+    # Print diagnostic
+    print timestamp_command ('scan dependency in file: ' . $src, 'Start')
+      if $self->config->verbose > 2;
+
+    open FILE, '<', $src or croak 'Cannot open "', $src, '" (', $!, ')';
+    my @lines = readline 'FILE';
+    close FILE;
+
+    # List of dependency patterns
+    my %dep_pattern = %{ $self->config->setting ('DEP_PATTERN') };
+
+    LINE: for my $line (@lines) {
+      # Ignore empty lines
+      next LINE if $line =~ /^\s*$/;
+
+      # Fortran source, also scan for program unit name
+      if (! $header_only and ! $self->progname) {
+        if ($self->is_type ('SOURCE') and $self->is_type_or (qw/FPP FORTRAN/)) {
+          my $pfx_pttn = '(?:(?:ELEMENTAL|(?:RECURSIVE(?:\s+PURE)?|' .
+                         'PURE(?:\s+RECURSIVE)?))\s+)?';
+          my $spc_pttn = '(?:(?:CHARACTER|COMPLEX|DOUBLE\s*PRECISION|INTEGER|' .
+                         'LOGICAL|REAL|TYPE)(?:\s*\(.+\)|\s*\*\d+\s*)??\s+)?';
+
+          if ($line =~ /^\s*PROGRAM\s+(\w+)/i) {
+            # Matches the beginning of a named main program
+            $self->progname (lc $1);
+            next LINE;
+
+          } elsif ($line =~ /^\s*MODULE\s+(\w+)/i) {
+            my $keyword = $1;
+
+            if (uc ($keyword) ne 'PROCEDURE') {
+              # Matches the beginning of a module
+              $self->progname (lc $keyword);
+              next LINE;
+            }
+
+          } elsif ($line =~ /^\s*BLOCK\s*DATA\s+(\w+)/i) {
+            # Matches the beginning of a named block data program unit
+            $self->progname (lc $1);
+            next LINE;
+
+          } elsif ($line =~ /^\s*$pfx_pttn SUBROUTINE\s+(\w+)/ix) {
+            # Matches the beginning of a subroutine
+            $self->progname (lc $1);
+            next LINE;
+
+          } elsif ($line =~ /^\s*$pfx_pttn $spc_pttn FUNCTION\s+(\w+)/ix) {
+            # Matches the beginning of a function
+            $self->progname (lc $1);
+            next LINE;
+          }
+        }
+      }
+
+      # Scan known dependencies
+      for my $depend (@depends) {
+        # Check if a pattern exists for the current dependency
+        next unless exists $dep_pattern{$depend};
+
+        # Attempt to match the pattern
+        my $pattern = $dep_pattern{$depend};
+
+        if ($line =~ /$pattern/i) {
+          my $match = $1;
+
+          # $match may contain multiple items delimited by space
+          NAME: for my $name (split /\s+/, $match) {
+            # Skip dependency if it is in the exclusion list
+            my $key = uc ($depend . '::' . $name);
+
+            if (exists $excl_dep{$key}) {
+              # Exclude this dependency, in the global list
+              next NAME if exists $excl_dep{$key}{''};
+
+              # Exclude this dependency, current sub-package
+              for my $pkname (@pknames) {
+                next NAME if exists $excl_dep{$key}{$pkname};
+              }
+            }
+
+            # Add this dependency to the list
+            $self->add_dep ($name, $depend);
+          }
+
+          next LINE;
+        }
+      }
+    }
+
+    # Diagnostic messages
+    if ($self->config->verbose > 2) {
+      my $base = $self->ppsrc ? $self->ppbase : $self->base;
+
+      print $self->srcpackage->name, ': ', $base;
+      print ': scanned ', scalar (@lines), ' lines for ';
+      print 'header ' if $header_only;
+      print 'dependencies: ', scalar (keys %{ $self->{DEP} }), "\n";
+      print timestamp_command ('scan dependency in file: ' . $src, 'End');
+    }
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   %rules = $srcfile->required_rules ();
+#
+# DESCRIPTION
+#   This method returns a hash in the following format:
+#     %rules = (
+#       target => {ACTION => action, DEP => [dependencies], ...},
+#       ...    => {...},
+#     );
+#   where the 1st rank keys are the available targets for building this source
+#   file, the second rank keys are ACTION and DEP. The value of ACTION is the
+#   action for building the target, which can be "COMPILE", "LOAD", "TOUCH",
+#   "CP" or "AR". The value of DEP is a refernce to an array containing a list
+#   of dependencies suitable for insertion into the Makefile.
+# ------------------------------------------------------------------------------
+
+sub required_rules {
+  my $self = shift;
+
+  if (not keys %{ $self->{RULES} }) {
+    my %outfile_ext = %{ $self->config->setting ('OUTFILE_EXT') };
+
+    if ($self->is_type (qw/SOURCE/)) {
+      # Source file
+      # ------------------------------------------------------------------------
+      # Determine the whether the language of the source file is supported
+      my %src_tool = %{ $self->config->setting ('SRC_TOOL') };
+
+      return () unless $self->lang;
+
+      # Compile object
+      # ------------------------------------------------------------------------
+      if ($self->objbase) {
+        # Depends on the source file
+        my @dep = ($self->_makerule_srcfile);
+
+        # Depends on the compiler flags flags-file
+        my @flags;
+        push @flags, ('FLAGS' )
+          if $self->flagsbase ('FLAGS' );
+        push @flags, ('PPKEYS')
+          if $self->flagsbase ('PPKEYS') and not $self->ppsrc;
+
+        push @dep, $self->flagsbase ($_) for (@flags);
+
+        # Source file dependencies
+        for my $name (sort keys %{ $self->{DEP} }) {
+          # A Fortran 9X module, lower case object file name
+          if ($self->{DEP}{$name} eq 'USE') {
+            (my $root = $name) =~ s/\.\w+$//;
+            push @dep, lc ($root) . $outfile_ext{OBJ};
+
+          # An include file
+          } elsif ($self->{DEP}{$name} =~ /^(?:INC|H|INTERFACE)$/) {
+            push @dep, $name;
+          }
+        }
+
+        $self->{RULES}{$self->objbase} = {ACTION => 'COMPILE', DEP => \@dep};
+
+        # Touch flags-files
+        # ----------------------------------------------------------------------
+        for my $flag (@flags) {
+          next unless $self->flagsbase ($flag);
+
+          $self->{RULES}{$self->flagsbase ($flag)} = {
+            ACTION => 'TOUCH',
+            DEP    => [
+              $self->srcpackage->flagsbase ($src_tool{$self->lang}{$flag}),
+            ],
+            DEST   => '$(FCM_FLAGSDIR)',
+          };
+        }
+      }
+
+      if ($self->exebase) {
+        # Link into an executable
+        # ----------------------------------------------------------------------
+        my @dep = ();
+        push @dep, $self->objbase               if $self->objbase;
+        push @dep, $self->flagsbase ('LD'     ) if $self->flagsbase ('LD'     );
+        push @dep, $self->flagsbase ('LDFLAGS') if $self->flagsbase ('LDFLAGS');
+
+        # Depends on BLOCKDATA program units, for Fortran programs
+        my %blockdata = %{ $self->config->setting ('BLOCKDATA') };
+        my @blkobj    = ();
+
+        if ($self->is_type_or (qw/FPP FORTRAN/) and keys %blockdata) {
+          # List of BLOCKDATA object files
+          if (exists $blockdata{$self->exebase}) {
+            @blkobj = keys (%{ $blockdata{$self->exebase} });
+
+          } elsif (exists $blockdata{''}) {
+            @blkobj = keys (%{ $blockdata{''} });
+          }
+
+          for my $name (@blkobj) {
+            (my $root = $name) =~ s/\.\w+$//;
+            $name = $root . $outfile_ext{OBJ};
+            push @dep, $root . $outfile_ext{DONE};
+          }
+        }
+
+        # Extra executable dependencies
+        my %exe_dep = %{ $self->config->setting ('EXE_DEP') };
+        if (keys %exe_dep) {
+          my @exe_deps;
+          if (exists $exe_dep{$self->exebase}) {
+            @exe_deps = keys (%{ $exe_dep{$self->exebase} });
+
+          } elsif (exists $exe_dep{''}) {
+            @exe_deps = keys (%{ $exe_dep{''} });
+          }
+
+          my $pattern = '\\' . $outfile_ext{OBJ} . '$';
+
+          for my $name (@exe_deps) {
+            if ($name =~ /$pattern/) {
+              # Extra dependency is an object
+              (my $root = $name) =~ s/\.\w+$//;
+              push @dep, $root . $outfile_ext{DONE};
+
+            } else {
+              # Extra dependency is a sub-package
+              my $var;
+              if ($self->config->setting ('FCM_PCK_OBJECTS', $name)) {
+                # sub-package name contains unusual characters
+                $var = $self->config->setting ('FCM_PCK_OBJECTS', $name);
+
+              } else {
+                # sub-package name contains normal characters
+                $var = $name ? join ('__', ('OBJECTS', $name)) : 'OBJECTS';
+              }
+
+              push @dep, '$(' . $var . ')';
+            }
+          }
+        }
+
+        # Source file dependencies
+        for my $name (sort keys %{ $self->{DEP} }) {
+          (my $root = $name) =~ s/\.\w+$//;
+
+          # Lowercase name for object dependency
+          $root = lc ($root) unless $self->{DEP}{$name} =~ /^(?:INC|H)$/;
+
+          # Select "done" file extension
+          if ($self->{DEP}{$name} =~ /^(?:INC|H)$/) {
+            push @dep, $name . $outfile_ext{IDONE};
+
+          } else {
+            push @dep, $root . $outfile_ext{DONE};
+          }
+        }
+
+        $self->{RULES}{$self->exebase} = {
+          ACTION => 'LOAD', DEP => \@dep, BLOCKDATA => \@blkobj,
+        };
+
+        # Touch Linker flags-file
+        # ----------------------------------------------------------------------
+        for my $flag (qw/LD LDFLAGS/) {
+          $self->{RULES}{$self->flagsbase ($flag)} = {
+            ACTION => 'TOUCH',
+            DEP    => [$self->srcpackage->flagsbase ($flag)],
+            DEST   => '$(FCM_FLAGSDIR)',
+          };
+        }
+
+      }
+
+      if ($self->donebase) {
+        # Touch done file
+        # ----------------------------------------------------------------------
+        my @dep = ($self->objbase);
+
+        for my $name (sort keys %{ $self->{DEP} }) {
+          (my $root = $name) =~ s/\.\w+$//;
+
+          # Lowercase name for object dependency
+          $root = lc ($root) unless $self->{DEP}{$name} =~ /^(?:INC|H)$/;
+
+          # Select "done" file extension
+          if ($self->{DEP}{$name} =~ /^(?:INC|H)$/) {
+            push @dep, $name . $outfile_ext{IDONE};
+
+          } else {
+            push @dep, $root . $outfile_ext{DONE};
+          }
+        }
+
+        $self->{RULES}{$self->donebase} = {
+          ACTION => 'TOUCH', DEP => \@dep, DEST => '$(FCM_DONEDIR)',
+        };
+      }
+      
+      if ($self->interfacebase) {
+        # Interface target
+        # ----------------------------------------------------------------------
+        # Source file dependencies
+        my @dep = ();
+        for my $name (sort keys %{ $self->{DEP} }) {
+          # Depends on Fortran 9X modules
+          push @dep, lc ($name) . $outfile_ext{OBJ}
+            if $self->{DEP}{$name} eq 'USE';
+        }
+
+        $self->{RULES}{$self->interfacebase} = {DEP => \@dep};
+      }
+
+    } elsif ($self->is_type ('INCLUDE')) {
+      # Copy include target
+      # ------------------------------------------------------------------------
+      my @dep = ($self->_makerule_srcfile);
+
+      for my $name (sort keys %{ $self->{DEP} }) {
+        # A Fortran 9X module, lower case object file name
+        if ($self->{DEP}{$name} eq 'USE') {
+          (my $root = $name) =~ s/\.\w+$//;
+          push @dep, lc ($root) . $outfile_ext{OBJ};
+
+        # An include file
+        } elsif ($self->{DEP}{$name} =~ /^(?:INC|H|INTERFACE)$/) {
+          push @dep, $name;
+        }
+      }
+
+      $self->{RULES}{$self->base} = {
+        ACTION => 'CP', DEP => \@dep, DEST => '$(FCM_INCDIR)',
+      };
+
+      # Touch IDONE file
+      # ------------------------------------------------------------------------
+      if ($self->donebase) {
+        my @dep = ($self->_makerule_srcfile);
+
+        for my $name (sort keys %{ $self->{DEP} }) {
+          (my $root = $name) =~ s/\.\w+$//;
+
+          # Lowercase name for object dependency
+          $root   = lc ($root) unless $self->{DEP}{$name} =~ /^(?:INC|H)$/;
+
+          # Select "done" file extension
+          if ($self->{DEP}{$name} =~ /^(?:INC|H)$/) {
+            push @dep, $name . $outfile_ext{IDONE};
+
+          } else {
+            push @dep, $root . $outfile_ext{DONE};
+          }
+        }
+
+        $self->{RULES}{$self->donebase} = {
+          ACTION => 'TOUCH', DEP => \@dep, DEST => '$(FCM_DONEDIR)',
+        };
+      }
+
+    } elsif ($self->is_type_or (qw/EXE SCRIPT/)) {
+      # Copy executable file
+      # ------------------------------------------------------------------------
+      my @dep = ($self->_makerule_srcfile);
+
+      # Depends on dummy copy file, if file is an "always build type"
+      push @dep, $self->config->setting (qw/MISC CPDUMMY/)
+        if $self->is_type_or (
+          split (/,/, $self->config->setting ('ALWAYS_BUILD_TYPE'))
+        );
+
+      # Depends on other executable files
+      for my $name (sort keys %{ $self->{DEP} }) {
+        push @dep, $name if $self->{DEP}{$name} eq 'EXE';
+      }
+
+      $self->{RULES}{$self->base} = {
+        ACTION => 'CP', DEP => \@dep, DEST => '$(FCM_BINDIR)',
+      };
+
+    } elsif ($self->is_type ('LIB')) {
+      # Archive object library
+      # ------------------------------------------------------------------------
+      my @dep;
+      for my $name (sort keys %{ $self->{DEP} }) {
+        next unless $self->{DEP}{$name} eq 'OBJ';
+
+        if ($name =~ /^\$\(\w+\)$/) {
+          # Dependency is a Makefile variable
+          push @dep, $name;
+
+        } else {
+          # Dependency is an object
+          (my $root = $name) =~ s/\.\w+$//;
+          push @dep, lc ($root) . $outfile_ext{OBJ};
+        }
+      }
+
+      $self->{RULES}{$self->base} = {ACTION => 'AR', DEP => \@dep};
+    }
+  }
+
+  return %{ $self->{RULES} };
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = $srcfile->write_makerules ();
+#
+# DESCRIPTION
+#   This method returns a string containing the "Make" rules for building the
+#   source file.
+# ------------------------------------------------------------------------------
+
+sub write_makerules {
+  my $self  = shift;
+  my $mk    = '';
+  my %rules = $self->required_rules;
+  my $nl    = " \\\n" . ' ' x 10;
+
+  for my $target (sort keys %rules) {
+    $mk .= $target . ':';
+    
+    for my $dep (@{ $rules{$target}{DEP} }) {
+      $mk .= $nl . $dep;
+    }
+
+    $mk .= "\n";
+
+    if (exists $rules{$target}{ACTION}) {
+      if ($rules{$target}{ACTION} eq 'COMPILE') {
+        if ($self->lang) {
+          $mk .= "\t" . 'fcm_internal compile:' . substr ($self->lang, 0, 1) .
+                 ' ' . $self->srcpackage->name . ' $< $@';
+          $mk .= ' 1' if ($self->flagsbase ('PPKEYS') and not $self->ppsrc);
+          $mk .= "\n";
+        }
+
+      } elsif ($rules{$target}{ACTION} eq 'LOAD') {
+        $mk .= "\t" . 'fcm_internal load ' . $self->srcpackage->name . ' $< $@';
+        $mk .= ' ' . join (' ', @{ $rules{$target}{BLOCKDATA} })
+          if @{ $rules{$target}{BLOCKDATA} };
+        $mk .= "\n";
+
+      } elsif ($rules{$target}{ACTION} eq 'TOUCH') {
+        $mk .= "\t" . 'touch ' . catfile ($rules{$target}{DEST}, '$@') . "\n";
+
+      } elsif ($rules{$target}{ACTION} eq 'CP') {
+        $mk .= "\t" . 'cp $< ' . $rules{$target}{DEST} . "\n";
+        $mk .= "\t" . 'chmod u+w ' . catfile ($rules{$target}{DEST}, '$@') . "\n";
+
+      } elsif ($rules{$target}{ACTION} eq 'AR') {
+        $mk .= "\t" . 'fcm_internal archive $@ $(^F)' . "\n";
+      }
+    }
+
+    $mk .= "\n";
+  }
+
+  return $mk;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = $srcfile->_makerule_srcfile ();
+#
+# DESCRIPTION
+#   This internal method returns a string containing the location of the
+#   source file relative to a package source path. This string will be
+#   suitable for use in a "Make" rule file for FCM.
+# ------------------------------------------------------------------------------
+
+sub _makerule_srcfile {
+  my $self = shift;
+
+  my $return;
+  my @searchpath;
+  my $label;
+  my $dir;
+  my $base;
+
+  if ($self->ppsrc) {
+    $return     = $self->ppsrc;
+    @searchpath = $self->srcpackage->ppsearchpath;
+    $label      = 'PPSRCDIR';
+    $dir        = $self->ppdir;
+    $base       = $self->ppbase;
+
+  } else {
+    $return     = $self->src;
+    @searchpath = $self->srcpackage->searchpath;
+    $label      = 'SRCDIR';
+    $dir        = $self->dir;
+    $base       = $self->base;
+  }
+
+  $return = catfile $dir, $base;
+
+  # Use variable for directory name
+  # if container package name contains word characters only
+  if ($self->srcpackage->name =~ /^\w+$/) {
+    for my $i (0 .. $#searchpath) {
+      if ($dir eq $searchpath[$i]) {
+        my $returndir = '$(' . $label . $i . '__' . $self->srcpackage->name .
+                        ')';
+        $return = catfile $returndir, $base;
+        last;
+      }
+    }
+  }
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/SrcPackage.pm
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/SrcPackage.pm	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/SrcPackage.pm	(revision 5058)
@@ -0,0 +1,895 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::SrcPackage
+#
+# DESCRIPTION
+#   This is a class to process a source directory package. It uses the
+#   supplied inheritance hierarchy to obtain a list of source files of this
+#   package.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+package Fcm::SrcPackage;
+
+# Standard pragma
+use strict;
+use warnings;
+
+# Standard modules
+use Carp;
+use File::Spec::Functions;
+use File::Basename;
+use File::Path;
+
+# FCM component modules
+use Fcm::Util;
+use Fcm::SrcFile;
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $package = Fcm::SrcPackage->new (
+#     CONFIG     => $config,
+#     NAME       => $name,
+#     CURRENT    => $current,
+#     REQUIREPP  => $requirepp,
+#     NEWPP      => $newpp,
+#     SEARCHPATH => \@path,
+#   );
+#
+# DESCRIPTION
+#   This method constructs a new instance of the Fcm::SrcPackage class.
+#
+# ARGUMENTS
+#   CONFIG     - reference to a Fcm::Config instance
+#   NAME       - name of the source directory package
+#   CURRENT    - package declared in current build?
+#   REQUIREPP  - require pre-processing?
+#   NEWPP      - pre-process option has changed?
+#   SEARCHPATH - search path of files in the source package
+# ------------------------------------------------------------------------------
+
+sub new {
+  my $this  = shift;
+  my %args  = @_;
+  my $class = ref $this || $this;
+
+  my $self  = {
+    CONFIG     => exists $args{CONFIG}     ? $args{CONFIG}     : &main::cfg,
+    NAME       => exists $args{NAME}       ? $args{NAME}       : undef,
+    CURRENT    => exists $args{CURRENT}    ? $args{CURRENT}    : undef,
+    REQUIREPP  => exists $args{REQUIREPP}  ? $args{REQUIREPP}  : undef,
+    NEWPP      => exists $args{NEWPP}      ? $args{NEWPP}      : undef,
+    SEARCHPATH => exists $args{SEARCHPATH} ? $args{SEARCHPATH} : [],
+
+    # Reference to Fcm::CfgFile, source package configuration file
+    CFG       => undef,
+
+    # References to Fcm::SrcFile, list of source files
+    SRCFILE   => [],
+  };
+
+  bless $self, $class;
+  return $self;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $config = $package->config;
+#
+# DESCRIPTION
+#   This method returns a reference to the Fcm::Config instance.
+# ------------------------------------------------------------------------------
+
+sub config {
+  my $self = shift;
+
+  return $self->{CONFIG};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $name = $package->name;
+#   $package->name ($name);
+#
+# DESCRIPTION
+#   This method returns the name of this package. If an argument is specified,
+#   the name is set to the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub name {
+  my $self = shift;
+
+  if (@_) {
+    $self->{NAME} = shift;
+  }
+
+  return $self->{NAME};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $package->current;
+#   $package->current ($flag);
+#
+# DESCRIPTION
+#   This method returns the "current" flag of the source package. If an
+#   argument is specified, the flag is set to the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub current {
+  my $self = shift;
+
+  if (@_) {
+    $self->{CURRENT} = shift;
+  }
+
+  return $self->{CURRENT};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $package->requirepp;
+#   $package->requirepp ($flag);
+#
+# DESCRIPTION
+#   This method returns the "require PP" flag of the source package. If an
+#   argument is specified, the flag is set to the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub requirepp {
+  my $self = shift;
+
+  if (@_) {
+    $self->{REQUIREPP} = shift;
+  }
+
+  return $self->{REQUIREPP};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = $package->newpp;
+#   $package->newpp ($flag);
+#
+# DESCRIPTION
+#   This method returns the flag to denote whether pre-processor option for
+#   this source package has changed. If an argument is specified, the flag is
+#   set to the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub newpp {
+  my $self = shift;
+
+  if (@_) {
+    $self->{NEWPP} = shift;
+  }
+
+  return $self->{NEWPP};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $cfgfile = $package->cfg;
+#   $package->cfg ($cfgfile);
+#
+# DESCRIPTION
+#   This method returns a reference to a Fcm::CfgFile instance for the source
+#   package configuration file. If an argument is specified, the reference is
+#   set to the value of the argument.
+# ------------------------------------------------------------------------------
+
+sub cfg {
+  my $self = shift;
+
+  if (@_) {
+    $self->{CFG} = $_[0];
+  }
+
+  return $self->{CFG};
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @path = $package->searchpath;
+#   $package->searchpath (@path);
+#
+# DESCRIPTION
+#   This method returns the source file search path associated with this
+#   source package in the current build. If arguments are specified, the
+#   search path is replaced by the array in the argument list.
+# ------------------------------------------------------------------------------
+
+sub searchpath {
+  my $self = shift;
+
+  @{ $self->{SEARCHPATH} } = @_ if @_; 
+
+  return @{ $self->{SEARCHPATH} };
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @path = $package->ppsearchpath;
+#
+# DESCRIPTION
+#   This method returns the pre-processed source file search path associated
+#   with this source package in the current build.
+# ------------------------------------------------------------------------------
+
+sub ppsearchpath {
+  my $self = shift;
+
+  my @path = ();
+  my @name = split /__/, $self->name;
+
+  for my $ppsrcdir (@{ $self->config->setting (qw/PATH PPSRC/) }) {
+    push @path, catfile ($ppsrcdir, @name);
+  }
+
+  return @path;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $base = $package->flagsbase ($flag);
+#
+# DESCRIPTION
+#   Returns the base name of a flags-file, determined by $flag.
+# ------------------------------------------------------------------------------
+
+sub flagsbase {
+  my ($self, $flag) = @_;
+
+  return join ('__', ($flag, $self->name)) .
+         $self->config->setting (qw/OUTFILE_EXT FLAGS/);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @srcfile = $package->srcfile;
+#
+# DESCRIPTION
+#   This method returns a list of references to Fcm::SrcFile instances
+#   associated with this package.
+# ------------------------------------------------------------------------------
+
+sub srcfile {
+  my $self = shift;
+
+  return @{ $self->{SRCFILE} };
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $package->update_file_info ();
+#
+# DESCRIPTION
+#   This method updates the source file information of this package. Please
+#   note that information is only updated if the cache file for this package
+#   does not exist. For a package declared in the current build, the
+#   information is also updated if the cache file is out of date.
+# ------------------------------------------------------------------------------
+
+sub update_file_info {
+  my $self      = shift;
+
+  # Check if the cache file exists and up to date
+  my @cachepath = @{ $self->config->setting (qw/PATH CACHE/) };
+  my $cachefile = find_file_in_path ($self->_cache_basename, \@cachepath);
+
+  my $uptodate  = $cachefile ? 1 : 0;
+  if ($uptodate and $self->{CURRENT}) {
+    # Is cache file up to date compared with directory?
+    $uptodate = (stat $cachefile) [9] > (stat $self->{SEARCHPATH}[0]) [9];
+
+    # Is cache file up to date compared with each file?
+    if ($uptodate) {
+      my $dir = $self->{SEARCHPATH}[0];
+
+      if (opendir DIR, $dir) {
+        my @files = map {catfile $dir, $_} grep {!/^\.\.?/} readdir 'DIR';
+        closedir DIR;
+        $uptodate = (grep {(stat $cachefile) [9] > (stat) [9]} @files) ? 1 : 0;
+      }
+    }
+  }
+
+  # Read package source file information if it appears to be up to date
+  $uptodate = $self->_read_file_list_cache ($cachefile) if ($uptodate);
+
+  # Update package source file information if necessary
+  if (not $uptodate) {
+    # Get list of files by searching through the search path
+    my @files = ();
+    for my $dir (@{ $self->{SEARCHPATH} }) {
+      opendir DIR, $dir;
+      while (my $base = readdir 'DIR') {
+        next if $base =~ /^\./;
+
+        my $file = catfile $dir, $base;
+        next if -d $file;
+
+        push @files, $file unless grep {basename ($_) eq $base} @files;
+      }
+      closedir DIR;
+    }
+
+    # Declare new instances of source file objects
+    my @srcfile = ();
+    for my $file (@files) {
+      if (basename ($file) eq $self->config->setting (qw/CFG_NAME SRCPACKAGE/)) {
+        $self->{CFG} = Fcm::CfgFile->new (CONFIG => $self->config, SRC => $file);
+
+      } else {
+        my $srcfile = Fcm::SrcFile->new (
+          CONFIG     => $self->config,
+          SRC        => $file,
+          SRCPACKAGE => $self,
+        );
+
+        # Determine source file types
+        $srcfile->determine_type;
+
+        # Record files of known types
+        push @srcfile, $srcfile;
+      }
+    }
+
+    # Set each SRCFILE to reference the source file instances
+    $self->{SRCFILE} = \@srcfile;
+
+    # Decipher configuration file if necessary
+    $self->_decipher_cfg if $self->cfg;
+
+    # Write to a new cache file
+    $self->_update_file_list_cache ();
+
+    # Source package info updated. Make sure the "current" flag is set to true
+    $self->current (1);
+  }
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $up_to_date = $self->_read_file_list_cache ($file);
+#
+# DESCRIPTION
+#   This internal method reads the cache $file of this package and assigns the
+#   information to the SRCFILE list. It returns true if the cache appears to
+#   be up to date.
+# ------------------------------------------------------------------------------
+
+sub _read_file_list_cache {
+  my $self = shift;
+  my $file = shift;
+
+  my $cfg = Fcm::CfgFile->new (CONFIG => $self->config, SRC => $file);
+
+  # Read from config file
+  $cfg->read_cfg;
+  my @lines = $cfg->lines;
+
+  my %filetype = ();
+  my $uptodate = 1;
+  for my $line (@lines) {
+    next unless $line->{LABEL};
+
+    # On package declared in the current build, check that file is not deleted
+    if (not -f $line->{LABEL}) {
+      $uptodate = 0;
+      last;
+    }
+
+    $filetype{$line->{LABEL}} = $line->{VALUE};
+  }
+
+  # Assign to SRCFILE list if cache file is up to date
+  if ($uptodate) {
+    my @srcfiles = ();
+
+    for my $file (sort keys %filetype) {
+      if ($filetype{$file} eq 'SRCPACKAGECFG') {
+        $self->{CFG} = Fcm::CfgFile->new (CONFIG => $self->config, SRC => $file);
+
+      } else {
+        my $srcfile = Fcm::SrcFile->new (
+          CONFIG     => $self->config,
+          SRC        => $file,
+          TYPE       => $filetype{$file},
+          SRCPACKAGE => $self,
+        );
+
+        push @srcfiles, $srcfile;
+      }
+    }
+
+    $self->{SRCFILE} = [@srcfiles];
+
+    $self->_decipher_cfg if $self->cfg;
+  }
+
+  return $uptodate;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_update_file_list_cache (\@cachepath);
+#
+# DESCRIPTION
+#   This internal method updates the cache file of this package by writing
+#   current SRCFILE information to it. The argument @cachepath must be the
+#   search path of the build cache directory.
+# ------------------------------------------------------------------------------
+
+sub _update_file_list_cache {
+  my $self      = shift;
+  my @cachepath = @{ $self->config->setting (qw/PATH CACHE/) };
+
+  my $cfg = Fcm::CfgFile->new (CONFIG => $self->config);
+
+  if ($self->{CFG}) {
+    $cfg->add_line (LABEL => $self->cfg->src, VALUE => 'SRCPACKAGECFG')
+  }
+
+  for my $file (@{ $self->{SRCFILE} }) {
+    $cfg->add_line (LABEL => $file->src, VALUE => $file->type);
+  }
+
+  my $cachefile = catfile $cachepath[0], $self->_cache_basename;
+  $cfg->print_cfg ($cachefile);
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $basename = $self->_cache_basename ($type);
+#
+# DESCRIPTION
+#   This internal method returns the basename of a cache file for this
+#   package. If no argument is specified, it returns the package file list
+#   cache name. Otherwise, it returns the package file dependency cache name.
+# ------------------------------------------------------------------------------
+
+sub _cache_basename {
+  my $self = shift;
+  my $type = $_[0] ? $_[0] : 'PCKFILE';
+
+  return $self->{NAME} . $self->config->setting ('CACHE', $type);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $self->_decipher_cfg ();
+#
+# DESCRIPTION
+#   This internal method deciphers the CFG file associated with this source
+#   package.
+# ------------------------------------------------------------------------------
+
+sub _decipher_cfg {
+  my $self = shift;
+
+  $self->cfg->read_cfg;
+  my @lines = $self->cfg->lines;
+
+  my %cfg_label = %{ $self->config->setting ('CFG_LABEL') };
+
+  LINE: for my $line (@lines) {
+    my $label = $line->{LABEL};
+    my $value = $line->{VALUE};
+
+    next unless $label;
+    next if uc $label eq $cfg_label{CFGFILE}{TYPE};
+    next if uc $label eq $cfg_label{CFGFILE}{VERSION};
+
+    my ($prefix, $name) = split /::/, $label;
+
+    # Get name of file from the package cfg
+    my $srcfile;
+    if ($name) {
+      ($srcfile) = grep {$_->base eq $name} @{ $self->{SRCFILE} };
+
+      # Create new instance of Fcm::SrcFile if not already in package
+      if (not $srcfile) {
+        my $src    = find_file_in_path ($name, $self->{SEARCHPATH});
+        my $target = $name unless $src;
+
+        $srcfile   = Fcm::SrcFile->new (
+          CONFIG     => $self->config,
+          SRCPACKAGE => $self,
+          SRC        => $src ? $src : $name,
+          TARGET     => $target,
+          PCKCFG     => 1,
+        );
+        push @{ $self->{SRCFILE} }, $srcfile;
+
+      } else {
+        $srcfile->pckcfg (1);
+      }
+
+    } else {
+      w_report 'Warning: ', $line->{SRC}, ': LINE ', $line->{NUMBER},
+               ': label "', $label, '" not recognised.';
+      next LINE;
+    }
+
+    $prefix = uc $prefix;
+    if ($prefix eq $cfg_label{TYPE}) {
+      # Type label of source file
+      $srcfile->type (uc $value);
+      $srcfile->scan (0) if $srcfile->is_type (qw/BINARY LIB/);
+      next LINE;
+
+    } elsif ($prefix eq $cfg_label{SCAN}) {
+      # Scan original file for dependency?
+      $srcfile->scan ($value);
+      next LINE;
+
+    } elsif ($prefix eq $cfg_label{TARGET}) {
+      # Name of build target for this source file
+      $srcfile->exebase ($value);
+      next LINE;
+
+    } elsif ($prefix eq $cfg_label{INTNAME}) {
+      # Program unit name of this source file
+      $srcfile->progname ($value);
+      next LINE;
+
+    } elsif ($prefix eq $cfg_label{DEP}) {
+      # Dependency of this source file
+      my ($type, $target) = split /::/, $value;
+      $srcfile->add_dep ($target, uc $type);
+      next LINE;
+
+    } else {
+      w_report 'Warning: ', $line->{SRC}, ': LINE ', $line->{NUMBER},
+               ': label "', $label, '" not recognised.';
+      next LINE;
+    }
+  }
+
+  return 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $package->scan_dependency ();
+#   $package->scan_dependency (HEADER_ONLY => 1);
+#
+# DESCRIPTION
+#   This method scans the dependency in each source file in this source
+#   package and updates the package dependency cache. If HEADER_ONLY is
+#   specified, it performs dependency scan for pre-processor headers only if
+#   this source package requires pre-processing.
+# ------------------------------------------------------------------------------
+
+sub scan_dependency {
+  my $self = shift;
+  my %args = @_;
+
+  # Search for include header dependencies only
+  my $header_only = exists $args{HEADER_ONLY} ? $args{HEADER_ONLY} : 0;
+
+  # Get list of source files
+  # If header dependencies only, only consider FPP, C and CPP files
+  my @srcfiles = $header_only
+                 ? grep {$_->is_type_or (qw/FPP C CPP/)} $self->srcfile
+                 : grep {$_->type} $self->srcfile;
+  return unless @srcfiles;
+
+  # Location of the cache
+  my @cachepath = @{ $self->config->setting (qw/PATH CACHE/) };
+  my $cachebase = $header_only
+                  ? $self->_cache_basename ('PCKPPDEPEND')
+                  : $self->_cache_basename ('PCKDEPEND');
+  my $cachefile = find_file_in_path ($cachebase, \@cachepath);
+
+  # Obtain old dependency information from cache file if it exists
+  my %dep     = ();
+  my %intname = ();
+
+  if ($cachefile) {
+    # Read the cache
+    my $cfg = Fcm::CfgFile->new (CONFIG => $self->config, SRC => $cachefile);
+    $cfg->read_cfg;
+    my @lines = $cfg->lines;
+
+    # Get list of source file base names
+    my %srcfilebase;
+    for (@srcfiles) {
+      my $base = $_->ppsrc ? $_->ppbase : $_->base;
+      $srcfilebase{$base} = 1;
+    }
+
+    for my $line (@lines) {
+      next unless $line->{LABEL};
+
+      # Label is either INTNAME or a dependency type name
+      # For INTNAME, value is the program unit name
+      # Otherwise, value is file::dependency
+      my $type = $line->{LABEL};
+      (my $file, my $depend) = split /::/, $line->{VALUE};
+
+      # Make sure $file exists in the list of source file base names
+      next unless exists $srcfilebase{$file};
+
+      if ($type eq 'INTNAME') {
+        $intname{$file} = $depend;
+
+      } else {
+        $dep{$file}{$depend} = $type;
+      }
+    }
+  }
+
+  # If a source file is newer than the cache file, re-scan dependency for that
+  # source file.
+  my $uptodate        = $cachefile ? 1 : 0;
+  my $cachefile_mtime = $cachefile ? (stat $cachefile) [9] : undef;
+  my $count           = 0;
+
+  for my $srcfile (@srcfiles) {
+    # Check modified time of source file
+    my $srcfile_mtime = $srcfile->mtime;
+
+    # If a package config file exists and it affects the source file,
+    # compare its timestamp with that of the source file
+    if ($srcfile->pckcfg) {
+      $srcfile_mtime = $self->cfg->mtime if not defined $srcfile_mtime;
+      $srcfile_mtime = ($self->cfg->mtime > $srcfile_mtime) ? $self->cfg->mtime
+                                                            : $srcfile_mtime;
+    }
+
+    # For files requiring PP, must re-scan if PP option has changed
+    my $rescan = ($self->newpp and $srcfile->is_type_or (qw/FPP C/)) ? 1 : 0;
+
+    if ($cachefile_mtime and $cachefile_mtime > $srcfile_mtime and ! $rescan) {
+      # No need to re-scan dependency, read dependency from cache
+      my $base = ($srcfile->ppsrc ? $srcfile->ppbase : $srcfile->base);
+
+      $srcfile->progname ($intname{$base}) if $intname{$base};
+      $srcfile->dep ($dep{$base})          if $dep{$base};
+
+    } else {
+      # Rescan dependency
+      $srcfile->progname (undef);
+      my $rc = $srcfile->scan_dependency (HEADER_ONLY => $header_only);
+      my %dp = $srcfile->dep;
+
+      # Get list of dependencies for updating the cache
+      my $base = ($srcfile->ppsrc ? $srcfile->ppbase : $srcfile->base);
+
+      $intname{$base} = $srcfile->progname;
+      $dep    {$base} = \%dp;
+
+      $uptodate = 0;
+      $count++ if $rc;
+    }
+  }
+
+  # Output diagnostic, if necessary
+  if ($self->config->verbose > 1 and $count) {
+    my $out =  $self->name . ': scanned ' . $count . ' file(s) for';
+    $out   .= ' header' if $header_only;
+    $out   .= ' dependency' . "\n";
+    print $out;
+  }
+
+  # Check whether package config file is newer than the dependency cache
+  if ($uptodate and $self->cfg) {
+    $uptodate = $cachefile_mtime > $self->cfg->mtime ? 1 : 0;
+  }
+
+  if (not $uptodate) {
+    # Update dependency cache file
+    my $cfg = Fcm::CfgFile->new (CONFIG => $self->config);
+
+    # Program unit name of source files
+    for my $file (keys %intname) {
+      next unless $intname{$file};
+
+      $cfg->add_line (
+        LABEL => 'INTNAME',
+        VALUE => $file . '::' . $intname{$file},
+      );
+    }
+
+    # Dependencies of source files
+    for my $file (keys %dep) {
+      for my $depend (keys %{ $dep{$file} }) {
+        $cfg->add_line (
+          LABEL => $dep{$file}{$depend},
+          VALUE => $file . '::' . $depend,
+        );
+      }
+    }
+
+    # Create an empty config file if no dependency in this source package
+    $cfg->add_line unless $cfg->lines;
+
+    # Write to config file
+    my $outfile = catfile $cachepath[0], $cachebase;
+    $cfg->print_cfg ($outfile);
+  }
+
+  return not $uptodate;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = $package->makerule_uptodate ();
+#
+# DESCRIPTION
+#   This method returns true if the make rule file for this source package
+#   is up to date.
+# ------------------------------------------------------------------------------
+
+sub makerule_uptodate {
+  my $self = shift;
+
+  my $return = 0;
+
+  if (not $self->newpp) {
+    # Check whether a Make rule file already exists
+    my $mkbase = $self->name . $self->config->setting (qw/OUTFILE_EXT MK/);
+    my $mkfile = find_file_in_path (
+      $mkbase,
+      $self->config->setting (qw/PATH BLD/),
+    );
+
+    # Check location of source package file type cache
+    my $pckfile = find_file_in_path (
+      $self->_cache_basename ('PCKFILE'),
+      $self->config->setting (qw/PATH CACHE/),
+    );
+
+    # Check location of source package dependency cache
+    my $pckdepend = find_file_in_path (
+      $self->_cache_basename ('PCKDEPEND'),
+      $self->config->setting (qw/PATH CACHE/),
+    );
+
+    # If make rule file exists, determine whether it is out of date
+    if ($pckdepend) {
+      if ($mkfile) {
+        my $pckfile_mt   = (stat $pckfile)  [9];
+        my $pckdepend_mt = (stat $pckdepend)[9];
+        my $mkfile_mt    = (stat $mkfile)   [9];
+
+        $return = 1 if $mkfile_mt >= $pckdepend_mt and $mkfile_mt >= $pckfile_mt;
+      }
+
+    } else {
+      $return = 1; # No cache file, no need to have a make rule
+    }
+  }
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $package->write_makerule ();
+#
+# DESCRIPTION
+#   This method writes to the I<Make> rule file of the current source package.
+# ------------------------------------------------------------------------------
+
+sub write_makerule {
+  my $self = shift;
+
+  # Package Make rule header
+  my $mk = '# Automatic Make rule for ' . $self->name . "\n\n";
+
+  # Set up variable for directory name
+  # if package name contains only word characters
+  my @searchpath   = $self->searchpath;
+  my @ppsearchpath = $self->ppsearchpath;
+
+  if ($self->name =~ /^\w+$/) {
+    # Package search path
+    my %path = (SRCDIR => \@searchpath, PPSRCDIR => \@ppsearchpath);
+
+    for my $key (keys %path) {
+      my $count = 0;
+      my @dirs  = @{ $path{$key} };
+
+      for my $i (0 .. $#dirs) {
+        next unless -d $dirs[$i];
+        $mk .= $key . $i . '__' . $self->name . ' = ' . $dirs[$i] . "\n";
+        $count++;
+      }
+
+      $mk .= "\n" if $count;
+    }
+  }
+
+  my $mk_out;
+
+  # Make rules for copying data files, if necessary
+  {
+    # Get a list of files with no associated type
+    my @files = grep {not $_->type} @{ $self->{SRCFILE} };
+
+    if (@files) {
+      my $target = $self->name . $self->config->setting (qw/OUTFILE_EXT ETC/);
+      $mk_out .= $target . ' :';
+
+      # Depends on all un-typed source files
+      my $nl = " \\\n" . ' ' x 10;
+      for my $file (@files) {
+        my $dir = $file->dir;
+
+        # Use variable for directory name
+        # if package name contains only word characters
+        if ($self->name =~ /^\w+$/) {
+          for my $i (0 .. $#searchpath) {
+            if ($dir eq $searchpath[$i]) {
+              $dir = '$(SRCDIR' . $i . '__' . $self->name . ')';
+              last;
+            }
+          }
+        }
+
+        $mk_out .= $nl . catfile ($dir, $file->base);
+      }
+
+      # Depends on dummy copy file, so there will be no dependency inheritance
+      $mk_out .= $nl . $self->config->setting (qw/MISC CPDUMMY/);
+
+      # Actions for target
+      $mk_out .= "\n";
+      $mk_out .= "\t" . 'cp $^ $(FCM_ETCDIR)' . "\n";
+      $mk_out .= "\t" . 'touch ' . catfile ('$(FCM_DONEDIR)', '$@') . "\n";
+
+      $mk_out .= "\n";
+    }
+  }
+
+  # Make rules for source files
+  my @srcfiles = grep {$_->type} @{ $self->{SRCFILE} };
+  for my $srcfile (@srcfiles) {
+    $mk_out .= $srcfile->write_makerules;
+  }
+
+  # Write make rule file only if necessary
+  if ($mk_out) {
+    $mk .= $mk_out;
+
+    # Write to output file
+    my $mkbase = $self->name . $self->config->setting (qw/OUTFILE_EXT MK/);
+    my $blddir = ${ $self->config->setting (qw/PATH BLD/) }[0];
+    my $mkfile = catfile $blddir, $mkbase;
+
+    if (not -d $blddir) {
+      print 'Make directory: ', $blddir, "\n" if $self->config->verbose > 1;
+      mkpath $blddir or croak $blddir, ': cannot create directory, abort';
+    }
+
+    open OUT, '>', $mkfile
+      or croak 'Cannot open "', $mkfile, '" (', $!, '), abort';
+    print OUT $mk;
+    close OUT or croak 'Cannot close "', $mkfile, '" (', $!, '), abort';
+
+    print 'Generated: ', $mkfile, "\n" if $self->config->verbose > 1;
+
+    return 1;
+
+  } else {
+    return 0;
+  }
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/Timer.pm
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/Timer.pm	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/Timer.pm	(revision 5058)
@@ -0,0 +1,73 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::Timer
+#
+# DESCRIPTION
+#   This is a package of timer utility used by the FCM command.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+package Fcm::Timer;
+
+# Standard pragma
+use warnings;
+use strict;
+
+# Exports
+our (@ISA, @EXPORT, @EXPORT_OK);
+
+sub timestamp_command;
+
+require Exporter;
+@ISA = qw(Exporter);
+@EXPORT = qw(timestamp_command);
+
+# ------------------------------------------------------------------------------
+
+# Module level variables
+my %cmd_start_time = (); # Command start time, (key = command, value = time)
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = &Fcm::Timer::timestamp_command ($command[, $status]);
+#
+# DESCRIPTION
+#   This function returns a string adding to $command a prefix according the
+#   value of $status. If $status is not specified or does not match the word
+#   "end", the status is assumed to be "start". At "start", the prefix will
+#   contain the current timestamp. If $status is the word "end", the prefix
+#   will contain the total time taken since this function was called with the
+#   same $command at the "start" status.
+# ------------------------------------------------------------------------------
+
+sub timestamp_command {
+  (my $command, my $status) = @_;
+
+  my $prefix;
+  if ($status and $status =~ /end/i) {
+    # Status is "end", insert time taken
+    my $lapse = time () - $cmd_start_time{$command};
+    $prefix = sprintf "# Time taken: %12d s=> ", $lapse;
+
+  } else {
+    # Status is "start", insert time stamp
+    $cmd_start_time{$command} = time;
+
+    (my $sec, my $min, my $hour, my $mday, my $mon, my $year) = localtime;
+    $prefix = sprintf "# Start: %04d-%02d-%02d %02d:%02d:%02d=> ",
+              $year + 1900, $mon + 1, $mday, $hour, $min, $sec;
+  }
+
+  return $prefix . $command . "\n";
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
Index: /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/Util.pm
===================================================================
--- /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/Util.pm	(revision 5058)
+++ /IOIPSL/tags/v2_2_5/tools/FCM_V1.2/lib/Fcm/Util.pm	(revision 5058)
@@ -0,0 +1,848 @@
+#!/usr/bin/perl
+# ------------------------------------------------------------------------------
+# NAME
+#   Fcm::Util
+#
+# DESCRIPTION
+#   This is a package of misc utilities used by the FCM command.
+#
+# COPYRIGHT
+#   (C) Crown copyright Met Office. All rights reserved.
+#   For further details please refer to the file COPYRIGHT.txt
+#   which you should have received as part of this distribution.
+# ------------------------------------------------------------------------------
+
+package Fcm::Util;
+
+# Standard pragma
+use warnings;
+use strict;
+
+# Exports
+our (@ISA, @EXPORT, @EXPORT_OK);
+
+sub expand_rev_keyword;
+sub expand_tilde;
+sub expand_url_keyword;
+sub e_report;
+sub find_srcdir;
+sub find_file_in_path;
+sub get_browser_url;
+sub get_command_string;
+sub get_rev_of_wc;
+sub get_rev_keyword;
+sub get_url_of_wc;
+sub get_url_keyword;
+sub get_wct;
+sub is_url;
+sub is_wc;
+sub print_command;
+sub run_command;
+sub svn_date;
+sub touch_file;
+sub w_report;
+
+require Exporter;
+@ISA = qw(Exporter);
+@EXPORT = qw(
+  expand_rev_keyword
+  expand_tilde
+  expand_url_keyword
+  e_report
+  find_srcdir
+  find_file_in_path
+  get_browser_url
+  get_command_string
+  get_rev_of_wc
+  get_rev_keyword
+  get_url_of_wc
+  get_url_keyword
+  get_wct
+  is_url
+  is_wc
+  print_command
+  run_command
+  svn_date
+  touch_file
+  w_report
+);
+
+# Standard modules
+use Carp;
+use Cwd;
+use File::Basename;
+use File::Find;
+use File::Path;
+use File::Spec;
+use POSIX qw/strftime/;
+
+# FCM component modules
+use Fcm::Timer;
+
+# ------------------------------------------------------------------------------
+
+# Module level variables
+my %svn_info       = (); # "svn info" log, (key1 = path,
+                         # key2 = URL, Revision, Last Changed Rev)
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   %srcdir = &Fcm::Util::find_srcdir ($topdir, $toppck, $join);
+#
+# DESCRIPTION
+#   Search $topdir for sub-directories containing regular files. Returns a hash
+#   with each key/value pair assigned to a unique name of the source directory
+#   and the location of the source directory. If $toppck is set the name of
+#   each source directory will be prefixed with this package name, and the
+#   search may include the $topdir in the result. If $join is set, the name of
+#   the sub-package will use $join as the delimiter of packages. Otherwise, the
+#   default double underscore '__' will be used.  Please note that all
+#   directories beginning with a ".", i.e. hidden directories, are ignored.
+# ------------------------------------------------------------------------------
+
+sub find_srcdir {
+  (my $topdir, my $toppck, my $join) = @_;
+  $join = defined ($join) ? $join : '__';
+
+  my @dirs = ();
+
+  # Locate all source directories containing regular files
+  if (-d $topdir) {
+    find (
+      sub {
+        my $dir = $File::Find::name;
+        return 0 if $dir eq $topdir and not $toppck;
+
+        if (-d $dir) {
+          # Ignore sub-directories with names beginning with .
+          if ($dir ne $topdir) {
+            my $subdir = substr ($dir, length ($topdir) + 1);
+            return 0 if grep {m/^\./} File::Spec->splitdir ($subdir);
+          }
+
+          # Read contents of directory
+          opendir DIR, $dir;
+          my @files = readdir 'DIR';
+          closedir DIR;
+
+          # Check if the directory contains one or more source file
+          my $contain_src;
+          for my $file (@files) {
+            next if $file =~ /^\./; # ignore hidden file
+
+            if (-f File::Spec->catfile ($dir, $file)) {
+              $contain_src = 1;
+              last;
+            }
+          }
+
+          push @dirs, $dir if $contain_src;
+          return 1;
+
+        } else {
+          return 0;
+        }
+      },
+
+      $topdir,
+    );
+  }
+
+  # String length of src directory name
+  my $topdir_len = length $topdir;
+
+  # Assign new source directories to current build
+  my @pck    = $toppck ? split (/$join/, $toppck) : ();
+  my %srcdir = ();
+  for my $dir (@dirs) {
+    my $name = ($dir eq $topdir) ? '' : substr $dir, $topdir_len + 1;
+    my @path = File::Spec->splitdir ($name);
+    my $key  = join $join, (@pck, @path);
+
+    $srcdir{$key} = $dir;
+  }
+
+  return %srcdir;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   %srcdir = &Fcm::Util::find_file_in_path ($file, \@path);
+#
+# DESCRIPTION
+#   Search $file in @path. Returns the full path of the $file if it is found
+#   in @path. Returns "undef" if $file is not found in @path.
+# ------------------------------------------------------------------------------
+
+sub find_file_in_path {
+  my ($file, $path) = @_;
+
+  for my $dir (@$path) {
+    my $full_file = File::Spec->catfile ($dir, $file);
+    return $full_file if -e $full_file;
+  }
+
+  return undef;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $expanded_path = &Fcm::Util::expand_tilde ($path);
+#
+# DESCRIPTION
+#   Returns an expanded path if $path is a path that begins with a tilde (~).
+# ------------------------------------------------------------------------------
+
+sub expand_tilde {
+  my $file = $_[0];
+
+  $file =~ s#^~([^/]*)#$1 ? (getpwnam $1)[7] : ($ENV{HOME} || $ENV{LOGDIR})#ex;
+
+  return $file;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $rc = &Fcm::Util::touch_file ($file);
+#
+# DESCRIPTION
+#   Touch $file if it exists. Create $file if it does not exist. Return 1 for
+#   success or 0 otherwise.
+# ------------------------------------------------------------------------------
+
+sub touch_file {
+  my $file = $_[0];
+  my $rc   = 1;
+
+  if (-e $file) {
+    my $now = time;
+    $rc = utime $now, $now, $file;
+
+  } else {
+    mkpath dirname ($file) unless -d dirname ($file);
+
+    $rc = open FILE, '>', $file;
+    $rc = close FILE if $rc;
+  }
+
+  return $rc;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $new_url = &Fcm::Util::expand_url_keyword (URL => $url[, CFG => $cfg]);
+#
+# DESCRIPTION
+#   Expand URL if its begins with a pre-defined pattern followed by a keyword
+#   that can be found in the setting of CFG. If URL is a genuine URL, the
+#   function also attempts to expand any . or .. in the path. If CFG is not
+#   set, it defaults to &main::cfg.
+# ------------------------------------------------------------------------------
+
+sub expand_url_keyword {
+  my %args = @_;
+  my $url  = $args{URL};
+  my $cfg  = exists $args{CFG} ? $args{CFG} : &main::cfg;
+
+  # Prefix for URL keyword
+  my $prefix = $cfg->setting (qw/MISC EXPURL_PREFIX/);
+
+  # Pattern for URL keyword
+  my $pattern = '^' . $prefix . '([^/]+)';
+
+  # Standard suffix for URL keyword
+  my %suffix_value = (tr => 'trunk', br => 'branches', tg => 'tags');
+
+  # URL matches pattern?
+  if ($url =~ /$pattern/) {
+    my $keyword = $1;
+
+    # Determine whether keyword is registered.
+    my $keyval = $cfg->setting ('URL', uc ($keyword));
+
+    if ((not $keyval) and $keyword =~ s/[-_](tr|br|tg)$//) {
+      # Keyword is not registered, but it matches a standard suffix
+      my $suffix = $suffix_value{$1};
+
+      $keyval = $cfg->setting ('URL', uc ($keyword)) . '/' . $suffix
+        if $cfg->setting ('URL', uc ($keyword));
+    }
+
+    # Expand if keyword is registered
+    $url =~ s/$pattern/$keyval/ if $keyval;
+  }
+
+  # Expand . and ..
+  if (&is_url ($url)) {
+    while ($url =~ s#/+\.(?:/+|$)#/#g) {next}
+    while ($url =~ s#/+[^/]+/+\.\.(?:/+|$)#/#g) {next}
+  }
+
+  return $url;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = &Fcm::Util::get_url_keyword (URL => $url[, CFG => $cfg]);
+#
+# DESCRIPTION
+#   Return a FCM URL keyword if URL matches a registered project URL or undef
+#   otherwise. If CFG is not set, it defaults to &main::cfg.
+# ------------------------------------------------------------------------------
+
+sub get_url_keyword {
+  my %args = @_;
+  my $url  = $args{URL};
+  my $cfg  = exists $args{CFG} ? $args{CFG} : &main::cfg;
+
+  my $return;
+
+  for my $key (%{ $cfg->setting ('URL') }) {
+    my $value = $cfg->setting ('URL', $key);
+    next unless defined $value;
+    next unless $url =~ s#^$value(?:/+|$)##;
+
+    $return = $cfg->setting (qw/MISC EXPURL_PREFIX/) . $key .
+              ($url ? '/' . $url : '');
+    last;
+  }
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $new_rev = &Fcm::Util::expand_rev_keyword (
+#     REV  => $rev,
+#     URL  => $url,
+#    [HEAD => $flag,]
+#    [CFG  => $cfg,]
+#  );
+#
+# DESCRIPTION
+#   Expand REV if URL is a known URL in CFG setting and REV matches a revision
+#   keyword of this URL, or if REV is "HEAD". SVN revision numbers, date and
+#   other keywords are ignored. HEAD should only be specified if REV has the
+#   value "HEAD". If HEAD is specified and is true, the return value of the
+#   function will be the operative revision number of the HEAD revision.
+#   Otherwise, the last commit revision will be returned. If CFG is not set,
+#   it defaults to &main::cfg.
+# ------------------------------------------------------------------------------
+
+sub expand_rev_keyword {
+  my %args = @_;
+  my $rev  = $args{REV};
+  my $url  = $args{URL};
+  my $head = exists $args{HEAD} ? $args{HEAD} : undef;
+  my $cfg  = exists $args{CFG } ? $args{CFG } : &main::cfg;
+
+  if (uc ($rev) eq 'HEAD') {
+    # Expand HEAD revision
+    &_invoke_svn_info (PATH => $url, CFG => $cfg) unless exists $svn_info{$url};
+    my $expanded_rev = $head
+                     ? $svn_info{$url}{Revision}
+                     : $svn_info{$url}{'Last Changed Rev'};
+
+    &w_report ($url, ': cannot determine HEAD revision.')
+      if $cfg->verbose > 1 and not $expanded_rev;
+
+    $rev = $expanded_rev if $expanded_rev;
+
+  } elsif ($rev !~ /^(?:\d+|BASE|COMMITTED|PREV|\{.+\})$/i) {
+    # Expand revision keyword, if required
+
+    # Get configuration settings
+    my %keywords  = %{ $cfg->setting (qw/REVISION/) };
+    my $separator = $cfg->setting (qw/MISC DIR_SEPARATOR/);
+
+    my $name      = '';
+
+    # Find out whether URL matches a registered repository
+    for my $keyword (keys %keywords) {
+      my $repos = $cfg->setting ('URL', uc ($keyword));
+      next unless $repos;
+
+      if ($url =~ m#^$repos(?:$separator|$)#) {
+        $name = $keyword;
+        last;
+      }
+    }
+
+    # If revision keyword exists for the registered repository, expand it
+    if ($name and exists $keywords{$name}{uc ($rev)}) {
+      $rev = $keywords{$name}{uc ($rev)};
+
+    } else {
+      &e_report (
+        $rev, ': revision keyword not found for ', $url,
+        ' in FCM configuration file, abort.',
+      );
+    }
+  }
+
+  return $rev;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $keyword = Fcm::Util::get_rev_keyword (
+#     REV => $rev,
+#     URL => $url,
+#    [CFG => $cfg,]
+#  );
+#
+# DESCRIPTION
+#   Returns a revision keyword if URL is a known URL in CFG setting and REV is
+#   a revision number that matches a revision keyword of this URL. Otherwise,
+#   it returns REV unchanged. If CFG is not set, it defaults to &main::cfg.
+# ------------------------------------------------------------------------------
+
+sub get_rev_keyword {
+  my %args = @_;
+  my $rev  = $args{REV};
+  my $url  = $args{URL};
+  my $cfg  = exists $args{CFG} ? $args{CFG} : &main::cfg;
+
+  if ($rev =~ /^\d+$/) {
+    # Get revision keyword, if REV is a revision number
+
+    # Get configuration settings
+    my %keywords  = %{ $cfg->setting (qw/REVISION/) };
+    my $separator = $cfg->setting (qw/MISC DIR_SEPARATOR/);
+
+    my $name      = '';
+
+    # Find out whether URL matches a registered repository
+    for my $keyword (keys %keywords) {
+      my $repos = $cfg->setting ('URL', uc ($keyword));
+      next unless $repos;
+
+      if ($url =~ m#^$repos(?:$separator|$)#) {
+        $name = $keyword;
+        last;
+      }
+    }
+
+    # If revision keyword for REV exists for the registered repository, get it
+    if ($name and exists $keywords{$name} and ref $keywords{$name} eq 'HASH') {
+      for my $key (keys %{ $keywords{$name} }) {
+        if ($rev eq $keywords{$name}{$key}) {
+          $rev = $key;
+          last;
+        }
+      }
+    }
+  }
+
+  return $rev;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $browser_url = Fcm::Util::get_browser_url (
+#     URL => $url,
+#    [CFG => $cfg,]
+#  );
+#
+# DESCRIPTION
+#   Returns a web address for browsing URL from Trac if URL is a known URL in
+#   CFG setting, and that it is a matching web address. Otherwise, it returns
+#   "undef". If CFG is not set, it defaults to &main::cfg.
+# ------------------------------------------------------------------------------
+
+sub get_browser_url {
+  my %args        = @_;
+  my $url         = $args{URL};
+  my $cfg         = exists $args{CFG} ? $args{CFG} : &main::cfg;
+  my $browser_url = undef;
+
+  # Get configuration settings
+  my %keywords  = %{ $cfg->setting (qw/TRAC/) };
+  my $separator = $cfg->setting (qw/MISC DIR_SEPARATOR/);
+
+  my $name  = '';
+  my $trail = '';
+
+  # Find out whether URL matches a registered repository
+  for my $keyword (keys %keywords) {
+    my $repos = $cfg->setting ('URL', uc ($keyword));
+    next unless $repos;
+
+    if ($url =~ m#^$repos(?:$separator(.*$)|$)#) {
+      $name  = $keyword;
+      $trail = $1 if $1;
+      last;
+    }
+  }
+
+  # If TRAC web address exists for the registered repository, get it
+  if ($name and exists $keywords{$name}) {
+    $browser_url  = $keywords{$name};
+    $browser_url .= $separator . $trail if $trail;
+  }
+
+  return $browser_url;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = &is_wc ([$path]);
+#
+# DESCRIPTION
+#   Returns true if current working directory (or $path) is a Subversion
+#   working copy.
+# ------------------------------------------------------------------------------
+
+sub is_wc {
+  my $path = @_ ? $_[0] : cwd ();
+
+  if (-d $path) {
+    return (-e File::Spec->catfile ($path, qw/.svn format/)) ? 1 : 0;
+
+  } elsif (-f $path) {
+    return (-e File::Spec->catfile (dirname ($path), qw/.svn format/)) ? 1 : 0;
+
+  } else {
+    return 0;
+  }
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $flag = &is_url ($url);
+#
+# DESCRIPTION
+#   Returns true if $url is a URL.
+# ------------------------------------------------------------------------------
+
+sub is_url {
+  # This should handle URL beginning with svn://, http:// and svn+ssh://
+  return ($_[0] =~ m#^[\+\w]+://#);
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = &get_wct ([$dir]);
+#
+# DESCRIPTION
+#   If current working directory (or $dir) is a Subversion working copy,
+#   returns the top directory of this working copy; otherwise returns an empty
+#   string.
+# ------------------------------------------------------------------------------
+
+sub get_wct {
+  my $dir = @_ ? $_[0] : cwd ();
+
+  return '' if not &is_wc ($dir);
+
+  my $updir = dirname $dir;
+  while (&is_wc ($updir)) {
+    $dir   = $updir;
+    $updir = dirname $dir;
+    last if $updir eq $dir;
+  }
+
+  return $dir;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = &get_url_of_wc ([$path[, $refresh]]);
+#
+# DESCRIPTION
+#   If current working directory (or $path) is a Subversion working copy,
+#   returns the URL of the associated Subversion repository; otherwise returns
+#   an empty string. If $refresh is specified, do not use the cached
+#   information.
+# ------------------------------------------------------------------------------
+
+sub get_url_of_wc {
+  my $path    = @_ ? $_[0] : cwd ();
+  my $refresh = exists $_[1] ? $_[1] : 0;
+  my $url  = '';
+
+  if (&is_wc ($path)) {
+    delete $svn_info{$path} if $refresh;
+    &_invoke_svn_info (PATH => $path) unless exists $svn_info{$path};
+    $url = $svn_info{$path}{URL};
+  }
+
+  return $url;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &_invoke_svn_info (PATH => $path, [CFG => $cfg]);
+#
+# DESCRIPTION
+#   The function is internal to this module. It invokes "svn info" on $path to
+#   gather information on URL, Revision and Last Changed Rev. The information
+#   is stored in a hash table at the module level, so that the information can
+#   be re-used. If CFG is not set, it defaults to &main::cfg.
+# ------------------------------------------------------------------------------
+
+sub _invoke_svn_info {
+  my %args = @_;
+  my $path = $args{PATH};
+  my $cfg  = exists $args{CFG} ? $args{CFG} : &main::cfg;
+
+  return if exists $svn_info{$path};
+
+  # Invoke "svn info" command
+  my @info = &run_command (
+    [qw/svn info/, $path],
+    PRINT => $cfg->verbose > 2, METHOD => 'qx', DEVNULL => 1, ERROR => 'ignore',
+  );
+  for (@info) {
+    chomp;
+
+    if (/^(URL|Revision|Last Changed Rev):\s*(.+)$/) {
+      $svn_info{$path}{$1} = $2;
+    }
+  }
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $string = &get_command_string ($cmd);
+#   $string = &get_command_string (\@cmd);
+#
+# DESCRIPTION
+#   The function returns a string by converting the list in @cmd or the scalar
+#   $cmd to a form, where it can be executed as a shell command.
+# ------------------------------------------------------------------------------
+
+sub get_command_string {
+  my $cmd    = $_[0];
+  my $return = '';
+
+  if (ref ($cmd) and ref ($cmd) eq 'ARRAY') {
+    # $cmd is a reference to an array
+
+    # Print each argument
+    for my $i (0 .. @{ $cmd } - 1) {
+      my $arg = $cmd->[$i];
+
+      $arg =~ s/./*/g if $i > 0 and $cmd->[$i - 1] eq '--password';
+
+      if ($arg =~ /[\s'"*?]/) {
+        # Argument contains a space, quote it
+        if (index ($arg, "'") >= 0) {
+          # Argument contains an apostrophe, quote it with double quotes
+          $return .= ($i > 0 ? ' ' : '') . '"' . $arg . '"';
+
+        } else {
+          # Otherwise, quote argument with apostrophes
+          $return .= ($i > 0 ? ' ' : '') . "'" . $arg . "'";
+        }
+
+      } else {
+        # Argument does not contain a space, just print it
+        $return .= ($i > 0 ? ' ' : '') . ($arg eq '' ? "''" : $arg);
+      }
+    }
+
+  } else {
+    # $cmd is a scalar, just print it "as is"
+    $return = $cmd;
+  }
+
+  return $return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &print_command ($cmd);
+#   &print_command (\@cmd);
+#
+# DESCRIPTION
+#   The function prints the list in @cmd or the scalar $cmd, as it would be
+#   executed by the shell.
+# ------------------------------------------------------------------------------
+
+sub print_command {
+  my $cmd = $_[0];
+
+  print '=> ', &get_command_string ($cmd) , "\n";
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   @return = &run_command (\@cmd, <OPTIONS>);
+#   @return = &run_command ($cmd , <OPTIONS>);
+#
+# DESCRIPTION
+#   This function executes the command in the list @cmd or in the scalar $cmd.
+#   The remaining are optional arguments in a hash table. Valid options are
+#   listed below. If the command is run using "qx", the function returns the
+#   standard output from the command. If the command is run using "system", the
+#   function returns true on success. By default, the function dies on failure.
+#
+# OPTIONS
+#   METHOD  => $method - this can be "system", "exec" or "qx". This determines
+#                        how the command will be executed. If not set, the
+#                        default is to run the command with "system".
+#   PRINT   => 1       - if set, print the command before executing it.
+#   ERROR   => $flag   - this should only be set if METHOD is set to "system"
+#                        or "qx". The $flag can be "die" (default), "warn" or
+#                        "ignore". If set to "die", the function dies on error.
+#                        If set to "warn", the function issues a warning on
+#                        error, and the function returns false. If set to
+#                        "ignore", the function returns false on error.
+#   RC      => 1       - if set, must be a reference to a scalar, which will be
+#                        set to the return code of the command.
+#   DEVNULL => 1       - if set, re-direct STDERR to /dev/null before running
+#                        the command.
+#   TIME    => 1       - if set, print the command with a timestamp before
+#                        executing it, and print the time taken when it
+#                        completes. This option supersedes the PRINT option.
+# ------------------------------------------------------------------------------
+
+sub run_command {
+  my $cmd     = shift;
+  my %options = @_;
+  my $method  = exists $options{METHOD}  ? $options{METHOD}  : 'system';
+  my $print   = exists $options{PRINT}   ? $options{PRINT}   : undef;
+  my $error   = exists $options{ERROR}   ? $options{ERROR}   : 'die';
+  my $rc      = exists $options{RC}      ? $options{RC}      : undef;
+  my $devnull = exists $options{DEVNULL} ? $options{DEVNULL} : undef;
+  my $time    = exists $options{TIME}    ? $options{TIME}    : undef;
+  my @return  = ();
+
+  # Check that the $error flag is set correctly
+  $error = 'die' unless $error =~ /^(?:die|warn|ignore)$/i;
+
+  # Print the command before execution, if necessary
+  if ($time) {
+    print &timestamp_command (&get_command_string ($cmd));
+
+  } elsif ($print) {
+    &print_command ($cmd);
+  }
+
+  # Re-direct to /dev/null if necessary
+  if ($devnull) {
+    $devnull = File::Spec->devnull;
+
+    # Save current STDERR
+    no warnings;
+    open OLDERR, ">&STDERR" or croak 'Cannot dup STDERR (', $!, '), abort';
+    use warnings;
+
+    # Redirect STDERR to /dev/null
+    open STDERR, '>', $devnull
+      or croak 'Cannot redirect STDERR (', $!, '), abort';
+
+    # Make sure the channels are unbuffered
+    my $select = select;
+    select STDERR; $| = 1;
+    select $select;
+  }
+
+  if (ref ($cmd) and ref ($cmd) eq 'ARRAY') {
+    # $cmd is an array
+    my @command = @{ $cmd };
+
+    if ($method eq 'qx') {
+      @return = qx(@command);
+
+    } elsif ($method eq 'exec') {
+      exec (@command);
+
+    } else {
+      system (@command);
+      @return = $? ? () : (1);
+    }
+
+  } else {
+    # $cmd is an scalar
+    if ($method eq 'qx') {
+      @return = qx($cmd);
+
+    } elsif ($method eq 'exec') {
+      exec ($cmd);
+
+    } else {
+      system ($cmd);
+      @return = $? ? () : (1);
+    }
+  }
+
+  # Put STDERR back to normal, if redirected previously
+  if ($devnull) {
+    close STDERR;
+
+    open STDERR, ">&OLDERR" or croak 'Cannot dup STDERR (', $!, '), abort';
+  }
+
+  # Print the time taken for command after execution, if necessary
+  print &timestamp_command (&get_command_string ($cmd), 'end') if $time;
+
+  if ($?) {
+    # The command has failed
+    if ($error eq 'die') {
+      # Throw fatal error if ERROR is set to "die"
+      croak &get_command_string ($cmd), ' failed (', $?, ')';
+
+    } elsif ($error eq 'warn') {
+      # Issue warning if ERROR is set to "warn"
+      carp  &get_command_string ($cmd), ' failed (', $?, ')';
+    }
+  }
+
+  # Set the return code if necessary
+  $$rc = $? if $rc;
+
+  return @return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &e_report (@message);
+#
+# DESCRIPTION
+#   The function prints @message to STDERR and aborts with a error.
+# ------------------------------------------------------------------------------
+
+sub e_report {
+  print STDERR @_, "\n" if @_;
+
+  exit 1;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   &w_report (@message);
+#
+# DESCRIPTION
+#   The function prints @message to STDERR and returns.
+# ------------------------------------------------------------------------------
+
+sub w_report {
+  print STDERR @_, "\n" if @_;
+
+  return;
+}
+
+# ------------------------------------------------------------------------------
+# SYNOPSIS
+#   $date = &svn_date ($time);
+#
+# DESCRIPTION
+#   The function returns a date, formatted as by Subversion. The argument $time
+#   is the number of seconds since epoch.
+# ------------------------------------------------------------------------------
+
+sub svn_date {
+  my $time = shift;
+
+  return strftime ('%Y-%m-%d %H:%M:%S %z (%a, %d %b %Y)', localtime ($time));
+}
+
+# ------------------------------------------------------------------------------
+
+1;
+
+__END__
